Repository navigation
workplan-project: linking a sub-issue can silently steal it from another tracker #72
Description
From the QEP-6 field test (QuantEcon/qeps#19, finding 1 — the strongest single finding in it).
The hazard
Sub-issue membership is single-parent. Claiming an item that already has a parent detaches it from that parent, and in the standard tooling this is silent and non-optional: both gh issue edit P --add-sub-issue C and gh issue edit C --parent P pass replace_parent=true unconditionally, with no opt-out flag, while the REST endpoint exposes it as optional. Adding an item and stealing it from another tracker are the same gesture, indistinguishable at the call site.
What it cost in the field: ten items claimed from QuantEcon/project-qep2#1 — the org's QEP-2 rollout tracker — were detached by the act of linking them elsewhere. meta#358 was left holding only the one item that had been closed, and therefore reported {total: 1, completed: 1, percent_completed: 100}. A programme whose pilot had not started displayed as complete, in a repository nobody was looking at, with no error and no warning.
Why it reaches this skill
Most of what workplan-project links is newly created and parentless, so the common path is safe. The re-run path is not:
Re-runs are safe if you look first: before each create,
gh issue list --repo <o>/<r> --search "<title> in:title"— file only what is missing, and edit rather than duplicate.
Finding an existing issue and linking it is exactly the claiming gesture. On a repo whose issues are already organised — which is most repos worth auditing — that issue plausibly has a parent already.
The same applies wherever a report's work item is an existing issue rather than a new one. The step-2 mapping table sends MERGE and NEEDS MAINTAINER DECISION verdicts into the package as work items; where the work item is landing PR #n or deciding issue #m, the natural implementation links the existing thing.
A second, quieter problem: the two link methods disagree
The skill prescribes the raw REST call:
gh api repos/<o>/<r>/issues/<parent-number>/sub_issues -F sub_issue_id="$id"which omits replace_parent, so it defaults to false and the call fails on an already-parented child. The native gh verb in the same situation succeeds and steals. Two ways to link, two different behaviours on the one case that matters, and the skill documents neither. (#70 proposes moving to the native verb for other reasons, which makes the stealing behaviour the default rather than the exception — so that change and this guard should land together.)
Proposed change
A read-before-write guard wherever the skill links an issue it did not just create:
parent=$(gh issue view <child> --repo <o>/<r> --json parent --jq '.parent.number // empty')- Empty → link normally.
- Set → stop. Report the existing parent and what claiming it would do. The drafts are already presented for approval before step 5, so this belongs in the draft's method note, not as a runtime prompt: "item #N is currently a work item of #P; adding it here removes it from #P."
- On approval, either re-link the displaced parent to the new tracker, or record the detachment in the method note so the change is visible to whoever owns the other tracker.
The non-destructive path costs exactly one extra read per child.
This is amendment 3 to QEP-6, proposed as an obligation on conform tooling; this skill is that tooling for the create path.
Acceptance
- Every link of a pre-existing issue reads its current parent first
- An already-parented item surfaces in the approval draft, naming the parent it would leave
- The skill uses one link method with one documented behaviour, not two that differ
- Detachments that go ahead are recorded in the method note
Activity
qe 0.10.0: workplan-project conforms to QEP-6 — the six field-test fi…