test(claude-code): cover synthetic-to-real session binding end to end - #31
Merged
Conversation
CLA-01 requires the overlay to bind a synthetic session id to the real Claude Code session observed by the hook, and to persist that mapping. The unit tests in session-map cover recordSessionMapping in isolation, but no gateway-level test exercised the resolve path: a claude-synth- request landing, being resolved to the latest real hook session, and the binding being written. Add a gateway e2e test: seed a real hook session, send a synthetic-id request, assert it binds to the real id, then confirm the binding stays anchored to the first real id even after a newer session becomes latest. Adapter suite 146/146, typecheck clean. No production code changed.
…ssion report CLA-05 requires the report to show per-turn and cumulative saved chars. The report already renders 'Latest eviction savings' (from the snapshot) and 'Cumulative eviction savings' (summed across recent turn bindings), but no test asserted the eviction lines — only reduction metrics were covered. Add a test: seed a snapshot + two turn bindings (300, 200 saved chars), assert 'Latest eviction savings: 200' (most recent turn) and 'Cumulative eviction savings: 500' (sum). No production code changed.
Stage 4 semantic-delta wiring needs a turnSeq — a per-session, monotonically increasing real turn number to slice DeltaViews across requests. It cannot be derived from the message array (resent whole each request and rewritten by eviction, so not monotonic), and nothing else in the codebase maintains one (bindings have no absolute turn number; gateway only has revision = content sha256). Add turn-counter.ts: readClaudeTurnSeq (0 when missing/corrupt, fail-open) and bumpClaudeTurnSeq (load -> +1 -> atomic persist under claude-context/). Storage only — NOT yet wired into the gateway; the wiring waits on supervisor confirmation that turnSeq = a persisted per-session counter. 5 unit tests: default 0, monotonic increment, survives restart, per-session isolation, corrupt-file fail-open. Adapter suite 152/152, typecheck clean.
…eway) Encapsulate the whole per-request V2 semantic flow in one fail-open function runSemanticPipeline: bump turn counter -> build+persist this turn's RawSemanticTurnRecord -> load registry -> rebuild the (lastProcessedTurnSeq, now] interval into a DeltaView -> updateRegistryFromDelta -> persist the registry with expectedVersion, advancing the watermark only when the registry actually changed. Watermark note: the shared mapper already sets lastProcessedTurnSeq = delta.toTurnSeqInclusive in its patch, so a successful update returns a registry whose watermark is advanced — persisted as-is. changed=false (estimator failed / no updates / version conflict) leaves the watermark put so the next request re-covers the same interval. updateRegistryFromDelta is injected (like applyArchivePlan's archiveFn) so tests drive the branches with a fake, no live LLM. 5 unit tests: changed persists + advances watermark, unchanged does not persist, version conflict abandons, interval covers only (from, now], internal error fails open (ran=false). STORAGE/ORCHESTRATION ONLY — the gateway if(estimator) call block (PART 2) is a separate main-flow change. Adapter suite 157 green.
Add a feature-gated, fail-open semantic block in handleRequest, right after the session id is resolved and before eviction planning. When an estimator is configured (resolveClaudeTaskStateEstimator, env-driven, default off) it runs runSemanticPipeline over the inbound envelope.messages; any error is caught and logged, never propagated — the request proceeds unchanged. Uses envelope.messages (the real inbound history) rather than the eviction-rewritten overlayMessages, since the semantic delta must reflect true turns. With no estimator configured the block is a no-op, so all 157 existing tests stay green — proving the wiring doesn't disturb the existing request flow. typecheck clean.
…en when wired Add an optional resolveEstimator injection seam to the gateway dependencies (mirrors the existing cloneRequestPayload seam) so tests can drive the semantic block with a fake estimator instead of a live LLM. Two gateway e2e tests: (1) an injected estimator returning task updates -> the request succeeds and a task registry is persisted at task-state/<sessionId>/registry.json with version>=1, proving the semantic block actually ran; (2) an injected estimator whose estimate() throws -> the request still forwards 200 unchanged, proving the block is fail-open and never blocks the request path. With no estimator configured (default) the block is a no-op, already covered by the existing 157 green tests. Adapter suite 159 green, typecheck clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLA-01 requires the overlay to bind a synthetic session id to the real Claude Code session observed by the hook, and to persist that mapping. The unit tests in session-map cover recordSessionMapping in isolation, but no gateway-level test exercised the resolve path: a claude-synth- request landing, being resolved to the latest real hook session, and the binding being written.
Add a gateway e2e test: seed a real hook session, send a synthetic-id request, assert it binds to the real id, then confirm the binding stays anchored to the first real id even after a newer session becomes latest.
Adapter suite 146/146, typecheck clean. No production code changed.