No sanctioned first-mutation path: create_issue requires a branches/ worktree the issue-first gate forbids creating #749

Closed
opened 2026-07-18 13:03:04 -05:00 by jcwalker3 · 1 comment
Owner

Summary

The first author mutation of any task — creating the tracking issue — has no sanctioned path from a clean control checkout. Two rules that are each individually correct contradict each other at exactly that moment:

  • The issue-first workflow gate forbids worktree creation, branch creation, issue lock, and any file modification before the tracking issue exists.
  • The #274 branches-only mutation guard (threaded into gitea_create_issue by #450) rejects any author mutation whose workspace is the stable control checkout, and demands a session-owned worktree under branches/.

So creating the issue requires a branches/ worktree, and creating a branches/ worktree requires the issue. Neither rule can be satisfied first.

Observed

From a clean control checkout on master, with a valid prgs-author profile and create_issue capability resolved:

gitea_create_issue(...)
→ success: false
  performed: false
  blocker_kind: "missing_issue_worktree"
  reasons: ["Branches-only mutation guard (#274): author mutation blocked:
     workspace is the stable control checkout; create or switch to a
     session-owned worktree under branches/. project root:
     .../Gitea-Tools; workspace: .../Gitea-Tools"]
  exact_next_action: "Bind an issue-backed worktree under branches/
     (scripts/worktree-start or git worktree add branches/issue-<N>-*) ..."

The prescribed exact_next_action is itself unsatisfiable at this phase: it names branches/issue-<N>-*, but <N> is precisely the issue number that does not yet exist.

Why this matters

The deadlock is not theoretical, and the escapes observed so far are all unsatisfactory:

  1. Violate the issue-first gate — create a worktree pre-issue. Defeats the gate's purpose and produces worktrees with no tracking issue.
  2. mkdir a dummy worktree — this is what happened during the #712 incident, recorded verbatim in #713: an operator ran mkdir -p branches/issue-creation-dummy and retried. That directory is not a registered Git worktree, which is the exact hole #713 AC1 proposes to close.
  3. Borrow an unrelated pre-existing worktree — used during the creation of #747, by passing worktree_path pointing at a clean, unrelated scratch worktree (branches/chore-tmp-create-issue-pytest-runner, left untouched with no commits). It satisfies the guard only by accident, since that worktree has no connection to the work being filed. This issue itself was filed the same way, because no other path exists.

Every current escape either weakens a guard or launders an unrelated binding through it. That is worth fixing deliberately rather than leaving each session to improvise.

Sequencing with #713 (important)

#713 asks to make this guard stricter: require proof of a registered Git worktree (git worktree list membership) rather than mere directory existence. That is correct and it closes escape (2) above. But #713 as written makes this deadlock strictly worse, because it removes the improvised hatch without supplying a legitimate first-mutation path.

Any fix here must therefore satisfy #713 AC1 rather than route around it. A bootstrap exception implemented as "skip the guard when no worktree exists" would directly reintroduce what #713 is trying to eliminate.

Proposed direction (not prescriptive)

Options worth weighing, in rough order of preference:

  • Phase-scoped exemption. Recognize create_issue as a pre-issue phase where no issue-backed worktree can exist by definition, and permit it from the control checkout provided the checkout is clean and the operation creates no local mutation. Issue creation writes nothing to the working tree — it is a pure remote mutation — so the branches-only guard is arguably protecting against a hazard this call does not present.
  • Sanctioned bootstrap worktree. A single registered, reusable worktree (e.g. branches/author-bootstrap) explicitly recognized for pre-issue mutations, created and registered by tooling rather than improvised per session. Satisfies #713 because it is a genuine registered worktree.
  • Two-phase issue creation. Allow issue creation to return the number first, then require the worktree before any subsequent mutation (claim, lock, commit). This preserves the guard everywhere it protects local state.

Acceptance criteria

  • AC1 — There is exactly one documented, sanctioned way to create a tracking issue from a clean control checkout, requiring no improvised directory, no unrelated borrowed worktree, and no violation of the issue-first gate.
  • AC2 — That path satisfies #713's registered-worktree requirement, or is explicitly and narrowly scoped as a pre-issue phase exemption with a stated rationale for why the guard's hazard does not apply.
  • AC3 — The guard continues to fail closed for every author mutation that touches local state (commit, push, lock, file modification) from the control checkout. The exemption, if any, does not generalize.
  • AC4 — When the guard does block, exact_next_action is satisfiable at the phase it is emitted in. It must not instruct the caller to create branches/issue-<N>-* at a point where <N> cannot be known.
  • AC5 — Regression tests cover: issue creation from a clean control checkout succeeds via the sanctioned path; issue creation from a dirty control checkout still fails closed; and a post-issue local mutation from the control checkout still fails closed.
  • AC6 — The workflow documentation describing the issue-first gate states the sanctioned first-mutation path, so sessions stop improvising one.

References

  • #713 — must sequence with this; records the same incident, proposes the opposite (stricter) remedy.
  • #711 — sibling defect of the same class: gitea_close_issue also has no sanctioned path under #274. A shared solution is plausible.
  • #274 — source of the branches-only guard. Its requirement 5 ("if a branches/ worktree does not exist for the task, create one before doing work") shows the issue-first ordering was not anticipated.
  • #450 — threaded worktree_path through gitea_create_issue and introduced the fail-closed behaviour now deadlocking.
  • #275 — prior art for resolving an impossible-ordering gate, though scoped to the post-issue claim/lock phase.
  • #618 — adjacent guard-consistency surface (create_issue and create_issue_comment enforce the binding differently).
  • #557 — precedent for scoping a narrow bootstrap exception without weakening general gates.

Filed while working #747; the deadlock was hit and worked around during that task's issue creation.

## Summary The first author mutation of any task — creating the tracking issue — has no sanctioned path from a clean control checkout. Two rules that are each individually correct contradict each other at exactly that moment: - The **issue-first workflow gate** forbids worktree creation, branch creation, issue lock, and any file modification *before* the tracking issue exists. - The **#274 branches-only mutation guard** (threaded into `gitea_create_issue` by #450) rejects any author mutation whose workspace is the stable control checkout, and demands a session-owned worktree under `branches/`. So creating the issue requires a `branches/` worktree, and creating a `branches/` worktree requires the issue. Neither rule can be satisfied first. ## Observed From a clean control checkout on master, with a valid `prgs-author` profile and `create_issue` capability resolved: ``` gitea_create_issue(...) → success: false performed: false blocker_kind: "missing_issue_worktree" reasons: ["Branches-only mutation guard (#274): author mutation blocked: workspace is the stable control checkout; create or switch to a session-owned worktree under branches/. project root: .../Gitea-Tools; workspace: .../Gitea-Tools"] exact_next_action: "Bind an issue-backed worktree under branches/ (scripts/worktree-start or git worktree add branches/issue-<N>-*) ..." ``` The prescribed `exact_next_action` is itself unsatisfiable at this phase: it names `branches/issue-<N>-*`, but `<N>` is precisely the issue number that does not yet exist. ## Why this matters The deadlock is not theoretical, and the escapes observed so far are all unsatisfactory: 1. **Violate the issue-first gate** — create a worktree pre-issue. Defeats the gate's purpose and produces worktrees with no tracking issue. 2. **`mkdir` a dummy worktree** — this is what happened during the #712 incident, recorded verbatim in #713: an operator ran `mkdir -p branches/issue-creation-dummy` and retried. That directory is not a registered Git worktree, which is the exact hole #713 AC1 proposes to close. 3. **Borrow an unrelated pre-existing worktree** — used during the creation of #747, by passing `worktree_path` pointing at a clean, unrelated scratch worktree (`branches/chore-tmp-create-issue-pytest-runner`, left untouched with no commits). It satisfies the guard only by accident, since that worktree has no connection to the work being filed. This issue itself was filed the same way, because no other path exists. Every current escape either weakens a guard or launders an unrelated binding through it. That is worth fixing deliberately rather than leaving each session to improvise. ## Sequencing with #713 (important) #713 asks to make this guard **stricter**: require proof of a *registered* Git worktree (`git worktree list` membership) rather than mere directory existence. That is correct and it closes escape (2) above. But #713 as written makes this deadlock strictly worse, because it removes the improvised hatch without supplying a legitimate first-mutation path. Any fix here must therefore satisfy #713 AC1 rather than route around it. A bootstrap exception implemented as "skip the guard when no worktree exists" would directly reintroduce what #713 is trying to eliminate. ## Proposed direction (not prescriptive) Options worth weighing, in rough order of preference: - **Phase-scoped exemption.** Recognize `create_issue` as a pre-issue phase where no issue-backed worktree can exist by definition, and permit it from the control checkout *provided* the checkout is clean and the operation creates no local mutation. Issue creation writes nothing to the working tree — it is a pure remote mutation — so the branches-only guard is arguably protecting against a hazard this call does not present. - **Sanctioned bootstrap worktree.** A single registered, reusable worktree (e.g. `branches/author-bootstrap`) explicitly recognized for pre-issue mutations, created and registered by tooling rather than improvised per session. Satisfies #713 because it is a genuine registered worktree. - **Two-phase issue creation.** Allow issue creation to return the number first, then require the worktree before any *subsequent* mutation (claim, lock, commit). This preserves the guard everywhere it protects local state. ## Acceptance criteria - **AC1** — There is exactly one documented, sanctioned way to create a tracking issue from a clean control checkout, requiring no improvised directory, no unrelated borrowed worktree, and no violation of the issue-first gate. - **AC2** — That path satisfies #713's registered-worktree requirement, or is explicitly and narrowly scoped as a pre-issue phase exemption with a stated rationale for why the guard's hazard does not apply. - **AC3** — The guard continues to fail closed for every author mutation that touches local state (commit, push, lock, file modification) from the control checkout. The exemption, if any, does not generalize. - **AC4** — When the guard does block, `exact_next_action` is satisfiable at the phase it is emitted in. It must not instruct the caller to create `branches/issue-<N>-*` at a point where `<N>` cannot be known. - **AC5** — Regression tests cover: issue creation from a clean control checkout succeeds via the sanctioned path; issue creation from a *dirty* control checkout still fails closed; and a post-issue local mutation from the control checkout still fails closed. - **AC6** — The workflow documentation describing the issue-first gate states the sanctioned first-mutation path, so sessions stop improvising one. ## References - **#713** — must sequence with this; records the same incident, proposes the opposite (stricter) remedy. - **#711** — sibling defect of the same class: `gitea_close_issue` also has no sanctioned path under #274. A shared solution is plausible. - **#274** — source of the branches-only guard. Its requirement 5 ("if a `branches/` worktree does not exist for the task, create one before doing work") shows the issue-first ordering was not anticipated. - **#450** — threaded `worktree_path` through `gitea_create_issue` and introduced the fail-closed behaviour now deadlocking. - **#275** — prior art for resolving an impossible-ordering gate, though scoped to the post-issue claim/lock phase. - **#618** — adjacent guard-consistency surface (`create_issue` and `create_issue_comment` enforce the binding differently). - **#557** — precedent for scoping a narrow bootstrap exception without weakening general gates. Filed while working #747; the deadlock was hit and worked around during that task's issue creation.
jcwalker3 added the status:readyworkflow-hardeningpreflightsafetytype:bug labels 2026-07-18 13:03:05 -05:00
jcwalker3 added status:in-progress and removed status:ready labels 2026-07-18 15:13:18 -05:00
Author
Owner

Issue claim heartbeat

<!-- gitea-issue-claim-heartbeat:v1 --> **Issue claim heartbeat** - kind: claim - issue: #749 - branch: fix/issue-749-create-issue-bootstrap - phase: claimed - profile: prgs-author - pr: none - blocker: none - next_action: create worktree and begin implementation
jcwalker3 added status:pr-open and removed status:in-progress labels 2026-07-18 15:19:39 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#749