Skip to content

Orchestrator: workers must never mutate the MAIN checkout's git state — guard hook + dirty-main census signal #106

Description

@robercano-ghbot

Incident

After the issue #102 advance (PR #103, 2026-07-09), main's checkout was left dirty: the PR's harness changes were staged in the MAIN checkout's index (git status showed A/MM/AD entries for loop-daemon.sh, loop-event.sh, skills/setup files, templates), while the working tree was mostly back at HEAD. Reconstruction: the implementer at some point ran git add (and likely a shared-branch checkout) in the main checkout instead of its worktree — the run's own execution note mentions the docs phase "had to remove the stale harness worktree to check out the shared branch". The staged content was fully contained in PR #103, so recovery was git reset + restore, but this is the second time main has been found dirty after loop activity.

Why it matters

  • merge-ready.sh's local_sync only fast-forwards a clean main — a dirty checkout silently stops local syncing, so the owner's terminal drifts behind merged reality (exactly the "main is not clean, this keeps happening" experience).
  • A dirty index in main risks a later owner commit accidentally including leaked worker content.
  • Module boundaries are enforced for FILE edits, but nothing stops a worker from mutating the main checkout's GIT state (index, HEAD, branch).

Proposal

  1. Guard hook (mechanism, not discipline): extend guard-git-add.py (or add a sibling PreToolUse hook) to block git state mutations — add, rm, mv, restore --staged, reset, checkout <branch>, switch — when the command's repo toplevel is the MAIN checkout rather than a .claude/worktrees/* path, for worker sessions. Worker detection is the design question: e.g. the orchestrator/implementer agent defs export a marker env var the hook checks, defaulting to allow for owner sessions. Same best-effort spirit as the existing hook (keyed off sandbox/settings, never blocks non-hardened repos).
  2. Prompt reinforcement: implementer + orchestrator agent definitions get an explicit hard rule: ALL git operations happen inside the worker's own worktree; a worker that needs a shared branch checks it out IN ITS WORKTREE (git worktree add semantics already guarantee exclusivity — surface the "branch already checked out elsewhere" failure as a re-scope signal to the orchestrator, not a license to use the main checkout).
  3. Census hygiene line (visibility): loop-census.sh emits a main_dirty=yes|no (porcelain non-empty, excluding known sandbox mask paths) line so every tick surfaces contamination instead of it being discovered by the owner days later.

Acceptance criteria

  • A worker session attempting git add/checkout/switch/reset with toplevel == main checkout is blocked by the hook with a message pointing it at its worktree; owner sessions are unaffected.
  • Implementer/orchestrator defs state the worktree-only git rule and the re-scope path for shared-branch work.
  • loop-census.sh reports main_dirty= each tick, excluding sandbox mask phantom paths (see docs/HARDENING.md → Caveats).
  • Tests: guard hook unit cases (worker-in-main blocked, worker-in-worktree allowed, owner-in-main allowed); census line covered in loop-census.test.sh.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmodule:harnessOrchestrator machinery under .claudeplannedOwner-approved for the autonomous loop (assigned ONLY by the owner)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions