refactor(sessions): move parent-session forking behind the accessor boundary#101699
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 7, 2026, 12:36 PM ET / 16:36 UTC. Summary PR surface: Source +149, Tests +50, Other -1. Total +198 across 13 files. Reproducibility: not applicable. as a behavior-preserving refactor. The review relies on source comparison plus the PR's live-output parity proof rather than a current-main failure reproduction. 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 standalone accessor-boundary slice after exact-head CI and maintainer session-state review, while leaving the broader SQLite runtime flip in #98236. Do we have a high-confidence way to reproduce the issue? Not applicable as a behavior-preserving refactor. The review relies on source comparison plus the PR's live-output parity proof rather than a current-main failure reproduction. Is this the best way to solve the issue? Yes. Moving the remaining parent-fork transcript and child-entry persistence behind the session accessor is the clean owner-boundary step; leaving raw runtime/store writes or folding every seam into the giant SQLite flip would be harder to review. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 453f5968bbca. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +149, Tests +50, Other -1. Total +198 across 13 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
Review history (3 earlier review cycles)
|
4b8a8b4 to
75f2d08
Compare
|
Superseded by the Evidence section in the PR body (refreshed for head 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 boundary ops: |
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]>
…oundary (openclaw#101699) * refactor(sessions): move parent-session forking behind the accessor boundary * fix(sessions): lazy-load fork transcript runtime to keep accessor imports lean * test(sessions): align parent fork mock shape
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]>
…oundary (openclaw#101699) * refactor(sessions): move parent-session forking behind the accessor boundary * fix(sessions): lazy-load fork transcript runtime to keep accessor imports lean * test(sessions): align parent fork mock shape
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
Related: #101179
What Problem This Solves
After #101179 moved the raw fork-transcript write behind the accessor, the rest of parent-session forking still bypassed the session accessor seam (#88838):
session-fork.runtime.tsresolved parent transcript paths directly (resolveSessionFilePath) and owned branch-selection reads, andsession-fork.tspersisted the forked child entry through a raw whole-storeupdateSessionStoremutation with hand-rolled alias cleanup. Callers passed rawsessionsDir/targetSessionsDirpaths across the boundary. This is prep for the SQLite storage flip (#98236), which makes the whole fork operation storage-owned.Why This Change Was Made
forkSessionFromParentTranscript(parent-branch transcript fork composingcreateForkedSessionTranscript) andforkSessionEntryFromParentTarget(fork + child-entry persist + alias cleanup as one store operation). Pure branch-selection logic moves tosrc/config/sessions/session-fork-transcript.runtime.ts, loaded lazily so accessor consumers do not pull the agents session-manager runtime (and its module-init package metadata reads) at import time.session-fork.tsbecomes a thin adapter;session-fork.runtime.tsshrinks to the lazy token-estimation seam (its transcript-tail usage reads flow through gateway fs helpers, so the fork decision stays caller-supplied via a documentedresolveDecisioncallback; the SQLite flip resolves the decision inside the storage boundary).targetStorePathcarries the cross-agent worktree-session fork (session-create-service.ts) as an explicit contract instead of a directory-path hack.subagent-spawn,session-create-service,session-parent-fork-prepare) pass session keys and store paths, not directories.User Impact
No user-visible behavior change. Thread/subagent/operator session forks keep the same branch selection, oversized-parent skip decision, fork placement (beside the mutated store), and entry/alias persistence 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, two configured agents to exercise the cross-agent path, public RPCs only, then direct sessions.json/transcript-JSONL inspection; macOS, Node v24.18.0). Run onorigin/mainbaseline (c8d95da14c) and this branch at head003c51975c— 17/17 checkpoints pass on both (behavior parity plus the new contract). 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 (session-fork.tswrite debt -> 0)session-accessor.parent-fork.test.tsagainst the accessor API; store-dir boundary test exercises real transcript files instead of runtime mockssubagent-spawn.*family,agent-consult-runtime,server.sessions.create,agent-runner-session-reset, accessor suites, plus the command/channel isolation suites the earlier CI run flagged (agents.delete,channels.list,channel-setup/*,session.test.ts)pnpm buildgreen with no[INEFFECTIVE_DYNAMIC_IMPORT]for the new lazy boundary.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/mainclean (two rounds; latest on003c51975c)