feat: add CEO session resume via Claude --resume/--session-id - #1065
feat: add CEO session resume via Claude --resume/--session-id#1065mihirathale98 wants to merge 8 commits into
Conversation
Enable CEO sessions to be resumed after interruption by persisting Claude session IDs and using --resume on respawns. The completion guard now continues the same conversation instead of cold-starting, and `factory resume <path>` reconnects interactively via os.execvp.
Thread session_id/resume_session_id through invoke_agent instead of a separate _invoke_agent_core function, preserving backward compat with existing mocks. Extract session_id from events.jsonl after each spawn. Add 30 tests covering all 7 change areas.
Sentrux Quality ReportAbsoluteDiff (vs base branch) |
cmd_resume now prints to stderr with the message 'No CEO session found to resume.' instead of the old stdout message 'No checkpoint found.'
Test now creates a CEO session ID and mocks os.execvp/shutil.which, matching the pattern in test_session_resume.py.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1065 +/- ##
==========================================
+ Coverage 88.36% 88.41% +0.05%
==========================================
Files 127 127
Lines 14864 14937 +73
Branches 2347 2362 +15
==========================================
+ Hits 13134 13206 +72
Misses 1234 1234
- Partials 496 497 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… verdict Add session threading (session_id, resume_session_id) to AgentRunRequest, CycleState, RunnerMeta, and CEO completion guard. Document ParallelConfig model, CheckpointState parallel fields, superseded verdict, and cmd_resume session resolution. Fix AgentRole enumeration to match source.
|
@mihirathale98 Looks like there are some conflicts with the main branch |
|
@ceo-review |
There was a problem hiding this comment.
✅ Factory Review: KEEP
Verdict: KEEP
Reason: QA: CLEAN — 3836 tests pass, 0 issues. Session resume feature correctly threads session IDs across spawns, handles all edge cases, and has comprehensive test coverage (30 new tests).
QA Analysis
Adversarial QA Report — PR #1065
Feature: CEO session resume via Claude --resume/--session-id
Approach: Skeptical user testing — every claim verified with evidence
Test Results
Session Persistence (Tests 1-3)
| # | Test | Result | Evidence |
|---|---|---|---|
| 1 | Write + read round-trip | PASS | write_ceo_session_id → read_ceo_session_id returns exact value. JSON contains session_id and created fields. |
| 2 | Malformed JSON | PASS | Writing {garbage to session.json → read_ceo_session_id returns None (no crash) |
| 3 | Missing file | PASS | Non-existent path → read_ceo_session_id returns None |
Claude Runner Command Building (Tests 4-8)
| # | Test | Result | Evidence |
|---|---|---|---|
| 4 | session_id → --session-id | PASS | build_command with session_id='my-sid-123' produces ['--session-id', 'my-sid-123'], no --resume |
| 5 | resume_session_id → --resume | PASS | build_command with resume_session_id='resume-456' produces ['--resume', 'resume-456'], no --session-id |
| 6 | Both set → resume wins | PASS | When both provided, --resume present, --session-id absent |
| 7 | Neither set → no flag | PASS | Neither --resume nor --session-id in command |
| 8 | Interactive command same logic | PASS | build_interactive_command with resume_session_id produces --resume flag |
Cycle State & Event Extraction (Tests 9-13)
| # | Test | Result | Evidence |
|---|---|---|---|
| 9 | CycleState round-trips claude_session_id | PASS | Default None, set to test-sid-789, write → read returns exact value |
| 10 | delete_cycle_state cleans both files | PASS | After delete: read_ceo_session_id returns None, read_cycle_state returns None |
| 11 | _extract_session_id filters by agent=ceo | PASS | With researcher and ceo events, returns only the ceo's session_id right-one |
| 12 | _extract_session_id with no ceo events | PASS | Only researcher events → returns None |
| 13 | _extract_session_id with empty data | PASS | CEO event with empty data dict → returns None |
cmd_resume Behavior (Tests 14-17)
| # | Test | Result | Evidence |
|---|---|---|---|
| 14 | Prefers cycle state over session file | PASS | With both present, os.execvp called with ['claude', '--resume', 'cycle-preferred'] |
| 15 | Falls back to session file | PASS | Session file only → ['claude', '--resume', 'file-only-sid'] |
| 16 | Returns 1 with no session | PASS | Exit code 1, stderr: "No CEO session found to resume." |
| 17 | Passes --model flag | PASS | ['claude', '--resume', 'model-sid', '--model', 'opus-5'] |
Schema & Model Validation (Tests 18-21)
| # | Test | Result | Evidence |
|---|---|---|---|
| 18 | All 13 agent roles present | PASS | Verified all expected roles in AgentRole Literal type |
| 19 | RunnerMeta defaults | PASS | supports_session_resume defaults to False |
| 20 | CycleState accepts new field | PASS | claude_session_id='test-sid' accepted by strict Pydantic model |
| 21 | AgentRunRequest accepts session fields | PASS | Both session_id and resume_session_id accepted |
Automated Test Suite (30 tests)
All 30 tests in tests/test_session_resume.py pass (0.17s).
All 22 tests in tests/test_checkpoint.py pass (0.26s).
Verdict
PASS — All 21 adversarial tests pass with evidence. All 52 targeted tests pass. No issues found.
The feature correctly:
- Generates and persists session IDs before CEO spawns
- Threads session_id on first spawn and resume_session_id on respawns
- Captures returned session_id from events and persists to cycle state
- Cleans up both cycle and session state on completion/abort
- Provides a working
factory resumecommand with proper fallback chain - Handles all edge cases (malformed data, missing files, missing binary)
Posted by Factory CEO
Store interactive/mode metadata in session.json at CEO launch so cmd_resume can inject a continuation prompt for headless sessions while leaving interactive sessions with a bare --resume.
When a CEO session exits without completing (crash, Ctrl+C, respawn cap hit), print the session ID and resume command to stderr so users can manually resume. Skips printing when the cycle completed cleanly since the session state is already cleaned up.
Closes #1048
Changes
session_idandresume_session_idtoAgentRunRequest,claude_session_idtoCycleStatesupports_session_resumecapability flag toRunnerMetasupports_session_resume=True, emit--session-id/--resumeflags in bothbuild_command()andbuild_interactive_command()read_ceo_session_id()/write_ceo_session_id()helpers for.factory/state/session.json, clear session file indelete_cycle_state()write_ceo_session_id(), pass to both interactive and headless pathssession_idon first spawn, capture returned session from events, useresume_session_idon respawns — preserving conversation context across CEO respawnsfactory resume: Replace checkpoint-display command with actual session reconnection viaos.execvp("claude", ["claude", "--resume", session_id]), checking bothCycleState.claude_session_idand.factory/state/session.jsonwrite_ceo_session_id()now storesinteractiveandmodemetadata.cmd_resumereads this to decide behavior:claude --resume— user drives conversation-pcontinuation prompt + CEO system prompt +--disallowedTools Agentso the CEO auto-continuesread_ceo_session(): New function returning full session metadata dict