Skip to content

fix(livesync): RC-1 probe stored-id contract + derivation settle cap - #272

Merged
Kyzcreig merged 3 commits into
mainfrom
fix/livesync-probe-stored-id-v2
Jul 10, 2026
Merged

Kyzcreig merged 3 commits into
mainfrom
fix/livesync-probe-stored-id-v2

Conversation

@Kyzcreig

Copy link
Copy Markdown
Collaborator

Ship-gate probe fixes learned during the live RC-1 run (PR #268 follow-up):

  • session.changes/session.resume take the STORED state.db id; session.create persists lazily → probe now runs read-only over an existing idle session
  • t_silence derivation settle wait capped at 120s

Live results with these fixes: RC-1 PROBE: PASS (6/6 — routing + semantic), t_silence derived from 71 live frames (longest quiet 96.05s → recommended 192, set live in config).

@greptile-apps

greptile-apps Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR fixes two RC-1 live-run issues: the probe now operates read-only against an existing stored session (avoiding session.create's lazy-persist problem), and the t_silence derivation settle timeout is trimmed from 300 s to 120 s. A new livesync-a1-watch.py script is added to measure cross-process commit→fetch latency.

  • RC-1 probe contract fix (livesync-rc1-probe.py): replaces synthetic session creation with a sqlite3 auto-pick or explicit argv[4]; captures c2_live_id from the steal response; resets status = None per loop iteration; removes the loose fallback that allowed an unrelated idle session to green the semantic assertion; drops the now-unneeded cleanup path.
  • Settle timeout cap (livesync-derive-tsilence.py): asyncio.wait_for timeout reduced to 120 s with an updated warning message; gap recording on timeout is unchanged.
  • New A1 latency proof (livesync-a1-watch.py): polls session.changes at the shipped 2.5 s interval, measures commit→fetch latency per row, and reports pass/fail against the ≤3 s SLO; requires stored_session_id as argv[4] with an early exit if absent.

Confidence Score: 4/5

Safe to merge for a development-only probe suite; the auto-pick path can select an actively-used session and disrupt it, so the fix is worth landing before the query is relied upon in any automated context.

The three issues flagged by previous review rounds are all correctly addressed. One remaining gap: the sqlite3 auto-pick SQL orders by most-recently-active rather than filtering for idle, meaning the auto-pick fallback can steal a session the user is actively working in. The scripts are development tooling run manually, which limits blast radius, but the mismatch between the comment and the query is a real defect.

scripts/livesync-rc1-probe.py — specifically the auto-pick SQL query and the c2_live_id extraction logic.

Important Files Changed

Filename Overview
scripts/livesync-rc1-probe.py Core RC-1 probe refactored to run read-only against an existing stored session; stale-status and loose-fallback bugs from previous round addressed, but auto-pick SQL may select an actively-used session rather than an idle one.
scripts/livesync-derive-tsilence.py Single change: derivation settle timeout tightened from 300 s to 120 s; gap recording on timeout is unchanged.
scripts/livesync-a1-watch.py New A1 latency-proof script; SID guard is present and early, Client reuse from the probe module is structurally sound given matching argv layout.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant DB as state.db (SQLite)
    participant C1 as Client-1
    participant C2 as Client-2
    participant SRV as Hermes WS Server

    note over DB,SRV: Session selection
    C1->>DB: SELECT session_id (auto-pick or argv[4])
    DB-->>C1: sid

    note over C1,SRV: Routing probe setup
    C1->>SRV: session.resume(sid)
    SRV-->>C1: result (c1 becomes holder)
    C1->>SRV: "session.changes(sid, since=0)"
    SRV-->>C1: committed rows (pre_ids)

    note over C2,SRV: Steal
    C2->>SRV: session.resume(sid)
    SRV-->>C2: "result {session_id: c2_live_id}"

    note over C1,SRV: (a) ROUTING assertions
    C1->>SRV: "session.changes(sid, since=cursor)"
    SRV-->>C1: result on C1 socket
    C1->>SRV: session.active_list()
    SRV-->>C1: sessions list on C1 socket

    note over C1,SRV: (b) SEMANTIC poll loop up to 20s
    loop every 2s until idle/waiting
        C1->>SRV: session.active_list()
        SRV-->>C1: sessions
        C1->>C1: match row by sid OR c2_live_id
        C1->>C1: "status = row.status"
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant DB as state.db (SQLite)
    participant C1 as Client-1
    participant C2 as Client-2
    participant SRV as Hermes WS Server

    note over DB,SRV: Session selection
    C1->>DB: SELECT session_id (auto-pick or argv[4])
    DB-->>C1: sid

    note over C1,SRV: Routing probe setup
    C1->>SRV: session.resume(sid)
    SRV-->>C1: result (c1 becomes holder)
    C1->>SRV: "session.changes(sid, since=0)"
    SRV-->>C1: committed rows (pre_ids)

    note over C2,SRV: Steal
    C2->>SRV: session.resume(sid)
    SRV-->>C2: "result {session_id: c2_live_id}"

    note over C1,SRV: (a) ROUTING assertions
    C1->>SRV: "session.changes(sid, since=cursor)"
    SRV-->>C1: result on C1 socket
    C1->>SRV: session.active_list()
    SRV-->>C1: sessions list on C1 socket

    note over C1,SRV: (b) SEMANTIC poll loop up to 20s
    loop every 2s until idle/waiting
        C1->>SRV: session.active_list()
        SRV-->>C1: sessions
        C1->>C1: match row by sid OR c2_live_id
        C1->>C1: "status = row.status"
    end
Loading

Reviews (4): Last reviewed commit: "fix(livesync): probe semantic matcher — ..." | Re-trigger Greptile

Comment thread scripts/livesync-rc1-probe.py Outdated
Comment thread scripts/livesync-a1-watch.py
Kyzcreig added 2 commits July 10, 2026 13:16
…e persists lazily)

Learned running the ship-gate live: session.changes/session.resume take the
STORED state.db id, and session.create seeds only the live registry (DB row
persists on first real turn) — so a synthetic probe session has no committed
rows. Probe now picks the most recent small idle cli/tui session (read-only,
never deletes) or takes one via argv. t_silence derivation: settle wait capped
120s.
@Kyzcreig
Kyzcreig force-pushed the fix/livesync-probe-stored-id-v2 branch from a5dffe9 to f41060e Compare July 10, 2026 20:16
Comment thread scripts/livesync-rc1-probe.py
…d, per-iteration status reset, a1-watch SID guard (Greptile #272)
@Kyzcreig
Kyzcreig merged commit 38973e0 into main Jul 10, 2026
35 checks passed
@Kyzcreig
Kyzcreig deleted the fix/livesync-probe-stored-id-v2 branch July 10, 2026 20:38
Kyzcreig added a commit that referenced this pull request Jul 10, 2026
…t the runtime id

The shipped hook polled with activeSessionId (live registry id); the RPC
only resolves stored state.db ids, so every poll 4044'd quietly and no
cross-surface rows ever rendered — found by the A2 eyes-on gate (screenshot
proof: rows committed to state.db, open transcript never updated). Same
contract gap the RC-1 probe hit server-side in #272.

- hook takes storedSessionId, puts it on the wire; runtime id still keys
  client state; fallback to runtime id only when no stored id exists yet
- desktop-controller passes selectedStoredSessionId
- 2 new regression tests pin the wire-id contract both ways
Kyzcreig added a commit that referenced this pull request Jul 10, 2026
… eyes-on catch) (#279)

* fix(desktop): session.changes poll must use the STORED session id, not the runtime id

The shipped hook polled with activeSessionId (live registry id); the RPC
only resolves stored state.db ids, so every poll 4044'd quietly and no
cross-surface rows ever rendered — found by the A2 eyes-on gate (screenshot
proof: rows committed to state.db, open transcript never updated). Same
contract gap the RC-1 probe hit server-side in #272.

- hook takes storedSessionId, puts it on the wire; runtime id still keys
  client state; fallback to runtime id only when no stored id exists yet
- desktop-controller passes selectedStoredSessionId
- 2 new regression tests pin the wire-id contract both ways

* fix(desktop): drop dev-local node_modules symlink + dead wireId guard (Greptile #279)

---------

Co-authored-by: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com>
Kyzcreig added a commit that referenced this pull request Sep 25, 2026
)

Removes the session.changes RPC, _load_session_sync_config, _SESSION_*
constants, dashboard.session_sync default, /api/status capabilities +
session_sync block, SessionDB.get_messages_after, and the three
scripts/livesync-* probes. No caller left: 0 session.changes calls in
2026-06-02..09-25 logs; the desktop consumer is gone.

Kept: idx_messages_session_id (used by get_messages_around,
session_lifecycle_statuses and upstream code), the id/tool_call_id fields
in _history_to_messages, and the dashboard state.db startup log (its test
moves to tests/hermes_cli/test_dashboard_state_db_log.py).

Verified: pytest tests/test_hermes_state_messages.py
tests/test_hermes_state_search.py tests/hermes_cli/test_dashboard_state_db_log.py
-> 293 passed; tests/test_tui_gateway_server.py -k history_to_messages -> pass.
Kyzcreig added a commit that referenced this pull request Sep 25, 2026
…tream-695 branches

Rows #268/#272 -> audit/scripts_misc/revert-livesync @ b0c914c,
#116/#118 -> audit/scripts_misc/revert-mem0-bgr @ 9c09f9e,
#695 -> audit/scripts_misc/upstream-695 @ 830f68a (off upstream/main).
Notes carry the verify commands and real output. Nothing merged.
Kyzcreig added a commit that referenced this pull request Sep 25, 2026
)

Removes the session.changes RPC, _load_session_sync_config, _SESSION_*
constants, dashboard.session_sync default, /api/status capabilities +
session_sync block, SessionDB.get_messages_after, and the three
scripts/livesync-* probes. No caller left: 0 session.changes calls in
2026-06-02..09-25 logs; the desktop consumer is gone.

Kept: idx_messages_session_id (used by get_messages_around,
session_lifecycle_statuses and upstream code), the id/tool_call_id fields
in _history_to_messages, and the dashboard state.db startup log (its test
moves to tests/hermes_cli/test_dashboard_state_db_log.py).

Verified: pytest tests/test_hermes_state_messages.py
tests/test_hermes_state_search.py tests/hermes_cli/test_dashboard_state_db_log.py
-> 293 passed; tests/test_tui_gateway_server.py -k history_to_messages -> pass.
Kyzcreig added a commit that referenced this pull request Sep 25, 2026
Fork-PR audit FINAL DROP (card t_46cf57ba, lead t_03e35f0e, FINAL.md row #352).
#352 shipped payload.message_ids on message.complete so the desktop
live-sync poll could stamp optimistic rows. The livesync seam is being
reverted (#268/#272, audit/scripts_misc/revert-livesync) and the apps/
consumer is already DROP by D9; no fork consumer of message_ids remains
(git grep apps/ ui-tui/ web/: 0 hits) and upstream never emitted it.

Removes _completed_turn_committed_ids(), its call under history_lock, the
payload field, and tests/tui_gateway/test_completed_turn_committed_ids.py.

Verified: test-gate narrow run test_failed_turn_retention.py +
test_desktop_runtime_footer.py + test_server_no_duplicate_defs.py:
29 passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant