Skip to content

feat: add CEO session resume via Claude --resume/--session-id - #1065

Open
mihirathale98 wants to merge 8 commits into
mainfrom
factory/run-070d24ad
Open

feat: add CEO session resume via Claude --resume/--session-id#1065
mihirathale98 wants to merge 8 commits into
mainfrom
factory/run-070d24ad

Conversation

@mihirathale98

@mihirathale98 mihirathale98 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #1048

Changes

  • Models: Add session_id and resume_session_id to AgentRunRequest, claude_session_id to CycleState
  • Runner protocol: Add supports_session_resume capability flag to RunnerMeta
  • Claude runner: Set supports_session_resume=True, emit --session-id/--resume flags in both build_command() and build_interactive_command()
  • Session persistence: Add read_ceo_session_id()/write_ceo_session_id() helpers for .factory/state/session.json, clear session file in delete_cycle_state()
  • CEO launch: Generate UUID before launch, persist via write_ceo_session_id(), pass to both interactive and headless paths
  • Completion guard: Thread session_id on first spawn, capture returned session from events, use resume_session_id on respawns — preserving conversation context across CEO respawns
  • factory resume: Replace checkpoint-display command with actual session reconnection via os.execvp("claude", ["claude", "--resume", session_id]), checking both CycleState.claude_session_id and .factory/state/session.json
  • Mode-aware resume: write_ceo_session_id() now stores interactive and mode metadata. cmd_resume reads this to decide behavior:
    • Interactive sessions (design, create): bare claude --resume — user drives conversation
    • Headless sessions (improve, build, research): injects -p continuation prompt + CEO system prompt + --disallowedTools Agent so the CEO auto-continues
    • CycleState presence always implies headless
  • read_ceo_session(): New function returning full session metadata dict
  • Tests: 37 tests covering all change areas (models, runner flags, persistence, guard threading, resume command with interactive/headless paths)

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.
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

Sentrux Quality Report

Absolute

Scanning ....
[scan] git ls-files: 438 total, 426 kept, 12 dropped (ext:12, meta:0, big:0)
[build_project_map] 426 files, 87 unique dirs, 80 cache misses, 4.9ms
[resolve] 668 resolved, 999 unresolved (of 1667 total specs)
[resolve_imports] project_map 5.0ms, suffix_idx 0.7ms, suffix_resolve 11.7ms, total 17.4ms
[build_graphs] 426 files | maps 2.1ms, imports 17.6ms, calls+inherit 3.9ms, total 23.5ms | 667 import, 5351 call, 0 inherit edges
sentrux check — 3 rules checked

Quality: 4444

✗ [Error] max_cc: 5 function(s) exceed max cyclomatic complexity of 30
    factory/cli/ceo.py:cmd_ceo (cc=104)
    factory/study.py:study_project_local (cc=42)
    factory/workflow/validation.py:validate_workflow (cc=40)
    factory/cli/_wizard.py:_welcome_wizard (cc=39)
    factory/cli/ceo.py:cmd_run (cc=37)

✗ 1 violation(s) found

Diff (vs base branch)

Scanning ....
[scan] git ls-files: 438 total, 426 kept, 12 dropped (ext:12, meta:0, big:0)
[build_project_map] 426 files, 87 unique dirs, 80 cache misses, 4.8ms
[resolve] 668 resolved, 999 unresolved (of 1667 total specs)
[resolve_imports] project_map 4.9ms, suffix_idx 0.8ms, suffix_resolve 11.9ms, total 17.6ms
[build_graphs] 426 files | maps 1.8ms, imports 17.7ms, calls+inherit 4.4ms, total 23.9ms | 667 import, 5351 call, 0 inherit edges
sentrux gate — structural regression check

Quality:      4445 -> 4444
Coupling:     0.81 → 0.81
Cycles:       5 → 5
God files:    2 → 2

Distance from Main Sequence: 0.34

✗ DEGRADED
  ✗ Complex functions increased: 35 → 36

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

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.96907% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 88.41%. Comparing base (6ae4379) to head (1eae1e8).

Files with missing lines Patch % Lines
factory/ceo_completion.py 97.61% 0 Missing and 1 partial ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

… 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.
@osilkin98

Copy link
Copy Markdown
Collaborator

@mihirathale98 Looks like there are some conflicts with the main branch

@osilkin98

Copy link
Copy Markdown
Collaborator

@ceo-review

github-actions[bot]
github-actions Bot previously approved these changes Jul 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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_idread_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:

  1. Generates and persists session IDs before CEO spawns
  2. Threads session_id on first spawn and resume_session_id on respawns
  3. Captures returned session_id from events and persists to cycle state
  4. Cleans up both cycle and session state on completion/abort
  5. Provides a working factory resume command with proper fallback chain
  6. 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.
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.

2 participants