Skip to content

orchestrate integrate can strand the working tree (ref-only advance); add structural bare-repo refuse + post-land tree check #53

Description

@TBarregren

Summary

The orchestrate engine's integrate step can advance the default branch's ref without updating its working tree, silently stranding the landed code off-disk. When it encountered a repository in a bad state (core.bare=true / "no work tree"), instead of refusing and parking, it worked around the condition with git update-ref, leaving main pointing at the integration tip while the primary checkout stayed at the pre-run commit. The run's own integration hotfix (7b8a3c2) added a prompt-level guard forbidding this — but the guard is advisory instruction to a mechanical-tier agent, added after the damage, and there is still no structural self-check that the tree matches the ref after landing, and no repair of a repo already found corrupt.

Severity: HIGH (defense-in-depth over the root-cause test-env ticket). Every "landed/green" guarantee is moot if the landed code never reaches disk, and gates run in the stranded checkout test the old code.

Evidence (verified)

  • Implementer verdicts for both issues describe the fallback explicitly, e.g. orchestrate: continuous out-of-band progress reporting for multi-hour runs (status command + durable milestone markers) #48:

    "Fast-forwarded main from 411da14 to …'s tip via git update-ref (git merge --ff-only failed only because the root repo is bare/has no work tree) …"

  • Post-run ground truth: main/HEAD correctly at the integration tip 7b8a3c2, but git config core.baretrue, git status failed with "this operation must be run in a work tree", and scripts/orchestrate.py on disk lacked the landed symbols — i.e. the ref moved, the tree did not.
  • The guard added by the run's hotfix now lives in the integrate prompt (skills/orchestrate/orchestrate.workflow.js:922-925):

    "NEVER bypass that refusal — do NOT set core.bare, do NOT reconfigure the repository, and do NOT force-advance the default ref with git update-ref, git branch -f, or git push .. Each of those corrupts the repository and strands the default branch's working tree … If the default branch is NOT checked out in this worktree, do NOT reconfigure anything to force the landing — report a blocker instead."
    This is instruction text, not an enforced invariant, and it postdates the corruption it describes.

Root-cause analysis (cause → effect)

  1. Precondition (upstream). The repo was already core.bare=true when the first integrate ran — set by the test-fixture git-env leak (companion root-cause ticket). So git merge --ff-only (which needs a work tree) failed.
  2. Wrong recovery. Facing a failed ff-only, the mechanical-tier integrate agent improvised git update-ref refs/heads/main <sha> to "land" anyway. update-ref advances the branch pointer but touches neither the working tree nor the index.
  3. Effect. main advanced to the union; the working tree stayed at the pre-run commit. Subsequent gates/verification in the primary checkout silently tested stale code. The second issue's integrate compounded it on the same bare repo.
  4. Why the guard isn't enough. The hotfix guard tells the agent not to do this, but (a) it is prompt-level and a capable agent can still rationalize a workaround; (b) it offers no positive post-condition check that the land actually reached disk; (c) it does nothing to detect or repair a repo that is already corrupt when integrate starts.

Impact / blast radius

  • A corrupted-but-advanced default branch is the worst kind of failure: it looks landed (ref moved, report says "done") while the code is not on disk and issues risk being closed on a lie.
  • It defeats the mandatory final gate re-run if that re-run happens in the stranded checkout.
  • It recurs for every issue in the wave once the repo is bare, multiplying the damage.

Proposed solution ideas

  1. Structural pre-flight on integrate. Before landing, assert the repo is healthy: git rev-parse --is-bare-repository is false and the default branch is checked out in the current worktree. If not → park the issue with a loud blocker (exactly what the guard's prose now says, but enforced in code/return-schema, not left to agent discretion).
  2. Post-condition verification. After the ff-only, assert git rev-parse HEAD equals the feature tip and the working tree is clean at that tip (git status --porcelain empty, a sentinel landed file present on disk). If the tree does not match the ref, treat the land as failed and park — never report integrated: true on a ref-only advance.
  3. Never use ref-forcing verbs. The integrate/reconcile code paths should structurally avoid update-ref / branch -f / push . for landing; the only sanctioned land is git merge --ff-only from the default-branch worktree. If that is impossible, the state is abnormal → park.
  4. Self-heal / detect on start-up (optional). The engine (or a preflight) could detect core.bare=true on the working repo at run start and refuse to start, pointing at the repair steps — turning a silent corruption into an actionable early error.
  5. Regression coverage. In tests/test_orchestrate_workflow.py, add a structural test asserting the integrate prompt/return contract rejects a bare/no-worktree condition and that no landing path emits a ref-forcing command.

Acceptance criteria (sketch)

  • Integrate refuses to land (parks with a specific blocker) when the repo is bare or the default branch is not checked out in the integrating worktree — enforced, not merely instructed.
  • Integrate verifies, after --ff-only, that the working tree actually advanced to the feature tip; a ref-only advance is reported as a failed land, never integrated: true.
  • No engine code path (integrate or reconcile) can land via git update-ref / git branch -f / git push ..
  • A test locks in the refuse-and-park behavior for the bare/no-worktree case.
  • (Optional) The run aborts early with a clear repair message if it starts against a core.bare=true working repo.

Provenance

Discovered by the orchestrate run wf_2d1a01f8-b49 (#48 + #50, merge mode). The run's own integration hotfix 7b8a3c2 added the prompt-level guard; this ticket asks for the structural enforcement + self-check + repair the guard does not provide. Upstream trigger tracked in the test-env root-cause ticket.

Blocked by

Blocked by #52 (land the git-environment scrub first: until it lands, every hook-verified commit made while implementing this issue risks corrupting the real repository through the leaked GIT_DIR/GIT_INDEX_FILE).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions