fix(workflows): provision labels in the /squad activate fast path - #1966
Conversation
The recommended `/squad activate` fast path (`squad-plan-accept`) still relied on `create-issue`'s `labels:` field, which GitHub silently drops for label names that do not already exist. On a fresh repository every fast-path issue came out unlabeled even after #1962 corrected the full `plan activate` path. Applies the same verified temporary-ID + `add_labels(create-if-missing)` pattern to the fast path: - `add_labels` is called in the same turn as each `create-issue`, with `item_number` set to that call's own `#aw_ph{N}` / `#aw_wi{N}` temp ID. - Reused or pre-existing issues are targeted by verified real numbers. - `item_number` is mandatory; the silent triggering-issue fallback is named so the origin intent issue never gets an owner label. - Label sets reach parity with `squad-plan-activate`: base `squad`, single certified owner, `@copilot` -> `squad:copilot`, multi-owner phase gets `squad` only and is recorded. - The fast path's stricter pre-mutation stop on a non-roster `Owner` is preserved, with an `@copilot` carve-out so the mapping is reachable. - #1962's minimal temp-ID compliance is preserved, not regressed. Raises the source-growth guard 160 -> 170 KB: it had ~37 bytes of headroom left after #1962, so it had stopped measuring growth and started blocking correct changes. The new prose is inside an inline `## skill:` block that gh-aw strips, so the canonical 40 KB ambient prompt budget is unaffected and still passing. Closes #1959 Parent #1957 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Two corrections to the #1959 fast-path label provisioning. 1. The primary Step 2 `- Labels:` computation said only "the frozen row `Owner` lowercased", which turns `@copilot` into the invalid `squad:@copilot`. Stating the mapping further down in the provisioning section left the primary rule wrong. The rule "Map `@copilot` to `squad:copilot`; never `squad:@copilot`" is now inline in that exact bullet, and the test asserts against the extracted bullet rather than the whole skill, so a correct-elsewhere/wrong-here workflow fails. 2. #1959 requires non-roster behavior to match the full activation path. The fast path stopped before mutation and demanded `/squad plan revise`; `squad-plan-activate` applies base `squad`, omits the owner label, and continues. The fast path now does the same and records the value under `Non-roster agent values`. `ROSTER_UNREADABLE:` still stops before mutation — that is a missing certified set, not an uncertified value, and it stops in `squad-plan-activate` too. Step 4 now requires the `Non-roster agent values` heading whenever an accepted `Owner` did not become a label, so the omission record points at something real. Multi-owner phase issues are unchanged: bare `squad`, explicitly recorded. This is a heading requirement only, not a summary redesign, so it stays compatible with #1963. Tests: 485 gh-aw tests pass. All four workflows strict-compile (FAILED=0). `npm run build` passes. Working as Procedures (Prompt Engineer) Refs #1959 Refs #1957 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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>
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The repository’s routing rules assign this PR to the Procedures agent (squad:procedures), so it should be reviewed and approved in that specialist context.
Review tier: Lite
Findings: None
What changed in this PR
This review is deferred because the task is explicitly routed to the Procedures agent (squad:procedures) and should be handled in that agent’s context.
Changes:
- Review not performed (routed to Procedures).
| File | Description |
|---|---|
| workflows/squad.md | Not reviewed (routed to Procedures) |
| test/gh-aw-quality.test.ts | Not reviewed (routed to Procedures) |
| test/gh-aw-activation-label-provisioning.test.ts | Not reviewed (routed to Procedures) |
| test/gh-aw-activate-fast-path-label-provisioning.test.ts | Not reviewed (routed to Procedures) |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
🛫 PR Readiness Check
PR Scope: 🔧 Infrastructure
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 4 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 | No Copilot review threads |
| ✅ | CI passing | All checks passing |
Files Changed (4 files, +506 −13)
| File | +/− |
|---|---|
test/gh-aw-activate-fast-path-label-provisioning.test.ts |
+416 −0 |
test/gh-aw-activation-label-provisioning.test.ts |
+10 −7 |
test/gh-aw-quality.test.ts |
+9 −1 |
workflows/squad.md |
+71 −5 |
Total: +506 −13
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🟡 Impact Analysis — PR #1966Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (1 file)
tests (3 files)
This report is generated automatically for every PR. See #733 for details. |
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
Both activation paths could report labels that were never applied, and the
`Activation bindings:` block specified JSON the agent could not produce.
Over-claim by attribution. `squad-plan-activate` Label Pre-flight Step 8 let a
summary name a `squad:{agent}` label once "that issue's `create-issue` call
returned successfully carrying it". `create-issue`'s `labels:` field cannot land
a label the repository lacks — the failure #1959 fixed — so that sentence
licensed reporting labels that never existed. Both paths now condition a
reported label on an accepted `add_labels` call carrying it for that same
issue, targeted by the item's own temporary ID or, for a reused item, its
verified real number. A successful `create-issue` is stated to be no evidence.
Invalid bindings JSON. The block required bare `{created task issue number}`.
The agent never learns a created issue's real number during its turn, and
gh-aw's temporary-ID substitution is a plain text replacement over the whole
comment body that does not skip fenced code blocks and keeps the `#`. A bare
`"issue":#aw_task1` becomes `"issue":#42` — invalid JSON that fails the entire
block. Verified against the pinned runtime (github/gh-aw-actions@v0.87.2,
setup/js/temporary_id.cjs). Quoting is the narrowest correct fix:
`"issue":"#aw_task1"` becomes `"issue":"#42"`, which parses. No new safe job,
no temporary_id_map plumbing, and no inferred numbers.
Runtime limitation, stated rather than worked around: safe outputs are applied
in a post-agent job, so a run has evidence only that a call was accepted for a
specific target, never the GitHub API result. "Accepted" is the strongest
honest claim; the prose forbids saying a label was verified, confirmed, or
checked. Under-claiming is rejected too — an accepted operation may not be
reported as an omission — and the required `Non-roster agent values` handling
from the base stack is preserved and made two-directional.
The post-activation checker resolves `"#42"` and bare integers, and fails
closed on an unresolved `#aw_...` rather than skipping or repairing it.
Tests: new focused suite covering both paths; 20 of its 28 assertions fail
against the pre-fix prose. Two existing assertions that pinned the old
create-issue attribution are updated to the strictly stronger condition, with
their original intent (#1860, #1959) preserved.
Source growth guard raised 170 -> 173 KB. Measured against dev with #1966
already squash-merged, so this counts only #1963's bytes: 175 485 B = 171.4 KB.
172 KB would leave 643 B, reproducing the near-zero-margin failure the guard's
own comment warns about; 173 KB leaves 1 667 B. Not set higher, because #1964
is unmerged and pre-raising for it would hide growth that has not happened. The
ambient prompt budget is unaffected at 32 795 B = 32.0 KB against 40 KB, which
is the condition that guard names as making a raise legitimate.
Closes #1963
Parent #1957
Previously stacked on #1959 (PR #1966); that merged to dev as 9e3f6b3, so this
now targets dev directly and contains #1963's changes only.
Working as Procedures (Prompt Engineer)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
#1963) (#1967) * fix(workflows): report accepted label outcomes in activation summaries Both activation paths could report labels that were never applied, and the `Activation bindings:` block specified JSON the agent could not produce. Over-claim by attribution. `squad-plan-activate` Label Pre-flight Step 8 let a summary name a `squad:{agent}` label once "that issue's `create-issue` call returned successfully carrying it". `create-issue`'s `labels:` field cannot land a label the repository lacks — the failure #1959 fixed — so that sentence licensed reporting labels that never existed. Both paths now condition a reported label on an accepted `add_labels` call carrying it for that same issue, targeted by the item's own temporary ID or, for a reused item, its verified real number. A successful `create-issue` is stated to be no evidence. Invalid bindings JSON. The block required bare `{created task issue number}`. The agent never learns a created issue's real number during its turn, and gh-aw's temporary-ID substitution is a plain text replacement over the whole comment body that does not skip fenced code blocks and keeps the `#`. A bare `"issue":#aw_task1` becomes `"issue":#42` — invalid JSON that fails the entire block. Verified against the pinned runtime (github/gh-aw-actions@v0.87.2, setup/js/temporary_id.cjs). Quoting is the narrowest correct fix: `"issue":"#aw_task1"` becomes `"issue":"#42"`, which parses. No new safe job, no temporary_id_map plumbing, and no inferred numbers. Runtime limitation, stated rather than worked around: safe outputs are applied in a post-agent job, so a run has evidence only that a call was accepted for a specific target, never the GitHub API result. "Accepted" is the strongest honest claim; the prose forbids saying a label was verified, confirmed, or checked. Under-claiming is rejected too — an accepted operation may not be reported as an omission — and the required `Non-roster agent values` handling from the base stack is preserved and made two-directional. The post-activation checker resolves `"#42"` and bare integers, and fails closed on an unresolved `#aw_...` rather than skipping or repairing it. Tests: new focused suite covering both paths; 20 of its 28 assertions fail against the pre-fix prose. Two existing assertions that pinned the old create-issue attribution are updated to the strictly stronger condition, with their original intent (#1860, #1959) preserved. Source growth guard raised 170 -> 173 KB. Measured against dev with #1966 already squash-merged, so this counts only #1963's bytes: 175 485 B = 171.4 KB. 172 KB would leave 643 B, reproducing the near-zero-margin failure the guard's own comment warns about; 173 KB leaves 1 667 B. Not set higher, because #1964 is unmerged and pre-raising for it would hide growth that has not happened. The ambient prompt budget is unaffected at 32 795 B = 32.0 KB against 40 KB, which is the condition that guard names as making a raise legitimate. Closes #1963 Parent #1957 Previously stacked on #1959 (PR #1966); that merged to dev as 9e3f6b3, so this now targets dev directly and contains #1963's changes only. Working as Procedures (Prompt Engineer) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(workflows): remove applied-label over-claim from the shared ontology Review on #1967 caught the exact defect this PR exists to remove, in a file the PR already touches. The `Activation bindings:` paragraph in the shared ontology said bindings record "epic labels reported as applied", while the paragraph directly below it correctly defines a reported label as an accepted `add_labels` operation that the agent never observes landing. squad.md stated the accurate contract and the ontology stated the strong one. That split matters because the ontology is the artifact contract the deterministic checker consumes and the file most likely to be read standalone: a consumer reading only that paragraph gets the claim the runtime cannot justify. Left as prose-only drift it would have turned one consistently-wrong place into two inconsistently-wrong ones. Reworded to the accepted-operation semantics used everywhere else, pointing a standalone reader at the definition below it. Guarded against re-drift with a negative assertion over the ontology, mirroring the guard the suite already applies to squad.md. The match is shape-targeted rather than a ban on the word "applied": the same file legitimately says safe outputs "are applied after the agent turn", which is the justification for the weaker claim, so a blanket match would forbid the correct prose too. Verified by mutation — restoring the old wording fails the new test, and only that test. Also pins the local `gh aw` install hint to v0.87.10, matching the pin in .github/workflows/squad-ci.yml, so local strict-compile and lock output match CI. The separate `gh-aw-actions@v0.87.2` citation is left as-is: that is the pinned runtime action whose temporary_id.cjs this change was verified against, which is a different artifact from the CLI extension, and v0.87.2 is still what .github/workflows references. Source growth re-measured after the edit: 175 519 B = 171.4 KB, 1 633 B under the 173 KB guard, so the guard is unchanged. Ambient prompt unaffected. Closes #1963 Parent #1957 Working as Procedures (Prompt Engineer) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Closes #1959
Parent #1957
Working as Procedures (Prompt Engineer) — issue carries
squad:procedures.Base branch:
bradygaster-plan-activate-temporary-ids(PR #1965 / #1962), notdev.#1965 is unmerged and this work builds directly on it — it establishes the temporary-ID contract, enables workflow-global
require-temporary-id: true, and itscreate-if-missingsafe output is the mechanism reused here. Merge #1965 first, then this PR's base auto-retargets todev.Problem
/squad activateroutes tosquad-plan-accept. When only a flatplanartifact exists (noprogram/implementation), it takes its own fast path with its owncreate-issuecalls — separate code fromsquad-plan-activate.#1955 fixed fresh-repo label provisioning in
squad-plan-activateonly. #1962 added just enough temporary-ID compliance to the fast path to survive the new globalrequire-temporary-id: trueflag, and deliberately stopped there. So the fast path still leaned oncreate-issue'slabels:field — and GitHub silently drops label names that don't already exist in the target repo instead of creating them.Net effect: on a fresh repository, the recommended activation command produced issues with none of the labels the docs promise, while the legacy long path worked correctly.
Fix
Applied the same verified pattern to the fast path. New
##### Fast-Path Label Provisioningsection insquad-plan-accept, plus a per-item- Label application:bullet in Step 2.add_labels(allowed: [squad, "squad:*"],create-if-missing: true) — the same safe outputsquad-plan-activateuses. Both paths now provision identically.item_number= thatcreate-issuecall's owntemporary_id(#aw_ph{N}phase,#aw_wi{N}work item)create-issuefirst,add_labelsimmediately after; no waiting for a returned number (none is returned)add_labelstargetEvery
add_labelscall must passitem_number. The prose names the consequence, not just the rule: omitting it does not error — gh-aw silently applies the labels to the triggering intent issue, branding the user's own request with an activated item's owner label. That's the same hazardsquad-plan-activatewarns about, stated identically.Parity with the full activation path
squadon every activated item.squad:{owner}from that row's own frozen certifiedOwner, lowercased. Re-read per row; never inherit the phase issue's owner or carry the previous row's value forward.@copilot→squad:copilot, neversquad:@copilot— stated inline in the primary Step 2 label computation, not only in the provisioning section (see below).squadonly, choose none of them, recorded underNon-roster agent values.squadonly, owner label omitted, run continues, value recorded (see below).ROSTER_UNREADABLE:→ stop before mutation, report the named reason. Also a stop insquad-plan-activate.@copilotmust be mapped in the primary computationThe Step 2
- Labels:bullet is where the label set is actually computed, and it said only "the frozen rowOwnerlowercased" — which turns@copilotinto the invalidsquad:@copilot. Stating the mapping only in the provisioning section below left the primary rule wrong; a model following the bullet literally still emits the bad label.Map
@copilottosquad:copilot; neversquad:@copilotis now inline in that exact bullet. The test extracts that bullet with/^- Labels:.*$/mand asserts against it specifically, so a workflow that is correct elsewhere but wrong in the primary computation fails.Non-roster: aligned to full activation
#1959 requires non-roster behavior to match the full activation path, so the fast path's pre-mutation stop is removed, not preserved.
squad-plan-activateapplies basesquad, omits the owner label, and continues; the fast path now does the same and records the value underNon-roster agent values. Both paths now produce identical results for the same plan.ROSTER_UNREADABLE:still stops before mutation. That is a different failure — no certified set exists at all, so omit-and-record has nothing to certify against — andsquad-plan-activatestops there too. The distinction is asserted by a dedicated test so the stop can't be mistaken for a leftover of the removed per-owner stop.Step 4 now requires the
Non-roster agent valuesheading whenever an acceptedOwnerdid not become asquad:{owner}label — multi-owner phase or uncertified value — naming the value and the issue it applied to. Without that, "record it" pointed at a heading the fast path never had to emit. This is a heading requirement only, not a summary redesign, so it stays compatible with #1963.#1962 compliance preserved
A dedicated regression test asserts the fast path keeps its
Temporary ID:bullet,require-temporary-id: true, both ID forms, the^#?aw_[A-Za-z0-9_]{3,12}$pattern, the uniqueness mandate, and the#aw_ph{N}parent linkage. A compiled-lock assertion confirmsrequire_temporary_idandrequired_field_additions.create_issuestill reach the handler config — that flag is workflow-global and covers the fast path'screate_issuecalls too.📌 Source-growth guard raised 160 → 170 KB
gh-aw-quality.test.ts's guard had ~37 bytes of headroom left after #1962 (163 803 of 163 840). At that margin it had stopped measuring growth and started blocking any correct change on byte count alone — the same failure mode its own comment block documents from #1842.The raise is legitimate by the criterion that comment names: the new prose lives entirely inside the
squad-plan-acceptinline## skill:block, which gh-aw strips during setup/interpolation, so it never enters the ambient prompt. The canonical budget —keeps the ambient prompt under 40 KB— is unaffected and still passing. I tightened the section first and still could not fit under 160 KB.Validation
squad,squad-implement-worker,squad-review,squad-deps-worker) compile with--strict --approve, all 4 lock files emitted,FAILED=0. Remaining warnings (concurrency discriminator,slash_command+bots) are pre-existing and identical on the base branch.485 passedacross all 14gh-aw-*suites.npm run buildpasses.Test changes
Added
test/gh-aw-activate-fast-path-label-provisioning.test.ts(26 tests) — routing sanity (/squad activate→squad-plan-accept, so the suite can't drift onto the wrong skill), the dropped-label root cause,add_labels+create-if-missingpresence, same-turn pairing at both section and bullet level, temp-ID targeting, #1962 compliance regression guard, mandatoryitem_number+ the silent-fallback hazard, no-borrowed-IDs, verified real numbers for reused issues, no-prediction, then per-label-set parity assertions (basesquad, own-row owner,@copilotasserted against the extracted primary bullet, multi-owner, non-roster omit-and-record,ROSTER_UNREADABLE:stop, the requiredNon-roster agent valuesheading, origin-issue safety, idempotency, default-color-is-not-a-failure, report-what-applied). Several assert the same string against both skills so the two paths can't silently diverge. One asserts the removed/squad plan revisestop is absent, so it can't be reintroduced alongside the new rule. Compiled-lock section fails closed on a missinggh awper this repo's #1834 convention.Updated
test/gh-aw-activation-label-provisioning.test.ts— one #1955 boundary test asserted the fast path contained noadd_labelsand nocreate-if-missing, encoding the exact gap #1959 closes. Inverted to assert the parity instead.Updated
test/gh-aw-quality.test.ts— guard constant and its rationale comment (see above).Impact on #1963 (activation reporting)
#1965 flagged that the
Activation bindings:JSON block still embeds real issue numbers and cannot be fixed there, becausereplaceTemporaryIdReferences()retains the#, which would emit invalid JSON. That constraint is unchanged by this PR — the fast path's Step 4 posts aplan-accepted/phases-acceptedartifact and does not emit anActivation bindings:block at all, so this PR neither worsens nor resolves it.Three things #1963 should now account for:
squad-plan-accept's Step 4 summary as well assquad-plan-activate's Step 4 record. Previously the fast path had no label operations worth attributing.Non-roster agent valuesis mandatory in the fast path's Step 4 whenever a label was omitted. Make Squad activation summaries report actual label outcomes #1963 must either keep that heading or explicitly supersede it — the omit-and-record rule in Step 2 and the provisioning section both reference it by name.add_labelscall actually applied; never a label that was skipped, deferred, or merely intended." Make Squad activation summaries report actual label outcomes #1963 can build on that rather than introducing a competing contract, but it will need to reconcile the fast path's heading with a mechanism that has no bindings block to hang structured data on.Out of scope (untouched)
Activation reporting redesign (#1963), capacity safeguards (#1961), broad behavioral contracts (#1960), E4 (#1958), checker distribution.
No changeset: no
packages/*/src/changes, so thechangelog-gatepath regex does not match.