From dd644aca107ac2a6bbe047753e40bbcca3234547 Mon Sep 17 00:00:00 2001 From: Brady Gaster <41929050+bradygaster@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:47:21 +0000 Subject: [PATCH 1/2] fix(workflow): forward-port safe-output reliability hardening from #1683 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Increase create-issue max from 50 to 75 (supports larger plans) - Add Output Budget Awareness: phasing guidance when >50 issues, compact bodies when >30 - Add Label Pre-flight: ensure squad/squad:{agent} labels exist before first create-issue - Add Transient Failure Handling: single retry on 5xx, skip+report on second failure or 4xx - Add Sub-issue Fallback: degrade gracefully on 404/422, record parent as body reference - Extend gh-aw-quality tests: assert max=75, lock all four hardening behaviors, add headroom regression guard Architecture: changes are terse inline guidance within Plan Activate — no new phases, no speculative redesign, compressed prompt architecture preserved (33 KB, ~67 KB headroom). Label pre-flight uses safe-output permissions (issues:read is correct per gh-aw platform layer). Manual forward-port required: PR #1683 branch contains pre-compression 112 KB squad.md — rebasing/merging would restore the uncompressed file and violate the 100 KB gh-aw ceiling. Refs #1678 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e5d9bcc6-d667-485b-bccb-738bd9842102 --- ...ix-safe-outputs-plan-activate-hardening.md | 12 +++++ test/gh-aw-quality.test.ts | 49 +++++++++++++++++++ workflows/squad.md | 18 ++++++- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-safe-outputs-plan-activate-hardening.md diff --git a/.changeset/fix-safe-outputs-plan-activate-hardening.md b/.changeset/fix-safe-outputs-plan-activate-hardening.md new file mode 100644 index 000000000..25e08a1b2 --- /dev/null +++ b/.changeset/fix-safe-outputs-plan-activate-hardening.md @@ -0,0 +1,12 @@ +--- +"squad-cli": patch +--- + +Harden Plan Activate safe-output reliability: +- Increase create-issue max from 50 to 75 (supports larger plans) +- Add label pre-flight validation before issue creation +- Add transient failure handling with single retry for 5xx errors +- Add output budget awareness and bounded phasing guidance +- Add graceful fallback for sub-issue API failures (404/422) + +Forward-port of intended changes from PR #1683, rebased on compressed prompt architecture (PR #1685). Ref: #1678 diff --git a/test/gh-aw-quality.test.ts b/test/gh-aw-quality.test.ts index db6e7dfce..58134e2a1 100644 --- a/test/gh-aw-quality.test.ts +++ b/test/gh-aw-quality.test.ts @@ -238,6 +238,12 @@ describe('gh-aw: safe-output configuration', () => { expect(safeOutputs['create-issue'], 'create-issue should exist').toBeDefined(); expect(safeOutputs['add-comment'], 'add-comment should exist').toBeDefined(); }); + + it('create-issue max is 75 (supports large plans, forward-port of #1683)', () => { + const ci = safeOutputs['create-issue']; + expect(ci, 'create-issue block must exist').toBeDefined(); + expect(ci['max'], 'create-issue max must be 75 — do not reduce below this').toBe(75); + }); }); // --------------------------------------------------------------------------- @@ -528,4 +534,47 @@ describe('gh-aw: prompt budget & planning import regression', () => { `Combined prompt is ${totalKB} KB — exceeds the gh-aw ${GH_AW_PROMPT_CEILING_KB} KB ceiling. Headroom: ${headroomKB} KB.` ).toBeLessThan(GH_AW_PROMPT_CEILING_BYTES); }); + + it('reports combined bytes and headroom', () => { + let totalBytes = Buffer.byteLength(squadContent, 'utf8'); + for (const importPath of imports) { + const fullPath = join(WORKFLOWS_DIR, importPath); + if (existsSync(fullPath)) totalBytes += Buffer.byteLength(readFileSync(fullPath, 'utf8'), 'utf8'); + } + const headroomBytes = GH_AW_PROMPT_CEILING_BYTES - totalBytes; + // Informational — log bytes/headroom; fail only if headroom < 5 KB (regression guard) + expect( + headroomBytes, + `Headroom too low: ${(headroomBytes / 1024).toFixed(1)} KB remaining of ${GH_AW_PROMPT_CEILING_KB} KB ceiling` + ).toBeGreaterThan(5 * 1024); + }); +}); + +// --------------------------------------------------------------------------- +// Test: Plan Activate hardening behaviors (forward-port #1683) +// --------------------------------------------------------------------------- + +describe('gh-aw: Plan Activate hardening behaviors', () => { + const content = readFileSync(SQUAD_WORKFLOW, 'utf8'); + + it('includes output budget awareness guidance', () => { + expect(content).toContain('Output Budget Awareness'); + expect(content).toMatch(/total.*>\s*50.*phased activation|phased.*activation.*>\s*50/i); + }); + + it('includes label pre-flight step before issue creation', () => { + expect(content).toContain('Label Pre-flight'); + expect(content).toMatch(/squad.*label.*exist|label.*squad.*exist/i); + }); + + it('includes transient failure handling with single retry', () => { + expect(content).toContain('Transient Failure Handling'); + expect(content).toMatch(/5xx.*retry|retry.*5xx/i); + }); + + it('includes graceful sub-issue fallback for 404/422', () => { + expect(content).toContain('Sub-issue Fallback'); + expect(content).toMatch(/404.*422|422.*404/); + expect(content).toMatch(/degrade gracefully|graceful/i); + }); }); diff --git a/workflows/squad.md b/workflows/squad.md index d817ae76e..01efb9a7d 100644 --- a/workflows/squad.md +++ b/workflows/squad.md @@ -50,7 +50,7 @@ safe-outputs: expires: 14d create-issue: labels: [squad] - max: 50 + max: 75 add-comment: max: 20 --- @@ -711,6 +711,22 @@ After phase acceptance, check if ready for automatic activation: After EVERY `create-issue` call: verify returned issue number, stop on failure, NEVER predict issue numbers. +##### Output Budget Awareness + +Count expected issues before starting. If total > 50: recommend phased activation (`/squad plan activate phase {N}`) and proceed with the current phase only. If total > 30: use compact issue bodies (scope + acceptance criteria only; omit elaboration). + +##### Label Pre-flight + +Before the first `create-issue`, check that labels `squad` and any `squad:{agent}` exist. If missing, create them with color `0075ca` / `e4e669` and description. Safe-output permissions handle the write — no additional token scope needed. Skip silently if label creation fails; continue activation. + +##### Transient Failure Handling + +On `5xx` response from `create-issue`: wait briefly and retry once. On second failure or `4xx`: record the issue title as skipped in the activation summary, continue with remaining issues. Never abort the full run for a single transient failure. + +##### Sub-issue Fallback + +When setting a `parent` sub-issue relationship returns `404` or `422` (feature disabled or repo plan): degrade gracefully — record the intended parent as a body reference (`Parent: #{issue_number}`), then continue. Never fail activation over sub-issue API unavailability. + ##### Step 2: Create Issues — Full Hierarchy Root → Epics → Tasks. Phase-specific: filter to matching phase heading. From 0d136af04825d2263d141633e3f904253d3d6580 Mon Sep 17 00:00:00 2001 From: Brady Gaster <41929050+bradygaster@users.noreply.github.com> Date: Wed, 12 Aug 2026 01:53:28 +0000 Subject: [PATCH 2/2] fix(workflows): replace impossible label-creation guidance with truthful preflight reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Label Pre-flight previously claimed that missing labels could be created via safe-output permissions with issues: read — this is factually wrong. The workflow declares issues: read (not write), and no create-label safe-output is configured. The instruction was success-shaped impossible guidance. Replacement behavior: - Verify labels squad and squad:{agent} exist before first create-issue - If missing: record as prerequisite gap in activation summary (requires issues: write + create-label safe-output — not configured) - Continue activation; apply existing labels normally; omit unavailable labels and report exactly which were omitted — do not abort creation Also: - Remove .changeset/fix-safe-outputs-plan-activate-hardening.md; workflow-only/test-only change does not require a package changeset - Add 3 focused tests locking the truthful preflight behavior: no impossible creation claims, prerequisite gap reporting, omit+report All approved hardening preserved: create-issue max=75, output budget phasing, single transient retry, sub-issue fallback. Closes #1683 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e5d9bcc6-d667-485b-bccb-738bd9842102 --- .../fix-safe-outputs-plan-activate-hardening.md | 12 ------------ test/gh-aw-quality.test.ts | 16 ++++++++++++++++ workflows/squad.md | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) delete mode 100644 .changeset/fix-safe-outputs-plan-activate-hardening.md diff --git a/.changeset/fix-safe-outputs-plan-activate-hardening.md b/.changeset/fix-safe-outputs-plan-activate-hardening.md deleted file mode 100644 index 25e08a1b2..000000000 --- a/.changeset/fix-safe-outputs-plan-activate-hardening.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"squad-cli": patch ---- - -Harden Plan Activate safe-output reliability: -- Increase create-issue max from 50 to 75 (supports larger plans) -- Add label pre-flight validation before issue creation -- Add transient failure handling with single retry for 5xx errors -- Add output budget awareness and bounded phasing guidance -- Add graceful fallback for sub-issue API failures (404/422) - -Forward-port of intended changes from PR #1683, rebased on compressed prompt architecture (PR #1685). Ref: #1678 diff --git a/test/gh-aw-quality.test.ts b/test/gh-aw-quality.test.ts index 58134e2a1..1d9abe10a 100644 --- a/test/gh-aw-quality.test.ts +++ b/test/gh-aw-quality.test.ts @@ -567,6 +567,22 @@ describe('gh-aw: Plan Activate hardening behaviors', () => { expect(content).toMatch(/squad.*label.*exist|label.*squad.*exist/i); }); + it('label pre-flight does not claim impossible label creation', () => { + // Workflow has issues: read and no create-label safe-output; must not claim it can create labels + const preflight = content.match(/##### Label Pre-flight\n([\s\S]*?)(?=\n#####|\n####)/)?.[1] ?? ''; + expect(preflight).not.toMatch(/create them|safe-output permissions handle the write|no additional token scope/i); + }); + + it('label pre-flight reports missing labels as prerequisite gap', () => { + expect(content).toMatch(/prerequisite gap|prerequisite/i); + expect(content).toMatch(/issues: write/i); + expect(content).toMatch(/create-label.*safe-output|safe-output.*create-label/i); + }); + + it('label pre-flight continues activation and omits unavailable labels with report', () => { + expect(content).toMatch(/unavailable labels are omitted and reported|omitted and reported/i); + }); + it('includes transient failure handling with single retry', () => { expect(content).toContain('Transient Failure Handling'); expect(content).toMatch(/5xx.*retry|retry.*5xx/i); diff --git a/workflows/squad.md b/workflows/squad.md index 01efb9a7d..a1889904e 100644 --- a/workflows/squad.md +++ b/workflows/squad.md @@ -717,7 +717,7 @@ Count expected issues before starting. If total > 50: recommend phased activatio ##### Label Pre-flight -Before the first `create-issue`, check that labels `squad` and any `squad:{agent}` exist. If missing, create them with color `0075ca` / `e4e669` and description. Safe-output permissions handle the write — no additional token scope needed. Skip silently if label creation fails; continue activation. +Before the first `create-issue`, verify labels `squad` and any `squad:{agent}` exist. If missing, record them in the activation summary as a prerequisite gap (label creation requires `issues: write` + `create-label` safe-output — not configured in this workflow). Continue activation — `create-issue` will apply any existing labels normally; unavailable labels are omitted and reported, not silently applied. ##### Transient Failure Handling