…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
Summary
Hardens the
safe-outputssystem reliability in Plan Activate mode to address Brady's report of issues not being outputted from Actions.Root Cause Analysis
Multiple failure modes compound during Plan Activate:
max: 50limit — large plans could hit the cap silentlysquadlabel, potentially blockingcreate-issueChanges
maxfrom 50 → 75How safe-outputs permissions work
issues: readis correct. gh-aw safe-outputs bypass normal GITHUB_TOKEN permissions — writes are handled by the platform layer. Thecreate-issuesafe-output does NOT needissues: write.Full analysis
See
.squad/decisions/inbox/copilot-safe-outputs-reliability.md(gitignored, available locally).Ref: #1678