Skip to content

feat(#3278): persist Claude session per (pipeline,slice,role) across event pods (re-land of orphaned #3286) - #3332

Merged
jwbron merged 1 commit into
mainfrom
egg/3278-session-store-reland
Jun 26, 2026
Merged

feat(#3278): persist Claude session per (pipeline,slice,role) across event pods (re-land of orphaned #3286)#3332
jwbron merged 1 commit into
mainfrom
egg/3278-session-store-reland

Conversation

@jwbron

@jwbron jwbron commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Why this PR exists (re-land)

#3278 was marked done but its code never reached main. PR #3286 ("Closes #3278", MERGED) was a stacked PR whose base was egg/3279-reseed-threshold-injection, not main. #3279 (PR #3284) merged to main at 05:25; #3286 then merged at 07:32 into the already-merged #3279 branch, which was a dead branch — so #3286's commits never propagated to main. All four of #3278's new files are absent from main today, while #3279's threshold injection is present. GitHub still shows #3286 as MERGED / Closes #3278, hence the issue looked done.

This re-applies the #3278-only diff (everything #3286 added on top of the #3279 branch tip) onto current main. The #3279 content is already on main and is excluded. Sole rebase conflict was the env-var table in docs/architecture/context-discipline.md, resolved to keep main's (#3284) EGG_RESEED_THRESHOLD row alongside #3278's new EGG_SESSION_STATE_FILE / CLAUDE_CONFIG_DIR rows.

What

Make BRC warm resume (#3200) actually fire by persisting the Claude Code session per (pipeline, slice, role) across the one-shot event pods — the durable copy is orchestrator-owned and reached only over the controlled API, so the sandbox never writes host state.

claude --resume <session_id> re-enters a session by reading its local transcript ($CLAUDE_CONFIG_DIR/projects/<cwd-slug>/<session_id>.jsonl). Under the orchestrator-owned event loop (#3164) every BRC event is a fresh pod, so that transcript dies with the pod. Persisting only the session_id+occupancy pointer (the original #3276 scope) would let the gate record below_threshold "resume" decisions while every event silently cold-starts. The transcript itself has to cross pods.

Design (constraint: sandbox never writes host state)

  • Live store stays ephemeral — the agent's $CLAUDE_CONFIG_DIR is the pod's own filesystem (no host mount); Claude Code writes the transcript locally during the run, unchanged.
  • Durable copy is orchestrator-owned in Redissession_state_store.py keeps one TTL'd key per (pipeline, slice, role) holding the pointer and transcript together (no split-brain; TTL reaps abandoned state). Only the orchestrator writes it.
  • Round-trip over the controlled APIegg-orch session-state pull (before the agent) re-materialises the prior transcript + pointer into the pod so --resume finds a real session; egg-orch session-state push (after) ships the updated session back. Both best-effort and timeout-bounded; a failed sync degrades to a safe cold reseed.

The existing egg_agent substrate (session.py / reseed.py / __main__.py) is untouched.

Changes

  • orchestrator/session_state_store.py — Redis store (TTL, size-guarded, best-effort).
  • orchestrator/routes/session_state.pyGET/POST /api/v1/pipelines/<pid>/session-state (registered in api.py).
  • sandbox/egg_lib/session_state_sync.py — slug + transcript/pointer file I/O (pure).
  • sandbox/egg_lib/cli_session_state.pyegg-orch session-state pull|push (best-effort); verb wired in orch_cli.py.
  • orchestrator/concurrent_executor.py — inject CLAUDE_CONFIG_DIR + EGG_SESSION_STATE_FILE into event pods, gated on session_resume_enabled() (default pods byte-identical).
  • orchestrator/consensus_wrapper.py — pull before / push after the agent, preserving exit code; golden regenerated.
  • docs/architecture/context-discipline.md — documents the cross-pod persistence layer.

Tests

The original #3278 test suite comes along (store, route, sync, CLI, env-injection gating, wrapper golden). Per request, the suite was not re-run locally in this re-land session — relying on CI.

Verification still owed in-cluster

--resume end-to-end across two real event pods (the spike confirmed file mechanics + relocation locally; the resume call itself needs in-cluster gateway/API-key auth). Also worth measuring transcript transfer cost per resumed event.

Related

Closes #3278

…event pods

Re-land of the orphaned #3278 work. PR #3286 was merged into the
already-merged stacked branch egg/3279-reseed-threshold-injection rather
than main, so its commits never reached main even though the PR shows
MERGED and "Closes #3278". This re-applies the #3278-only diff (the
#3279 threshold injection is already on main) onto current main.

New: orchestrator/session_state_store.py (Redis TTL'd per-(pipeline,
slice,role) store holding pointer+transcript together), routes/
session_state.py (GET/POST /api/v1/pipelines/<pid>/session-state),
sandbox/egg_lib/{session_state_sync,cli_session_state}.py (slug + file
I/O + egg-orch session-state pull|push).
Wired: concurrent_executor.spawn_event injects CLAUDE_CONFIG_DIR +
EGG_SESSION_STATE_FILE gated on session_resume_enabled(); consensus_
wrapper pull-before/push-after; api.py blueprint reg; orch_cli verb.

Closes #3278
@jwbron
jwbron merged commit 7d8a4bb into main Jun 26, 2026
12 of 13 checks passed
james-in-a-box Bot pushed a commit that referenced this pull request Jun 26, 2026
Merging main into the PR branch pulls in #3332's session-store files
(session_state_store.py, session_state.py, session_state_sync.py) and
edits to concurrent_executor.py / consensus_wrapper.py, which add SDLC
ledger tokens (slice-N / TASK-N / cq-N) not present in the committed
baseline. The advisory ratchet's TestRepoBaselineIsClean flagged these
as net-new in the pull/3333/merge corpus that CI tests.

Regenerated via scripts/check-ledger-references.py --update-baseline so
a fresh checkout of the merged corpus scans clean.
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.

Persist the Claude Code session store per (pipeline,slice,role) so #3200 warm resume survives one-shot event pods

1 participant