Problem
afx workspace recover --apply respawns builders whose shellper session died (e.g. after a machine reboot). Every respawned builder ends up attributed to whatever architect terminal the operator ran afx workspace recover from — typically main — regardless of which architect originally spawned the builder. Sibling-architect workspaces lose their per-builder attribution on the first post-reboot recovery pass.
Downstream symptoms:
afx send architect from the respawned builder routes to the wrong architect (main instead of e.g. vscode)
architect:<sibling> addressing from that builder is rejected by the anti-spoofing check (tower-messages.ts:213-218) because spawned_by_architect now says main
- Dashboard + VS Code Agents-view attribution shows the wrong owner
- Sibling architects lose their per-team builder list after any recovery event
Root cause
packages/codev/src/agent-farm/commands/workspace-recover.ts has zero mentions of spawned_by_architect / spawnedByArchitect. The recovery flow never reads or preserves per-builder architect attribution.
Concrete chain:
BuilderInfo interface carries { builderId, issueArg, cliProtocol } — no architect field.
deriveBuilderInfo builds it from ProjectState (porch status.yaml) alone; the builder's global.db.builders.spawned_by_architect value is never consulted.
respawnBuilder runs child_process.spawn(process.execPath, [afx-entry, 'spawn', info.issueArg, '--resume', '--protocol', info.cliProtocol], { stdio: 'inherit' }) — no env override.
- The child
afx spawn process inherits CODEV_ARCHITECT_NAME from the shell that invoked workspace recover. That value is then read by spawn.ts's module-scope SPAWNING_ARCHITECT_NAME constant (process.env.CODEV_ARCHITECT_NAME || DEFAULT_ARCHITECT_NAME) and written into the new builder's spawned_by_architect DB column.
Result: all builders reattributed to whichever architect owned the recovery shell (or main if the shell wasn't inside an architect terminal at all).
Fix sketch
Three surgical changes:
-
Extend BuilderInfo with spawnedByArchitect: string | null. Nullable to accommodate legacy rows / pre-Spec-755 builders that predate the field.
-
Populate it in deriveBuilderInfo (or a wrapper) by reading spawned_by_architect from global.db.builders WHERE workspace_path = ? AND id = ? for the derived builderId. Use the existing read-only DB access pattern from overview.ts's enrichment logic (single query per project is fine — workspace recover isn't hot). Fall back to null if not found.
-
respawnBuilder sets the child's env so the spawn CLI reads the correct name:
spawn(process.execPath, [...], {
stdio: 'inherit',
env: {
...process.env,
CODEV_ARCHITECT_NAME: info.spawnedByArchitect ?? process.env.CODEV_ARCHITECT_NAME ?? 'main',
},
});
Preserving the caller's env for the null-fallback case keeps legacy-row recoveries at least self-consistent (they get the shell's architect, matching today's default).
Scope
packages/codev/src/agent-farm/commands/workspace-recover.ts — interface, derive, respawn
- Unit test coverage: extend the existing recover tests (if any) to cover multi-architect respawn preserves per-builder attribution.
Out of scope: any change to afx spawn itself, any change to the DB schema, per-workspace / cross-workspace recovery semantics.
Related
- Sibling attribution bug: VS Code Backlog
Reference issue in architect action ignores the QuickPick selection (always sends to main). Different code path, same "architects losing builder attribution" theme in the current release cycle.
- The
spawned_by_architect column read here is the same field enriched by overview.ts for the dashboard and VS Code Agents view, and read by tower-messages.ts for anti-spoofing on architect:<name> addressing.
Problem
afx workspace recover --applyrespawns builders whose shellper session died (e.g. after a machine reboot). Every respawned builder ends up attributed to whatever architect terminal the operator ranafx workspace recoverfrom — typicallymain— regardless of which architect originally spawned the builder. Sibling-architect workspaces lose their per-builder attribution on the first post-reboot recovery pass.Downstream symptoms:
afx send architectfrom the respawned builder routes to the wrong architect (main instead of e.g. vscode)architect:<sibling>addressing from that builder is rejected by the anti-spoofing check (tower-messages.ts:213-218) becausespawned_by_architectnow saysmainRoot cause
packages/codev/src/agent-farm/commands/workspace-recover.tshas zero mentions ofspawned_by_architect/spawnedByArchitect. The recovery flow never reads or preserves per-builder architect attribution.Concrete chain:
BuilderInfointerface carries{ builderId, issueArg, cliProtocol }— no architect field.deriveBuilderInfobuilds it fromProjectState(porch status.yaml) alone; the builder'sglobal.db.builders.spawned_by_architectvalue is never consulted.respawnBuilderrunschild_process.spawn(process.execPath, [afx-entry, 'spawn', info.issueArg, '--resume', '--protocol', info.cliProtocol], { stdio: 'inherit' })— noenvoverride.afx spawnprocess inheritsCODEV_ARCHITECT_NAMEfrom the shell that invokedworkspace recover. That value is then read byspawn.ts's module-scopeSPAWNING_ARCHITECT_NAMEconstant (process.env.CODEV_ARCHITECT_NAME || DEFAULT_ARCHITECT_NAME) and written into the new builder'sspawned_by_architectDB column.Result: all builders reattributed to whichever architect owned the recovery shell (or
mainif the shell wasn't inside an architect terminal at all).Fix sketch
Three surgical changes:
Extend
BuilderInfowithspawnedByArchitect: string | null. Nullable to accommodate legacy rows / pre-Spec-755 builders that predate the field.Populate it in
deriveBuilderInfo(or a wrapper) by readingspawned_by_architectfromglobal.db.builders WHERE workspace_path = ? AND id = ?for the derivedbuilderId. Use the existing read-only DB access pattern fromoverview.ts's enrichment logic (single query per project is fine —workspace recoverisn't hot). Fall back to null if not found.respawnBuildersets the child's env so the spawn CLI reads the correct name:Preserving the caller's env for the null-fallback case keeps legacy-row recoveries at least self-consistent (they get the shell's architect, matching today's default).
Scope
packages/codev/src/agent-farm/commands/workspace-recover.ts— interface, derive, respawnOut of scope: any change to
afx spawnitself, any change to the DB schema, per-workspace / cross-workspace recovery semantics.Related
Reference issue in architectaction ignores the QuickPick selection (always sends to main). Different code path, same "architects losing builder attribution" theme in the current release cycle.spawned_by_architectcolumn read here is the same field enriched byoverview.tsfor the dashboard and VS Code Agents view, and read bytower-messages.tsfor anti-spoofing onarchitect:<name>addressing.