Skip to content

Latest commit

 

History

History
65 lines (38 loc) · 6.27 KB

File metadata and controls

65 lines (38 loc) · 6.27 KB

v3.1.2 Jacobean

Released: 2026-05-23

Critical patch on the Jacobean line. Fixes the cross-workspace sibling-architect leak introduced by v3.1.1's #786 lifecycle work — a release-blocker that surfaced within minutes of v3.1.1 going live (the architect's own machine experienced it during post-release verify of a new workspace).

Sibling architects no longer leak across workspaces (#826)

In v3.1.1, opening a workspace OTHER than the one that registered sibling architects caused those siblings to be re-spawned as fresh PTYs in the new workspace. Concrete symptom: shannon had ob-refine registered; opening manazil for the first time showed main + ob-refine + bug-backlog in manazil's terminal list, with new PIDs and real running claude processes none of which belonged to manazil.

Root cause

state.db.architect had no workspace_path column. The table was global per Tower-daemon (anchored to Tower's startup CWD). v3.1.1's #786 added a launchInstance reconcile loop that iterated this global table and re-spawned every architect it found — without any workspace-scoping check, because the data model didn't carry the scope. Pre-v3.1.1, the table was harmless because nothing iterated it; v3.1.1's new iterate-and-respawn loop turned the global table into an active cross-workspace leak.

The fix: schema-level workspace scoping

Migration v11 (in state.db):

  • Add workspace_path TEXT NOT NULL column
  • Composite primary key (workspace_path, id)
  • Backfill workspace_path from global.db.terminal_sessions via ATTACH DATABASE, using architect.terminal_id as the disambiguation key (deterministic — the stable session UUID), with role_id LIMIT 1 as the fallback only when terminal_id has no matching session row
  • Drop orphans (architects with no current terminal_session row)
  • New index idx_architect_workspace on architect(workspace_path)

All state.ts accessors now require workspace context: getArchitects(workspacePath), setArchitect(workspacePath, …), setArchitectByName(workspacePath, name, …), removeArchitect(workspacePath, name), loadState(workspacePath). launchInstance and other callers thread workspacePath through.

All accessors canonicalize the passed workspacePath via realpathSync at the boundary — so symlinked workspace paths resolve to the same canonical key and the same architect row, regardless of which form the caller used.

Why per-site patching was rejected

The first three iterations of #827 tried to patch individual delete sites (deleteWorkspaceTerminalSessions, PtySession exit handlers, getTerminalsForWorkspace stale-cleanup) with isIntentionallyStopping guards to prevent the leaked rows from being preserved-then-deleted. Each iteration's CMAP caught the next delete site. After four sites and no clear stopping point, the architect-side review escalated to "the architecture is wrong, not the call sites" and the PR rewrote to Option A (schema-level scoping). The result is dramatically simpler — workspace isolation now falls out of the data model rather than depending on a maze of intent-flag guards.

Spec 786 stop+start MUST is preserved

The intentional-stop preservation mechanism from #786 stays — sibling architect rows still survive afx workspace stop + afx workspace start. With Option A, that preservation is now narrowly scoped to ONE concern (skip the architect-row delete on graceful stop), not "protect every site that could leak architects."

Migration ordering hotfix (#834, iter-7)

A late-cycle finding: an initial version of the migration placed CREATE INDEX idx_architect_workspace ON architect(workspace_path) in LOCAL_SCHEMA, which runs on every db.exec(LOCAL_SCHEMA) BEFORE migrations. On existing v10 databases, the architect table has no workspace_path column yet, so the index creation threw and the entire ensureLocalDatabase initialization aborted — breaking every upgrade install. Caught by the architect's local-install verification before publishing. Fixed by moving the CREATE INDEX out of LOCAL_SCHEMA and into the migration block itself (placed outside the alreadyMigrated guard so both fresh-install and upgrade-install paths land it). PR #834 ships alongside PR #827 in this release.

Process notes

This bug fix went through 7 iterations of CMAP-driven review across two PRs (#827 + #834). Each iteration's independent architect-side CMAP caught a finding the builder's own CMAP missed: the original race in the cross-workspace fix, then a stop+start regression, then exit-handler delete leakage, then getTerminalsForWorkspace stale-cleanup, then migration disambiguation non-determinism, then path-canonicalization mismatch, then the migration-ordering bug that broke upgrade installs. The two-CMAP discipline (builder runs one, architect runs an independent one) caught real bugs at every iteration — single-CMAP review on this PR would have shipped a regression.

Lesson saved in the architect's working memory: when builder's CMAP and architect's CMAP converge differently, the architect's CMAP isn't sloppiness — it's signal. Run both, trust the conjunction.

Breaking changes

None at the user-visible API level. The migration is automatic on first state.db read after upgrade.

For TypeScript consumers of @cluesmith/codev-core or internal state accessors: getArchitects, setArchitect, setArchitectByName, removeArchitect, loadState now require a workspacePath parameter. This is internal API; CLI / dashboard / VSCode extension consumers are unaffected.

Install

npm install -g @cluesmith/codev@3.1.2
afx tower stop && afx tower start

If you were running v3.1.1 with multiple workspaces, this release silently fixes the leak on Tower restart (migration v11 runs automatically). Confirm by opening a previously-unused workspace and verifying its architect list shows only the workspace's own architects.

Contributors

  • M Waleed Kadous (@waleedkadous) — reported the leak within minutes of v3.1.1 install (own machine), drove the 7-iteration CMAP discipline that caught every issue including the migration-ordering release blocker.
  • External Codev consumers — implicit pressure on multi-workspace correctness; the bug would have surfaced on every two-workspace adopter eventually.
  • Builder working under BUGFIX protocol across PRs #827 and #834.