Menu

#3 Feat/merge meridian hermes for jira from kan 86 migrate

closed
nobody
released (243)
2026-06-01
2026-05-12
Anonymous
No

Originally created by: Akarsh-Hegde

Related

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

Discussion

  • Anonymous

    Anonymous - 2026-05-12

    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.db binary committed to the repo

    ruvector.db is 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:

    git rm --cached ruvector.db
    

    Then add ruvector.db to the root .gitignore. The .gitignore was modified in this PR but only added data/memory/ and .claude/memory.dbruvector.db was missed.


    2. ensure_hermes_importable() called unconditionally at module import time

    In tagger.py (~line 38), tagger_daemon.py, and agents/tests/conftest.py, ensure_hermes_importable() runs at module top-level — before any stage flag is checked and before any if __name__ == '__main__' guard. Consequences:

    • Setting STAGE3_ENABLED=false does not prevent a crash if hermes-agent is uninstalled
    • pytest agents/tests/ fails entirely if hermes is unavailable, even for Stage 1 unit tests
    • A dev running Stage 1-only (rules, no LLM) still needs the full hermes checkout

    The hermes import should be deferred to the call site inside agent_tiebreaker.py — the only place it's actually needed. tagger.py and tagger_daemon.py should import agent_tiebreaker lazily, only when Stage 3 is enabled.


    🟡 Should Fix

    3. ui/app/api/tickets/route.ts has wrong file header

    // meridian — AI activity intelligence by Meridiona   ← actual
    // meridian — normalises screenpipe activity into structured app sessions  ← required (CLAUDE.md)
    

    This is the only file in the PR that deviates from the mandatory header.


    4. OTel as mandatory Rust dependencies — should be feature-flagged

    Cargo.toml adds six crates unconditionally, including opentelemetry-otlp with reqwest-client which pulls in a full TLS/HTTP stack:

    opentelemetry-otlp = { version = "0.27", default-features = false, features = ["http-proto", "reqwest-client", "trace", "logs"] }
    

    For a local-only SQLite daemon where OTel is optional infrastructure, these should be behind a Cargo feature:

    [features]
    observability = ["dep:tracing-appender", "dep:tracing-opentelemetry", "dep:opentelemetry", ...]
    

    Default off. Everyone without OpenObserve doesn't carry reqwest + TLS at compile time.


    5. tagger.py module docstring contradicts the implementation

    The opening docstring says: "No LLM is involved at this stage."

    But tagger.py is the 3-stage coordinator — it imports and invokes agent_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_cursor silent no-op on missing cursor row

    def advance_cursor(conn, last_session_id):
        conn.execute(
            "UPDATE agent_cursor SET ... WHERE id = 1 AND ? > last_session_id",
            (last_session_id, _utc_now(), last_session_id),
        )
    

    If agent_cursor has no row with id = 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 a log.warning() when rowcount == 0.


    7. hermes-agent pinned to a Git tag on an external repo

    hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git@v2026.4.30
    

    If this tag is deleted or the repo changes visibility, installs break with no fallback. Worth documenting a recovery path in services/README.md and mirroring/vendoring before this ships to other team members.


    🟢 Nits / Observations

    8. past_vote has no recency decay — The K-nearest past sessions for past_vote scoring 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.tshttp://localhost:5080/api/default/v1/traces is the default when MERIDIAN_OTLP_TRACES_ENDPOINT is 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-push hook runs cargo test + UI build/test but not cd 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-factored seed_session helpers. Sets a strong bar.
    • _parse_response / _repair_truncated_json in agent_tiebreaker.py handles the full range of malformed LLM output (naked JSON, markdown fences, truncated with trailing comma). The unit tests for these corner cases are solid.
    • Cursor monotonicity guard (WHERE ? > last_session_id) prevents any cursor rewind on restart — correct.
    • Stage flags allow incremental rollout and safe disablement without code changes.
    • foundation.rs cleanup is clean — fm_generate_category removed consistently from both extern "C" block and build.rs stub; raw_generate correctly switched to call_generate_text. No dangling declaration.
    • ONLY_TODAY mode is a pragmatic affordance to avoid re-grinding historical sessions on every dev cycle.

    Summary: Fix the ruvector.db binary (#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: #3

  • Anonymous

    Anonymous - 2026-05-12

    Ticket changed by: Akarsh-Hegde

    • status: open --> closed
     
  • Anonymous

    Anonymous - 2026-06-01

    Originally posted by: adityaharishch

    🎉 This PR is included in version 1.0.0 🎉

    The release is available on:

    Your semantic-release bot 📦🚀

     

Log in to post a comment.