fix(workflows): target activation labels via gh-aw temporary IDs - #1965
Conversation
`/squad plan activate` told the model to read a real issue number back from
each `create-issue` call and then target `add_labels` at it. gh-aw never
returns one — issue creation is deferred to the post-agent safe-output job,
so the agent only ever sees a success acknowledgement. Every operation that
depended on a "returned" number was therefore unsatisfiable, and an
`add_labels` call with no resolvable `item_number` does not error: it
silently labels the triggering intent issue.
Switch the full activation path to gh-aw's supported `temporary_id` linkage:
- Enable `require-temporary-id: true` on the `create-issue` safe output, so
a call that omits a temporary ID is rejected rather than silently
mis-targeted.
- Add a Temporary-ID Contract section covering the real gh-aw pattern
(`^#?aw_[A-Za-z0-9_]{3,12}$`), a deterministic derive-don't-invent minting
scheme (`#aw_epic{K}` / `#aw_task{N}`), and an explicit uniqueness rule —
gh-aw does not reject duplicates, it silently lets the last writer own the
mapping.
- Rewrite the Hallucination Guard to state that no issue number is returned,
and forbid predicting, inferring, or waiting for one.
- Target `add_labels.item_number`, `create_issue.parent`, and
`create_issue.blocked_by` at temporary IDs; keep verified real numbers for
pre-existing/reused issues and the triggering issue.
Roster correspondence, base `squad`, @copilot mapping, multi-owner and
non-roster behavior, and origin-issue safety are unchanged.
`require-temporary-id` is workflow-global (enforced at the MCP tool layer
from the single create_issue config), so the `squad-plan-accept` fast path
needed a minimal compliance edit to mint a temporary ID. Its label
provisioning remains out of scope — that is #1959.
Verified against the pinned gh-aw v0.87.10.
Closes #1962
Parent #1957
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
🛫 PR Readiness Check
PR Scope: 🔧 Infrastructure
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 3 commits — consider squashing before review |
| ✅ | Not in draft | Ready for review |
| ✅ | Branch up to date | Up to date with dev |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | No source files changed — changeset not required |
| ✅ | Scope clean | No .squad/ or docs/proposals/ files |
| ✅ | No merge conflicts | No merge conflicts |
| ✅ | Copilot threads resolved | 1 active Copilot thread(s) resolved (1 outdated skipped) |
| ✅ | CI passing | All checks passing |
Files Changed (3 files, +436 −22)
| File | +/− |
|---|---|
test/gh-aw-activation-label-provisioning.test.ts |
+20 −3 |
test/gh-aw-activation-temporary-ids.test.ts |
+357 −0 |
workflows/squad.md |
+59 −19 |
Total: +436 −22
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🟡 Impact Analysis — PR #1965Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (1 file)
tests (2 files)
This report is generated automatically for every PR. See #733 for details. |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The updated activation flow now applies labels via add_labels, but nearby workflow prose still contains inconsistent label-reporting and self-validation wording that can mislead the agent.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 2
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
workflows/squad.md — Step 2d uses “requested/recognized” as the count being compared, but then tells the agent to call… |
|
workflows/squad.md — This section now correctly instructs label application via add_labels targeting temporary_id,… |
What changed in this PR
Updates the squad-plan-activate workflow contract to use gh-aw temporary_id linkage (instead of unsatisfiable “returned issue numbers”) so add_labels can deterministically target the intended created issues and avoid silent fallback labeling of the triggering intent issue.
Changes:
- Enforces
require-temporary-id: trueforcreate-issuesafe outputs and documents a Temporary-ID Contract + hallucination guard updates. - Rewrites activation label application instructions to target
add_labels.item_numberat the mintingcreate-issue.temporary_id. - Adds a focused contract test suite asserting both the prose contract and the compiled lock’s
require_temporary_idwiring; updates the prior label-provisioning prose test accordingly.
| File | Description |
|---|---|
| workflows/squad.md | Requires temporary IDs for create-issue, updates activation contract to use temp IDs for parent/blocked_by/add_labels targeting, and refreshes hallucination guidance. |
| test/gh-aw-activation-temporary-ids.test.ts | New tests asserting the temporary-ID contract in prose plus compiled-lock verification of require_temporary_id. |
| test/gh-aw-activation-label-provisioning.test.ts | Updates an assertion to forbid the old “wait for returned issue number” contract and require the new “do not wait” language. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…t refs
Review finding: Step 2b dedups epics by title against prior phases, so a
reused epic was never minted in this run and has no entry in gh-aw's
temporary-ID map. Passing `#aw_epic{K}` for it was an unresolvable parent
reference.
- Step 2c parent targeting now branches on provenance: `#aw_epic{K}` only
when 2b minted the epic this run; the verified real number when 2b matched
a pre-existing epic by title.
- Sub-issue Fallback body reference gets the same carve-out. This matters
more there: gh-aw leaves an unresolved `#aw_…` body reference VERBATIM
rather than stripping it, so a temporary ID it never minted would ship to
the user as a literal "#aw_epic3".
- Adds two focused tests covering reused-epic parent targeting and the
fallback provenance branch.
Prose trimmed elsewhere to stay under the existing 160 KB source-growth
guard rather than raising it.
Refs #1962
Parent #1957
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Independent review found three factual defects in the prose and tests
shipped by the first commit. All three are corrected against the pinned
gh-aw v0.87.10 sources rather than gh-aw's own injected descriptions.
1. `report_incomplete` does NOT make the run conclude non-successfully.
`report_incomplete_handler.cjs` emits `core.warning` only, and
`handle_agent_failure.cjs` contains zero `setFailed`/`process.exit`
calls in 4453 lines. What it actually does is open or update a durable
`[aw] {workflow} reported incomplete result` tracking issue. Step 2e
now states that, says the run still reports success, and tells the
agent never to rely on a red run to carry the signal. gh-aw's own tool
description ("treated as a failure signal even when the agent exits
successfully") is misleading; trusting it was exactly the injected-prose
mistake #1961 exists to prevent.
Limitation, stated honestly: no narrow mechanism in the pinned runtime
makes an incomplete activation conclude red. Forcing one would require
a custom safe-job, which is outside this issue's scope. The durable
tracking record plus the "never report a clean activation you did not
perform" rule are what carry the signal.
2. Cap enforcement is dual, not collection-only. Per Safe Outputs MCE4,
`enforcePerTypeMax` in `safe_outputs_handlers.cjs` throws JSON-RPC
`E002: {type} limit reached` at invocation time, which the agent DOES
see; the collector then drops surplus items with a warning. Removed the
claim that an over-limit item "never appears as an error to the agent".
Reconciliation still keys on absence-of-success, not presence-of-error,
because a call can also simply never be made.
3. The old `max: 80` did not cause runtime truncation — 50 calls never
reached it. The real hazard is gh-aw's injected "Maximum 80 label(s)
can be added" wording against a 100-label worst case, which invites
agent self-truncation. 110 is justified by that hazard plus bounded
margin, and the docblock now explicitly records what is NOT claimed.
Also, per the PR #1965 review: Step 2e no longer demands a real issue
number for items created during the run. Issue creation is deferred to
the post-agent job, so it names the stable temporary ID plus title and
intended labels, and quotes a real number only for a reused issue —
avoiding reintroduction of #1962's invalid-number assumption.
Tests: renamed the two overclaiming tests and gave each a negative guard
that fails if the retracted wording returns; added coverage for the
temporary-ID rule. Trimmed prose to stay under the existing 160 KB
source-growth guard rather than raising another test's budget.
Verified: 221/221 across the three gh-aw suites; mutation-checked (cap
-> 80 fails 5 tests incl. the compiled-runtime one, reintroducing either
retracted claim fails its guard); strict compile clean on all four
workflows; npm run build passes.
Closes #1961
Parent #1957
Working as FIDO (Quality Owner)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2471face-3b27-4419-bd48-0becd2642d7f
Resolves the single workflows/squad.md conflict deliberately, keeping both sides rather than accepting either wholesale: - `- Temporary ID:` — takes #1965's byte-tightened wording ("be unique in this run", "gh-aw silently lets a duplicate's last writer own the mapping"). That commit trimmed prose specifically to stay under the source-growth guard; discarding it would undo that. - `- Labels:` — keeps #1959's corrected rule: the inline `@copilot` → `squad:copilot` mapping in the primary computation, and the non-roster omit-and-record contract. #1965's side of this line predates both fixes. The scoped uniqueness assertion in the fast-path suite now accepts either phrasing — `/(must not repeat within|be unique in) this run/` — and gains a second assertion requiring the silent-duplicate hazard that motivates it. The invariant is enforced more tightly than before, not relaxed: verified by deleting the clause from squad.md and confirming the test fails. Source-growth guard stays at 170 KB. #1965's trims bought room back but not enough — combined source measures 164.8 KB after the merge, still over 160. Comment updated with the post-merge number. Tests: 487 gh-aw tests pass (14 files). All four workflows strict-compile (FAILED=0). `npm run build` passes. Refs #1959 Refs #1957 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Stacks this branch explicitly on `bradygaster-plan-activate-temporary-ids` (PR #1965 head 528ba9b) so #1964 reviews as a clean delta on top of the temporary-ID contract instead of colliding with it at merge time. Conflict resolution (workflows/squad.md, Step 2d — the only conflict): - Took #1965's Self-Validation sentence wholesale. It is strictly stronger than the version on this branch ("requested/recognized", "the last task's temporary ID", "never substitute a guessed issue number"). - Reapplied only this branch's minimal trailing clause, which is #1961's concern and not addressed by #1965: never surface a safe-output cap as a guessed reason for a partial run — name a cap only when Step 2e observed one actually being reached. Step 2e concretized against the now-present mechanism: - The report identifier for an item created this run is the `temporary_id` minted under #1965's Temporary-ID Contract, not a GitHub issue number, because creation is deferred to the safe-output job. - A real number may be quoted only where independently verified (dedup-by-title match, or Step 1's idempotent-rerun path). This mirrors #1965's own "Existing and reused issues" rule rather than restating a parallel contract. Source-growth guard raised 160 -> 168 KB (test/gh-aw-quality.test.ts), per that guard's own documented criterion rather than to turn a red test green: - Ambient prompt (the canonical budget) measures 32.0 KB against 40 KB. - All growth is inside the `squad-plan-activate` inline skill, which the extractor strips from the ambient prompt and loads on demand. - Neither PR was individually over: #1965 alone 163812 B, #1961 alone 163819 B, against the old 163840 B ceiling — 28 and 21 bytes of headroom. Two independently-compliant PRs could not coexist under a threshold that tight. Validation: 239/239 across the four gh-aw suites; three mutation probes (cap 110->80, Step 2e temp-ID rule, retracted dual-enforcement claim) each fail their intended tests; all four workflows compile with `--strict` showing only pre-existing warnings; `npm run build` passes. Compiled lock confirms both contracts coexist: add_labels max 110, create_issue max 75 + require_temporary_id. Refs #1961, #1965. Parent #1957. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2471face-3b27-4419-bd48-0becd2642d7f
Review finding: 2d compared a "requested/recognized" count but then told the
agent to report it as `created={N}`, leaving {N} undefined.
- Defines the quantity once as the **created count** — the number of
`create-issue` calls this run emitted — and uses that one term throughout.
- Binds both parameters explicitly: `created={N}` set to that created count,
`expected={M}` set to the declared total.
- Adds a focused test that locks the single term and both parameter bindings,
and rejects a regression to mixed terminology. The assertion bounds its
slice at the next heading so it cannot reach into Step 4, whose separate
binding wording is #1963's scope.
Step 4's `Activation bindings:` block is deliberately untouched; its
reporting inconsistency is tracked in #1963.
Prose trimmed elsewhere to stay under the existing 160 KB source-growth guard.
Refs #1962
Parent #1957
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keeps the stack on `bradygaster-plan-activate-temporary-ids` current after #1965 advanced past 528ba9b. GitHub had flagged the PR CONFLICTING/DIRTY. One conflict, again at Step 2d, resolved by the same rule used for 528ba9b: take #1965's sentence wholesale and reapply only this branch's trailing clause. #1965 c95d43a tightens 2d by defining the created count explicitly ("the number of `create-issue` calls this run emitted") and binding `created={N}`/`expected={M}` to it. That definition is adopted verbatim. This branch's delta — never surface a safe-output cap as a *guessed* reason for a partial run; name one only when Step 2e observed it being reached — is reapplied to the trailing sentence. Step 2e is unchanged and still follows 2d. #1965's other two hunks in this commit (Uniqueness wording, pre-existing-epic parent rule) auto-merged and are present verbatim. Validation: 240/240 across the four gh-aw suites (count rises from 239 because c95d43a adds a test to #1965's own suite, which passes untouched); all four workflows compile with `--strict` showing only pre-existing warnings; `npm run build` passes. Refs #1961, #1965. Parent #1957. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2471face-3b27-4419-bd48-0becd2642d7f
Resolves #1964 against dev head 9e3f6b3, which now carries both #1965 (temporary-ID contract, fa5cc26) and #1966 (fast-path label provisioning, squash 9e3f6b3). workflows/squad.md — auto-merged, no conflict. #1966's edits land in the `/squad activate` fast path and Step 4; #1961's are in squad-plan-activate Step 2d/2e. Different regions, so the recurring Step 2d conflict did not recur this round. Verified by anchor grep that all three contracts survive unduplicated: #1966's Fast-Path Label Provisioning / non-roster reporting, #1965's Temporary-ID Contract and created-count wording, and #1961's capacity budget / max 110 / Step 2e. test/gh-aw-quality.test.ts — SOURCE_GROWTH_BUDGET_KB conflict resolved to 172 (was 168 here, 170 on dev). Measured, not guessed: combined source on the resolved branch is 173241 B (169.2 KB). - 168 now FAILS by 1209 B, so keeping it would land red. - 170 passes by only 839 B. That is the same stale-threshold failure this guard already hit once at 160, where 21-28 bytes of headroom meant two independently-compliant PRs could not coexist. - 172 leaves 2887 B (~2.8 KB) of real margin, so the guard still bites on genuine growth. dev's #1959/#1962 rationale comment is preserved verbatim; the #1961 rationale is appended beneath it rather than replacing it. Guard precondition confirmed: the canonical ambient prompt budget is still 32.0 KB (32795 B) against 40 KB, unchanged by either PR, because all growth sits inside inline `## skill:` blocks that gh-aw strips from the always-loaded prompt. That is this guard's own documented condition for a legitimate raise. Validation: 320/320 across 9 targeted suites including #1966's new fast-path suite; all four workflows `gh aw compile --strict` clean (pre-existing discriminator/slash_command warnings only); npm run build exit 0. Closes #1961 Parent #1957 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2471face-3b27-4419-bd48-0becd2642d7f
…cit (#1961) (#1964) * fix(gh-aw): prevent silent truncation of activation label operations Working as FIDO (Quality Owner). A 50-issue activation could lose `add_labels` operations while the run still reported success. Two causes: 1. `add-labels: max: 80` was below the worst case under the reading gh-aw's own injected prose invites. The compiler emits "Maximum {max} label(s) can be added", which reads as a budget of label NAMES; a full 50-issue activation applying `squad` + `squad:{agent}` needs 100. An agent taking that phrasing literally could conclude it had overrun and stop labeling early or batch issues together. 2. gh-aw v0.87.10 drops an over-limit item rather than failing. Its collector (`collect_ndjson_output.cjs`) rejects the item and `continue`s, pushing a string into `errors`; those are emitted with `core.warning`, never `core.setFailed`. The run finishes green with label operations missing and nothing announces it. Verified against the pinned runtime rather than inferred: `max` caps safe-output ITEMS (tool calls) per type, not label names inside a call. A two-label call costs one item. Capacity: largest supported activation is 50 issues (`enterprise` profile `max_issues: 50`, the highest documented profile limit, and the same threshold `squad-plan-activate` uses to force phased activation). Worst case at that size is 50 `create-issue` items, 50 `add_labels` calls, <=2 labels per call, 100 label names across the run. Changes: - `add-labels: max` 80 -> 110, sized to cover the worst case under BOTH readings (50 calls, 100 names) so no interpretation of the cap can justify dropping a label operation. `create-issue` stays at 75. - Record the derivation and the item-vs-label-name semantics in the activation skill, plus the fact that an over-limit item is dropped, not failed. - New Step 2e Label-Operation Reconciliation: track `activated` vs `labeled`, count a never-made/rejected/errored call as unlabeled, and on shortfall call `report_incomplete` naming the affected work items. gh-aw treats that as a failure signal even on successful exit, so a truncated activation can no longer be recorded as clean. Cap exhaustion becomes a nameable cause when observed; #1683's rule against guessing at caps is preserved and narrowed. Tests: `test/gh-aw-activation-capacity.test.ts` (27) covers the derived maximum, capacity under both readings, at-maximum and one-over boundaries, and the reconciliation contract. Compiled-artifact assertions prefer runtime evidence over prose: the declared cap reaching `GH_AW_SAFE_OUTPUTS_CONFIG`, the injected constraint carrying the same number, `report_incomplete` being present (and so callable), and agent-job permissions staying `issues: read`. Fails closed if `gh aw` is absent, per #1834. Mutation-checked: reverting the cap to 80 fails 4. No changes to temporary-ID linkage (#1962), fast-path parity (#1959), label result reporting (#1963), broad contract coverage (#1960), or E4 (#1958). Step 2e is deliberately neutral about how `add_labels` identifies its target so it does not conflict with #1962. Closes #1961 Parent #1957 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(gh-aw): correct runtime claims in activation capacity safeguards Independent review found three factual defects in the prose and tests shipped by the first commit. All three are corrected against the pinned gh-aw v0.87.10 sources rather than gh-aw's own injected descriptions. 1. `report_incomplete` does NOT make the run conclude non-successfully. `report_incomplete_handler.cjs` emits `core.warning` only, and `handle_agent_failure.cjs` contains zero `setFailed`/`process.exit` calls in 4453 lines. What it actually does is open or update a durable `[aw] {workflow} reported incomplete result` tracking issue. Step 2e now states that, says the run still reports success, and tells the agent never to rely on a red run to carry the signal. gh-aw's own tool description ("treated as a failure signal even when the agent exits successfully") is misleading; trusting it was exactly the injected-prose mistake #1961 exists to prevent. Limitation, stated honestly: no narrow mechanism in the pinned runtime makes an incomplete activation conclude red. Forcing one would require a custom safe-job, which is outside this issue's scope. The durable tracking record plus the "never report a clean activation you did not perform" rule are what carry the signal. 2. Cap enforcement is dual, not collection-only. Per Safe Outputs MCE4, `enforcePerTypeMax` in `safe_outputs_handlers.cjs` throws JSON-RPC `E002: {type} limit reached` at invocation time, which the agent DOES see; the collector then drops surplus items with a warning. Removed the claim that an over-limit item "never appears as an error to the agent". Reconciliation still keys on absence-of-success, not presence-of-error, because a call can also simply never be made. 3. The old `max: 80` did not cause runtime truncation — 50 calls never reached it. The real hazard is gh-aw's injected "Maximum 80 label(s) can be added" wording against a 100-label worst case, which invites agent self-truncation. 110 is justified by that hazard plus bounded margin, and the docblock now explicitly records what is NOT claimed. Also, per the PR #1965 review: Step 2e no longer demands a real issue number for items created during the run. Issue creation is deferred to the post-agent job, so it names the stable temporary ID plus title and intended labels, and quotes a real number only for a reused issue — avoiding reintroduction of #1962's invalid-number assumption. Tests: renamed the two overclaiming tests and gave each a negative guard that fails if the retracted wording returns; added coverage for the temporary-ID rule. Trimmed prose to stay under the existing 160 KB source-growth guard rather than raising another test's budget. Verified: 221/221 across the three gh-aw suites; mutation-checked (cap -> 80 fails 5 tests incl. the compiled-runtime one, reintroducing either retracted claim fails its guard); strict compile clean on all four workflows; npm run build passes. Closes #1961 Parent #1957 Working as FIDO (Quality Owner) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2471face-3b27-4419-bd48-0becd2642d7f * test(gh-aw): use explicit .js extension on helper import Addresses Copilot review on PR #1964. The reviewer is correct on both counts. `tsc --module nodenext --moduleResolution nodenext` reports exactly one error on this file, and TypeScript names the fix itself: test/gh-aw-activation-capacity.test.ts(94,46): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './helpers/gh-aw-lock.js'? The repo is `"type": "module"` with `module`/`moduleResolution: NodeNext` in the root tsconfig, and the four other suites importing this helper already use the `.js` specifier. This file was the lone outlier; it now matches. Verified: 293/293 across all eight suites that import the helper, all four workflows compile with `--strict` (pre-existing warnings only), `npm run build` passes, and the NodeNext type-check on this file is now clean. Refs #1961. Parent #1957. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2471face-3b27-4419-bd48-0becd2642d7f * fix(workflows): state Step 2e counts as accepted operations, not applied labels Copilot review flagged that Step 2e's reconciliation claimed issues "received their labels". #1963 (now merged to dev) settled the vocabulary: an accepted safe output is queued this turn and applied by the post-agent job, so the agent has evidence only that a call was accepted for a specific target -- never that a label reached GitHub. Step 2e was the remaining place still asserting application, which conflicts with the accepted-vs-applied rule the same skill now states. - `labeled` is defined as issues whose add_labels call was accepted. - The report_incomplete reason reads "had a label operation accepted". - "proof that every label landed" -> "every label operation was accepted". - Added an explicit statement that the counts track label operations, not labels present on GitHub. Tests: new assertion guards the positive and both retired over-claim phrasings; verified by reverting the prose (1 failed, 28 passed) rather than assuming. Tightened the sibling assertion to match the exact count definitions -- the previous `.*`-joined form matched a distant "was accepted" elsewhere in the flattened skill and so failed to detect a reverted definition. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0515ad5d-4c5b-48bb-92a6-4e1296594cab * test(gh-aw): anchor the false-success assertion, which was vacuous Copilot review caught that the regex ended in an `|activated.*artifact` alternative. Alternation binds loosest, so the whole pattern collapsed to that branch and matched any mention of the artifact -- the test would have passed with the `labeled < activated` condition deleted from the prose, which is the entire invariant it names. Anchored to the full sentence and verified by mutation: removing the condition from Step 2e now fails (1 failed / 28 passed) where it previously passed. Second instance of this defect class in this suite; the sibling count assertions were tightened in 4ec89dd for the same reason. Audited the remaining alternations here -- `/finish green|still succeeds/` is legitimate (two accepted phrasings, both specific). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0515ad5d-4c5b-48bb-92a6-4e1296594cab --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2471face-3b27-4419-bd48-0becd2642d7f Copilot-Session: 0515ad5d-4c5b-48bb-92a6-4e1296594cab

Closes #1962
Parent #1957
Working as Procedures (Prompt Engineer) — issue carries
squad:procedures.Problem
The
/squad plan activateprose instructed the model to read a real GitHub issue number back from eachcreate-issuecall, then targetadd_labelsat that number:gh-aw never returns an issue number to the agent. Issue creation is deferred to the post-agent safe-output job, so the agent only ever receives a success acknowledgement. The instruction was therefore unsatisfiable, and it left two failure modes:
add_labelscall whoseitem_numbercannot be resolved does not error — it silently applies the labels to the triggering intent issue, branding the user's own request with an activated item's owner label.Fix
Switched the full activation path to gh-aw's supported
temporary_idlinkage.create-issueaccepted calls without a temp IDrequire-temporary-id: true— a call without one is rejectedadd_labelsitem_number= the mintingcreate-issuecall'stemporary_id; a dedup-matched epic uses its verified real numberparent#aw_epic{K}when minted this run; verified real number when 2b reused a deduped prior-phase epicblockedByvia write API with a real numberblocked_bydeclared on thecreate-issuecall, using temporary IDsParent: #{issue_number}Parent: #aw_epic{K}when minted this run; verified real number otherwisereport_incompleteNew Temporary-ID Contract section covers:
^#?aw_[A-Za-z0-9_]{3,12}$.#aw_epic{K}(1-based epic position),#aw_task{N}(task's own#cell, non-alphanumerics →_, so2.3→#aw_task2_3). Overflow drops theepic/taskword rather than truncating the number.temporary_id;temporaryIdMap.set()silently overwrites, so the last writer owns the mapping and earlier references resolve to the wrong issue. No warning is emitted.Reused/deduped epics (review finding, fixed)
Step 2b dedups epics by title against prior phases. A reused epic was never minted in this run, so it has no entry in gh-aw's temporary-ID map — passing
#aw_epic{K}for it would be an unresolvable reference. Both places that reference an epic now branch on provenance:parent— temporary ID only when 2b minted the epic this run; verified real number for a dedup-by-title/prior-phase match. The prose also explicitly forbids passing a temporary ID that was not minted this run.#aw_…body reference verbatim rather than stripping it, so a temporary ID it never minted would ship to the user as a literal#aw_epic3.Preserved unchanged: roster correspondence (per-task
Agentcell, never inherited from the epic or carried forward), basesquad,@copilot→squad:copilot, multi-owner epic behavior, non-roster recording, and origin-issue safety.Contract verification (pinned gh-aw v0.87.10)
Checked against the binary's embedded schema and upstream source, not assumed:
create_issue.temporary_id— optional string,^#?aw_[A-Za-z0-9_]{3,12}$add_labels.item_number—number | string,^(\d+|#?aw_[A-Za-z0-9_]{3,12})$create_issue.parent/create_issue.blocked_by— accept temporary IDsrequire-temporary-id: truecompiles to"require_temporary_id":true,required_field_additions.create_issue: ["temporary_id"], and the agent-facing constraint stringtemporary_id is required.create_issue.blocked_by; anadd_labelshitting an unresolved temp ID returnsdeferredand gets one retry pass — socreate-issuethenadd_labelsis the supported order.replaceTemporaryIdReferences()leaves an unresolved#aw_…body reference intact — the basis for the fallback carve-out above.1.
require-temporary-idis workflow-global, not per-skill.It is enforced in
safe_outputs_handlers.cjsat the MCP tool layer from the singleconfig.create_issueblock.workflows/squad.mdhas a secondcreate_issuecaller — thesquad-plan-acceptfast path (#1959's scope) — whose calls would be rejected outright once the flag is on. I enabled the flag (an explicit #1962 success criterion) plus the smallest possible compliance edit to the fast path: mint a temporary ID and use it forparent. I deliberately did not addadd_labelsorcreate-if-missingthere — that remains #1959, and an existing test asserts their absence in that region (still passing).2. The
Activation bindings:JSON block still references real issue numbers — and cannot be fixed here.replaceTemporaryIdReferences()replaces an#aw_task1token with`#${number}`— the#is retained for same-repo refs. Embedding#aw_refs in that JSON code block would emit"issue": #42, which is invalid JSON. There is no substitution form that yields a bare number. Per the instruction to report rather than invent a workaround, I left the block unchanged and am flagging it as a blocker/decision for #1963 rather than papering over it. This means #1962's "no operation relies on an inferred issue number" is satisfied for every targeting operation but not yet for that reporting block.Viable avenue for #1963: gh-aw exposes the resolved map as
steps.process_safe_outputs.outputs.temporary_id_map(JSON{ "aw_id": {"repo":..., "number":N} }) and as artifact/tmp/gh-aw/temporary-id-map.json.Validation
squad,squad-implement-worker,squad-review,squad-deps-worker) compile with--strict, all 4 lock files emitted,FAILED=0. Remaining warnings are pre-existing ondev(concurrency discriminator, slash_command+bots).296 passedacrossgh-aw-activation-temporary-ids,gh-aw-activation-label-provisioning,gh-aw-agent-binding-correspondence,gh-aw-activate-roster-binding,gh-aw-plan-lifecycle,gh-aw-quality.npm run buildpasses.8127 passed. The 12 failures incli-packaging-smoke/acceptance/ external-state CLI tests are pre-existing — reproduced identically on the baseline with my changes stashed.Test changes
Updated
test/gh-aw-activation-label-provisioning.test.ts— one test asserted the prose must contain "never calladd_labelsbefore [create-issue] has returned a real issue number". That sentence encoded the exact invalid premise this issue corrects, so the test now asserts the inverse contract (the old rule is gone; the prose tells the model not to wait; create-issue returns no number).Added
test/gh-aw-activation-temporary-ids.test.ts(18 tests) — narrowly scoped to #1962: temporary-ID declaration on every activationcreate-issue, the documented gh-aw pattern, deterministic minting, the uniqueness mandate, mandatory explicititem_number,add_labels→temporary_idwiring, no-prediction rules, real numbers for reused issues, 2b/2c temp-ID fields, epic-as-parent, reused-epic parent targeting, sub-issue fallback provenance branching,blocked_byoncreate-issue, plus compiled-lock assertions thatrequire_temporary_idactually reaches the handler config. Fails closed on a missinggh awper the repo's #1834 convention.Prose was tightened to stay under the existing 160 KB source-growth guard rather than raising it.
Out of scope (untouched)
/squad activatefast-path provisioning (#1959, beyond the minimal compliance edit above), activation reporting redesign (#1963), capacity safeguards (#1961), broad behavioral suite (#1960), E4 (#1958), checker distribution.No changeset: no
packages/*/src/ortemplates/changes, so thechangelog-gatepath regex does not match.