refactor(gateway): route chat transcript injection through the session accessor#101688
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 7, 2026, 11:55 AM ET / 15:55 UTC. Summary PR surface: Source +173, Tests +91, Other 0. Total +264 across 9 files. Reproducibility: not applicable. this is a behavior-preserving refactor PR, not a bug report. The contributor supplied a live-output gateway proof showing current-main parity for the changed chat.inject persistence path. Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Land this accessor slice only after maintainer exact-head review confirms chat.inject, abort partials, source-reply media mirrors, updatedAt touches, and the boundary ratchet still preserve current behavior; otherwise deliberately defer it to the broader storage-flip lane. Do we have a high-confidence way to reproduce the issue? Not applicable: this is a behavior-preserving refactor PR, not a bug report. The contributor supplied a live-output gateway proof showing current-main parity for the changed chat.inject persistence path. Is this the best way to solve the issue? Yes: routing transcript injection and mirror rewrites through the session accessor is the maintainable boundary for the SQLite migration prep. The safer alternative is not another custom gateway writer, but exact-head validation or deliberate deferral to the broader storage-flip branch. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 3d206140f3f0. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +173, Tests +91, Other 0. Total +264 across 9 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
6a64c8b to
0dfff95
Compare
|
E2E behavior proof (ad-hoc harness per the live-proof pattern: isolated state dir, real gateway process, public RPCs only, then direct store/transcript artifact inspection — harness is not committed). Environment: macOS, Node v24.18.0, gateway booted from this branch's source via the repo test-instance machinery ( Checkpoints covering this PR's surface (branch run, verbatim): What this proves for the accessor-routed injection path: |
Squash-and-replay of sqlite/sessions-transcripts-storage-flip (361 commits, old head 7766516) onto main @ 21f2512, per the prep-series plan: Phase 0/1 accessor-prep PRs (#101178-#101182, #101688, #101699) landed the seam on main first, so this rebase reduces to the SQLite storage implementation behind the session accessor. Beyond the flip branch content, this merge: - implements findTranscriptEvent (reverse-seq), recordInboundSessionMeta and updateSessionLastRoute over SQLite (deriveLastRoutePatch shared with the file-era store for doctor paths) - adds cross-agent fork support (targetStorePath) so worktree/cross-agent sessions.create forks land child transcript rows in the target agent DB - keeps main's newer behavior across the conflict surface (work admission, archive-gated deletes, preflight overflow budgeting, model fallback, replyOptions dispatch shape, lifecycle-header ordering) - adopts the storage-neutral ContextEngineSessionTarget while preserving the deprecated CompactResult.sessionFile for shipped plugin readers Known remainder (tracked for follow-up commits on this PR): ~415 test-case failures across 43 files, dominated by stale vi.mock factories missing main-side exports; prod typecheck, full build, and boundary ratchet are green. Co-Authored-By: Claude Fable 5 <[email protected]>
…n accessor (openclaw#101688) * refactor(gateway): route chat transcript injection through the session accessor * fix(gateway): avoid map-spread in source reply mirror rewrite results * perf(sessions): find injected transcript duplicates with a reverse early-exit scan
Squash-and-replay of sqlite/sessions-transcripts-storage-flip (361 commits, old head 7766516) onto main @ 21f2512, per the prep-series plan: Phase 0/1 accessor-prep PRs (#101178-#101182, #101688, #101699) landed the seam on main first, so this rebase reduces to the SQLite storage implementation behind the session accessor. Beyond the flip branch content, this merge: - implements findTranscriptEvent (reverse-seq), recordInboundSessionMeta and updateSessionLastRoute over SQLite (deriveLastRoutePatch shared with the file-era store for doctor paths) - adds cross-agent fork support (targetStorePath) so worktree/cross-agent sessions.create forks land child transcript rows in the target agent DB - keeps main's newer behavior across the conflict surface (work admission, archive-gated deletes, preflight overflow budgeting, model fallback, replyOptions dispatch shape, lifecycle-header ordering) - adopts the storage-neutral ContextEngineSessionTarget while preserving the deprecated CompactResult.sessionFile for shipped plugin readers Known remainder (tracked for follow-up commits on this PR): ~415 test-case failures across 43 files, dominated by stale vi.mock factories missing main-side exports; prod typecheck, full build, and boundary ratchet are green. Co-Authored-By: Claude Fable 5 <[email protected]>
…n accessor (openclaw#101688) * refactor(gateway): route chat transcript injection through the session accessor * fix(gateway): avoid map-spread in source reply mirror rewrite results * perf(sessions): find injected transcript duplicates with a reverse early-exit scan
Squash-and-replay of sqlite/sessions-transcripts-storage-flip (361 commits, old head 7766516) onto main @ 21f2512, per the prep-series plan: Phase 0/1 accessor-prep PRs (#101178-#101182, #101688, #101699) landed the seam on main first, so this rebase reduces to the SQLite storage implementation behind the session accessor. Beyond the flip branch content, this merge: - implements findTranscriptEvent (reverse-seq), recordInboundSessionMeta and updateSessionLastRoute over SQLite (deriveLastRoutePatch shared with the file-era store for doctor paths) - adds cross-agent fork support (targetStorePath) so worktree/cross-agent sessions.create forks land child transcript rows in the target agent DB - keeps main's newer behavior across the conflict surface (work admission, archive-gated deletes, preflight overflow budgeting, model fallback, replyOptions dispatch shape, lifecycle-header ordering) - adopts the storage-neutral ContextEngineSessionTarget while preserving the deprecated CompactResult.sessionFile for shipped plugin readers Known remainder (tracked for follow-up commits on this PR): ~415 test-case failures across 43 files, dominated by stale vi.mock factories missing main-side exports; prod typecheck, full build, and boundary ratchet are green. Co-Authored-By: Claude Fable 5 <[email protected]>
Squash-and-replay of sqlite/sessions-transcripts-storage-flip (361 commits, old head 7766516) onto main @ 21f2512, per the prep-series plan: Phase 0/1 accessor-prep PRs (#101178-#101182, #101688, #101699) landed the seam on main first, so this rebase reduces to the SQLite storage implementation behind the session accessor. Beyond the flip branch content, this merge: - implements findTranscriptEvent (reverse-seq), recordInboundSessionMeta and updateSessionLastRoute over SQLite (deriveLastRoutePatch shared with the file-era store for doctor paths) - adds cross-agent fork support (targetStorePath) so worktree/cross-agent sessions.create forks land child transcript rows in the target agent DB - keeps main's newer behavior across the conflict surface (work admission, archive-gated deletes, preflight overflow budgeting, model fallback, replyOptions dispatch shape, lifecycle-header ordering) - adopts the storage-neutral ContextEngineSessionTarget while preserving the deprecated CompactResult.sessionFile for shipped plugin readers Known remainder (tracked for follow-up commits on this PR): ~415 test-case failures across 43 files, dominated by stale vi.mock factories missing main-side exports; prod typecheck, full build, and boundary ratchet are green. Co-Authored-By: Claude Fable 5 <[email protected]>
Related: #88838
Related: #98236
What Problem This Solves
src/gateway/server-methods/chat.tsandchat-transcript-inject.tswere among the last gateway runtime surfaces doing direct durable session/transcript persistence outside the session accessor seam (#88838): rawresolveSessionFilePathpath resolution, three directreadSessionTranscriptIndexJSONL reads, a directupdateSessionStoreEntrywrite, and ad-hoc transcript file creation. This keeps rebase conflicts for the SQLite storage flip (#98236) entangled with gateway behavior.Why This Change Was Made
Prep work for the storage flip: move persistence behind the accessor on
mainso the flip branch's chat.ts becomes a storage-implementation swap instead of a behavior-plus-storage rewrite.loadTranscriptEvents(scope)andfindTranscriptEvent(scope, match)(newest-first, early-exit reverse scan) — the hot idempotent append path never materializes the whole transcript.chat-transcript-inject.tspersists throughpersistSessionTranscriptTurnwith scope identity, in-lock assistant-scoped idempotency (shouldAppend+idempotencyLookup: "caller-checked"), and the session marker touch folded intotouchSessionEntry.rewriteSourceReplyTranscriptMirrors) around the existing branch-awarerewriteTranscriptEntriesInRuntimeTranscript.chat.tsjoinsmigratedSessionAccessorWriteFiles; its debt-baseline entries drop to zero.mainintentionally: legacy transcripts with id-less assistant rows still dedupe by idempotency key (real shipped file state).User Impact
No user-visible behavior change. Gateway-injected assistant messages (webchat media, abort partials,
chat.inject) persist through the accessor with the same idempotency, dedupe, and session-marker semantics.Evidence
End-to-end behavior proof — ad-hoc uncommitted harness (isolated state dir, real gateway process booted from this branch via
test/helpers/openclaw-test-instance.ts, public RPCs only, then direct sessions.json/transcript-JSONL inspection; macOS, Node v24.18.0). Run onorigin/mainbaseline (c8d95da14c) and this branch — 17/17 checkpoints pass on both (behavior parity). Checkpoints on this PR's surface:Static/test gates
node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.src.jsonclean; oxfmt + type-aware oxlint clean on changed filesnode scripts/check-session-accessor-boundary.mjsgreen; debt baseline regenerated (chat.ts write debt -> 0)node scripts/run-vitest.mjs src/gateway/server-methods— 2760 passed (pre-existingmodels.test.tsfailures reproduce identically on pristineorigin/main; local ambient auth state)chat.directive-tags(268),chat.abort-persistence,chat.inject.parentid,session-accessor, boundary-script tests.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/mainclean after two accepted findings were fixed (map-spread lint; full-parse idempotency lookup replaced with the reverse early-exit scan)