You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#832 introduced a persisted, agent-neutral conversation-session mechanism for architects: a session_id column on the architect table, written at spawn (Tower generates a UUID and passes it via the new HarnessProvider.session.newSessionArgs), and read back at every revive surface to --resume. It was needed because named sibling architects (Spec 755) sharecwd = workspacePath, which breaks the newest-jsonl-by-mtime heuristic.
Builders still resume via that jsonl-discovery heuristic (findLatestSessionId in spawn.ts, shipped in #831/#829). It works for builders because each owns a unique worktree cwd (.builders/<id>/), so "newest jsonl" is unambiguous. So this is not a bug — builders resume correctly today.
Why unify anyway
Now that the robust mechanism exists, builders are the lone holdout on a weaker one:
Single mechanism over two. One persisted-id path for all agent terminals beats maintaining jsonl-discovery for builders + stored-id for architects (consolidate duplicates / single source of truth).
More robust. A stored id is immune to the heuristic's edge cases — a stray manual claude session run in the worktree, mtime races, or --resume picking an unintended lineage.
Add a persisted session_id for builders (migration on the builders table, or reuse terminal_sessions — open question, see below).
At builder spawn (spawn.ts), generate a UUID, pass it via harness.session?.newSessionArgs, and store it.
On resume (afx spawn --resume, afx workspace recover), read the stored id and --resume via harness.session?.resumeArgs.
Fallback chain: stored id -> jsonl-discovery (kept as a legacy fallback) -> fresh. Unlike architects, builders need no transitional capture command — jsonl-discovery already resolves legacy builders correctly (unique cwd), so it's a clean, self-healing fallback with zero migration ceremony.
Open questions
Storage location:builders.session_id (parallels architect.session_id) vs a shared terminal_sessions.session_id (one column covering every terminal type). The latter is more "single source of truth" but a larger change to the global-db path.
Whether to eventually retire jsonl-discovery entirely (once all builders carry stored ids) or keep it permanently as the legacy fallback.
Acceptance criteria
A builder spawned after this lands stores its session id and resumes from it on --resume / recover, independent of jsonl mtime.
Legacy builders (no stored id) still resume via the jsonl-discovery fallback — no regression, no capture step.
Non-Claude builder harnesses spawn fresh (no claude flags leak), consistent with architects.
Context
#832 introduced a persisted, agent-neutral conversation-session mechanism for architects: a
session_idcolumn on thearchitecttable, written at spawn (Tower generates a UUID and passes it via the newHarnessProvider.session.newSessionArgs), and read back at every revive surface to--resume. It was needed because named sibling architects (Spec 755) sharecwd = workspacePath, which breaks the newest-jsonl-by-mtime heuristic.Builders still resume via that jsonl-discovery heuristic (
findLatestSessionIdinspawn.ts, shipped in #831/#829). It works for builders because each owns a unique worktree cwd (.builders/<id>/), so "newest jsonl" is unambiguous. So this is not a bug — builders resume correctly today.Why unify anyway
Now that the robust mechanism exists, builders are the lone holdout on a weaker one:
claudesession run in the worktree, mtime races, or--resumepicking an unintended lineage.HarnessProvider.sessioncapability (newSessionArgs/resumeArgs) added in Multi-architect conversation resume: disambiguate via per-architect session UUID #832 drops straight into the builder spawn path; Codex/Gemini/OpenCode builders degrade gracefully (nosessioncapability -> fresh spawn), same as architects.Proposed approach
session_idfor builders (migration on thebuilderstable, or reuseterminal_sessions— open question, see below).spawn.ts), generate a UUID, pass it viaharness.session?.newSessionArgs, and store it.afx spawn --resume,afx workspace recover), read the stored id and--resumeviaharness.session?.resumeArgs.Open questions
builders.session_id(parallelsarchitect.session_id) vs a sharedterminal_sessions.session_id(one column covering every terminal type). The latter is more "single source of truth" but a larger change to the global-db path.Acceptance criteria
--resume/ recover, independent of jsonl mtime.Out of scope
References
HarnessProvider.session(the mechanism this generalizes).