Originally created by: Akarsh-Hegde
Tickets: #133
Tickets: #201
Tickets: #229
Tickets: #251
Tickets: #253
Tickets: #274
Tickets: #281
Tickets: #288
Tickets: #298
Tickets: #3
Tickets: #310
Tickets: #338
Tickets: #345
Tickets: #346
Tickets: #350
Tickets: #372
Tickets: #458
Tickets: #554
Originally posted by: adityaharishch
PR Review — Feat/merge meridian hermes for jira (KAN-86)
This is a substantial, well-architected addition — a 3-stage Python intelligence layer running alongside the Rust ETL, with OTel observability end-to-end and a thorough migration test suite. The core pipeline design is solid. A few issues need to be resolved before merge, ranging from one hard blocker to several should-fixes.
🔴 Blockers
1.
ruvector.dbbinary committed to the reporuvector.dbis a machine-local vector store that must not be tracked in Git. It's a runtime artifact, and depending on how the embeddings service populated it, it may contain session text derived from screen activity (PII risk). Fix:Then add
ruvector.dbto the root.gitignore. The.gitignorewas modified in this PR but only addeddata/memory/and.claude/memory.db—ruvector.dbwas missed.2.
ensure_hermes_importable()called unconditionally at module import timeIn
tagger.py(~line 38),tagger_daemon.py, andagents/tests/conftest.py,ensure_hermes_importable()runs at module top-level — before any stage flag is checked and before anyif __name__ == '__main__'guard. Consequences:STAGE3_ENABLED=falsedoes not prevent a crash ifhermes-agentis uninstalledpytest agents/tests/fails entirely if hermes is unavailable, even for Stage 1 unit testsThe hermes import should be deferred to the call site inside
agent_tiebreaker.py— the only place it's actually needed.tagger.pyandtagger_daemon.pyshould importagent_tiebreakerlazily, only when Stage 3 is enabled.🟡 Should Fix
3.
ui/app/api/tickets/route.tshas wrong file headerThis is the only file in the PR that deviates from the mandatory header.
4. OTel as mandatory Rust dependencies — should be feature-flagged
Cargo.tomladds six crates unconditionally, includingopentelemetry-otlpwithreqwest-clientwhich pulls in a full TLS/HTTP stack:For a local-only SQLite daemon where OTel is optional infrastructure, these should be behind a Cargo feature:
Default off. Everyone without OpenObserve doesn't carry reqwest + TLS at compile time.
5.
tagger.pymodule docstring contradicts the implementationThe opening docstring says: "No LLM is involved at this stage."
But
tagger.pyis the 3-stage coordinator — it imports and invokesagent_tiebreaker(Stage 3 LLM). The docstring was written when this file was Stage 1 only and wasn't updated when Stage 2/3 were merged in.6.
advance_cursorsilent no-op on missing cursor rowIf
agent_cursorhas no row withid = 1(migration skipped, or seeding failed), this UPDATE matches 0 rows silently. The cursor never advances and every tick re-processes all sessions from the beginning — an invisible infinite spin. Add a rowcount check with alog.warning()when rowcount == 0.7.
hermes-agentpinned to a Git tag on an external repoIf this tag is deleted or the repo changes visibility, installs break with no fallback. Worth documenting a recovery path in
services/README.mdand mirroring/vendoring before this ships to other team members.🟢 Nits / Observations
8.
past_votehas no recency decay — The K-nearest past sessions forpast_votescoring are selected by embedding similarity with no time weighting. An incorrect tag from 3 months ago gets equal weight as a correct tag from yesterday. A simple time-decay factor (weight *= exp(-days_since / 14)) would make the signal more adaptive as workflows change.9. OTel default endpoint in
ui/lib/observability.ts—http://localhost:5080/api/default/v1/tracesis the default whenMERIDIAN_OTLP_TRACES_ENDPOINTis unset. When OpenObserve isn't running, the exporter times out silently on every span. Consider defaulting to a no-op when the env var is unset — opt-in observability rather than always-on-and-silently-failing.10. Python test suite not wired into pre-push — The
pre-pushhook runscargo test+ UI build/test but notcd services && pytest. The Stage 2/3 tests (test_semantic_matcher.py,test_agent_tiebreaker.py) have real coverage value. Either add them to pre-push or mark them as CI-only with a note in the README.What's Good
migration_005_agents.rs(649 lines) is exceptionally thorough — FK enforcement, column existence, UPSERT semantics, and well-factoredseed_sessionhelpers. Sets a strong bar._parse_response/_repair_truncated_jsoninagent_tiebreaker.pyhandles the full range of malformed LLM output (naked JSON, markdown fences, truncated with trailing comma). The unit tests for these corner cases are solid.WHERE ? > last_session_id) prevents any cursor rewind on restart — correct.foundation.rscleanup is clean —fm_generate_categoryremoved consistently from bothextern "C"block andbuild.rsstub;raw_generatecorrectly switched tocall_generate_text. No dangling declaration.ONLY_TODAYmode is a pragmatic affordance to avoid re-grinding historical sessions on every dev cycle.Summary: Fix the
ruvector.dbbinary (#1) and the unconditional hermes import (#2) before merging — both can cause silent data leakage or silent runtime failures for other contributors. Items [#3]–#7 are clean-up that can go in follow-up tickets if needed.Related
Tickets:
#3Ticket changed by: Akarsh-Hegde
Originally posted by: adityaharishch
🎉 This PR is included in version 1.0.0 🎉
The release is available on:
v1.0.0Your semantic-release bot 📦🚀