fix-candidate-wg-control-plane-destruction

Protect live .wg control plane from candidate promotion

Metadata

Statusdone
Agent identity02e879681e52e0a384106169be043416c4d946e850ab26b2269c57681b52a6e7
Created2026-08-01T08:02:49.686316922+00:00
Started2026-08-01T12:27:03.851215324+00:00
Completed2026-08-03T09:42:51.240485147+00:00
Tokens24170318 in / 50498 out

Description

CRITICAL DATA-LOSS BUG observed on erikg@lambda01:/home/erikg/phind on 2026-07-31 22:32 UTC. A worker worktree (agent-226, task build-partition-stitching) created a .wg symlink pointing to /home/erikg/phind/.wg so worker commands could reach the graph. Commit 9f9a440 accidentally added that symlink (create mode 120000 .wg). Task-owned finalization then promoted rescue/main commit 2f991af; the root working tree transition replaced/stripped the live .wg control plane. graph.jsonl, config.toml, historical chat/session directories, messages, logs, and most control state vanished, while runtime-created agents/attempts/chat/finalization/service subdirectories were recreated. The live TUI continued showing its cached graph; every graph mutation failed with Failed to save graph. Evidence is preserved remotely under /home/erikg/phind/.wg/recovery/incident-20260801; daemon stopped, orphan observers SIGSTOP-frozen, live TUI PID 1210893 preserved.

Implement an unskippable control-plane boundary:

  • .wg (file, dir, symlink, gitlink, path prefix, case/normalization variants) must never be included in worker candidate/rescue manifests, staged commits, validation materialization, merge projection, or promotion. Reject loudly before candidate sealing if worker changes/adds/deletes it.
  • Worktree setup may expose graph access only through explicitly out-of-band runtime plumbing; its helper symlink must be untrackable/uncommittable and never projected to source.
  • Protected-ref promotion must update only exact allowed candidate paths and must never use checkout/reset/tree application semantics that can replace the live project .wg. This must hold even if .wg is already tracked in candidate/base history.
  • Before any root worktree/ref operation, assert canonical .wg identity/type and preserve a durable external snapshot/receipt of graph+chat registry sufficient for rollback. Fail closed on self-referential or changed control path.
  • Detect and stop a project whose Git index/tree contains .wg, with a recovery command that removes the tracked entry without touching live control data.
  • Add diagnostics that retain full I/O cause (Failed to save graph: ENOENT path=...) rather than stripping the chain.

Validation

  • Reproduce agent worktree adding a self-referential .wg symlink, commit it, submit Land: candidate sealing/promotion must refuse; root graph/config/chat bytes unchanged.
  • Test .wg directory/file/symlink/gitlink deletion/replacement, nested .wg/**, normalization variants, and base history already tracking .wg.
  • Crash/restart around candidate checkpoint/ref CAS/root projection cannot alter control-plane bytes.
  • Verify ordinary source changes still land and worker graph access works out of band.
  • Add candidate-binary smoke using two isolated graphs and exact byte digests for graph/config/chat sessions before/after.
  • cargo fmt --check, focused finalization/worktree/spawn/chat tests, cargo check, cargo clippy.

Depends on

Required by

Log