Skip to content

Commit e0c84dd

Browse files
authored
Merge pull request #2197 from bradygaster/bradygaster-align-squad-gh-aw-compiler
fix(gh-aw): repair signed Cast review evidence and generation guidance
2 parents c7d8791 + 86d1c5c commit e0c84dd

12 files changed

Lines changed: 96 additions & 18 deletions
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@bradygaster/squad-cli": patch
3+
"@bradygaster/squad-sdk": patch
4+
---
5+
6+
Give the native review safe-output guard read access to same-repository workflow
7+
run evidence needed for signed Bootstrap provenance. Correct Cast generation
8+
guidance for Scribe's decision-only role and native specialist assignment.
9+
Document reviewed fresh generation rather than amending a signed Cast head.

‎docs/src/content/docs/guide/gh-aw.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,14 @@ permissions.
262262

263263
### Explicit fresh bootstrap after a failed attempt
264264

265+
Signed Cast provenance binds the exact generated head commit (`cast_sha`).
266+
Do not amend that branch or rewrite provenance to fix review findings. Correct
267+
the generation guidance in the trusted package, then use the reviewed fresh
268+
generation procedure below. The replacement Cast receives new provenance and
269+
still requires independent review and human approval. An automatic review
270+
infrastructure failure does not authorize bypassing the guard or merging the
271+
original Cast.
272+
265273
Closing an unmerged Cast PR still means **opt out**. Ordinary pushes, blank
266274
manual runs, renaming that PR, or deleting its branch do not undo that decision.
267275
To intentionally start over without deleting history, a maintainer can prepare

‎test/gh-aw-bootstrap-workflow.test.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,21 @@ afterAll(() => {
614614
});
615615

616616
describe('automatic Squad bootstrap workflow', () => {
617+
it('keeps generated support and routing guidance consistent with native contracts', () => {
618+
const dispatcher = readFileSync(resolve(ROOT, 'workflows/squad.md'), 'utf8');
619+
for (const source of [WORKFLOW, dispatcher]) {
620+
const normalized = source.replace(/\s+/g, ' ');
621+
expect(normalized).toContain('accepted durable decisions only, never session or orchestration logs');
622+
expect(normalized).toContain('`squad:{member}` labels as specialist assignment');
623+
expect(normalized).toContain('bare `squad` label is a tracking marker');
624+
expect(normalized).toContain('A name mention alone does not dispatch a native workflow');
625+
expect(source).not.toContain('Scribe logs sessions');
626+
}
627+
const guide = readFileSync(resolve(ROOT, 'docs/src/content/docs/guide/gh-aw.md'), 'utf8');
628+
expect(guide).toContain('Do not amend that branch or rewrite provenance');
629+
expect(guide.replace(/\s+/g, ' ')).toContain('reviewed fresh generation procedure');
630+
});
631+
617632
it('pins the canonical bootstrap validator digest in the authenticated runner', () => {
618633
const canonical = readFileSync(VALIDATOR);
619634
const commandDigest = WORKFLOW.match(

‎test/gh-aw-review-guard.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,27 @@ describe('independent Squad review guard', () => {
420420
.rejects.toThrow('missing or duplicate base-controlled bootstrap PR provenance');
421421
});
422422

423+
it('preserves review output when signed bootstrap run evidence is inaccessible', async () => {
424+
const f = fixture();
425+
makeBootstrap(f);
426+
const workspace = mkdtempSync(join(tmpdir(), 'squad-review-run-permission-'));
427+
workspaces.push(workspace);
428+
const path = join(workspace, 'output.json');
429+
const original = JSON.stringify({ items: [{
430+
type: 'submit_pull_request_review', event: 'COMMENT', body: 'Bootstrap Cast reviewed.',
431+
}] });
432+
writeFileSync(path, original);
433+
const get = async (route: string, fields?: Record<string, unknown>) => {
434+
if (route === `repos/${REPOSITORY}/actions/runs/29`) {
435+
throw Object.assign(new Error('Resource not accessible by integration'), { status: 403 });
436+
}
437+
return f.get(route, fields);
438+
};
439+
await expect(enforceReviewOutputs({ ...f.env, GH_AW_AGENT_OUTPUT: path }, get))
440+
.rejects.toThrow('Resource not accessible by integration');
441+
expect(readFileSync(path, 'utf8')).toBe(original);
442+
});
443+
423444
it.each([
424445
['branch', (f: ReturnType<typeof fixture>) => { f.pr.head.ref = 'attacker/bootstrap'; }],
425446
['title', (f: ReturnType<typeof fixture>) => { f.pr.title = 'Trusted bootstrap'; }],

‎test/gh-aw-review-workflow.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,8 @@ describe('gh-aw enforcing Squad reviewer', () => {
513513
expect(permissionBlock).toContain('pull-requests: read');
514514
expect(permissionBlock).toContain('copilot-requests: write');
515515
expect(permissionBlock).not.toMatch(/^\s+(contents|issues|pull-requests): write$/m);
516-
expect(safeOutputPermissions).toMatchObject({
516+
expect(safeOutputPermissions).toEqual({
517+
actions: 'read',
517518
contents: 'read',
518519
issues: 'write',
519520
'pull-requests': 'write',

‎workflows/package/squad-bootstrap.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,14 @@ sections. The coordinator has concrete Cast-source paths and one synchronized,
802802
nonzero Team Capabilities block. Remove bootstrap specialist directories not
803803
selected by this Cast, but never remove or rewrite the four built-ins.
804804

805+
In the coordinator and team introduction, describe Scribe as silently merging
806+
accepted durable decisions only, never session or orchestration logs. The
807+
canonical support charters govern when support agents run.
808+
Document `squad:{member}` labels as specialist assignment, using exact active
809+
registry names and `.squad/routing.md`. The bare `squad` label is a tracking marker,
810+
not specialist assignment. A name mention alone does not dispatch a native
811+
workflow; use documented `/squad` commands with their prerequisites.
812+
805813
## Shared payload
806814

807815
Write `.github/workflows/squad-bootstrap-payload.json` with this exact shape:

‎workflows/package/squad-review.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ safe-outputs:
7979
- REQUEST_CHANGES
8080
commit-id: ${{ github.event.pull_request.head.sha }}
8181
jobs:
82+
safe_outputs:
83+
permissions:
84+
actions: read
8285
review:
8386
name: review
8487
if: always()

‎workflows/package/squad.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2109,7 +2109,7 @@ Create/replace:
21092109
- `# Squad Coordinator` plus one short paragraph establishing that the coordinator routes work and does not replace specialist judgment.
21102110
- `## Cast sources` listing only the concrete final Cast paths for the team, routing, registry, history, policy, meet-the-squad, every active specialist member charter, and the four built-in charter paths (`.squad/agents/scribe/charter.md`, `.squad/agents/ralph/charter.md`, `.squad/agents/rai/charter.md`, `.squad/agents/fact-checker/charter.md`). Do not use path globs or dynamic paths.
21112111
- `## Routing work` with this behavior: read the routing table, select only active registry members, load only the selected member's charter, delegate through the platform's available agent mechanism, and synthesize the result for the user. If no route matches, choose the active Lead; if no active Lead exists, ask the user rather than inventing a member.
2112-
- `## Built-in Support Agents` — concise operational instructions establishing that Scribe, Ralph, Rai, and Fact Checker are mandatory always-on support agents with a fixed lifecycle role (Scribe logs sessions and merges decisions silently in the background; Ralph monitors the work queue on request; Rai reviews for RAI/content-safety concerns and can gate on 🔴 Critical findings; Fact Checker verifies claims and offers Devil's-Advocate review before risky work ships). Never present them as selectable domain specialists, never list them as routing-table destinations, and never count them toward `specialists`, `taskTypes`, or `hints`.
2112+
- `## Built-in Support Agents` — concise operational instructions establishing that Scribe, Ralph, Rai, and Fact Checker are mandatory support agents with a fixed lifecycle role (Scribe silently merges accepted durable decisions only, never session or orchestration logs; Ralph monitors the work queue on request; Rai reviews for RAI/content-safety concerns and can gate on 🔴 Critical findings; Fact Checker verifies claims and offers Devil's-Advocate review before risky work ships). Their canonical charters govern when they run. Never present them as selectable domain specialists, never list them as routing-table destinations, and never count them toward `specialists`, `taskTypes`, or `hints`.
21132113
- One complete generated Team Capabilities block delimited by `<!-- SQUAD:TEAM-CAPABILITIES:BEGIN -->` and `<!-- SQUAD:TEAM-CAPABILITIES:END -->`. Preserve the stable heading, metadata, specialist table, supported task types, routing hints, and capability boundaries format. Set `specialists` to the active registry count and set both `taskTypes` and `hints` to the routing-row count; all three counts must be nonzero and must never include the four built-ins. Generate every value from the final team, routing, registry, and active specialist charters.
21142114
21152115
The coordinator must be self-contained for the final Cast tree. It must not mention standalone lifecycle behavior, templates, configuration, decisions, plugins, logs, non-GH-AW clients, internal Squad source paths, or sample labels/names that are not active registry members. The four built-in charter references and the `## Built-in Support Agents` section are the one permitted exception to "no inactive/support roles" content — they are mandatory, not inactive, and must never be phrased as selectable specialists or routing destinations.
@@ -2119,6 +2119,7 @@ Keep naming consistent across generated team state and the Cast PR summary. In d
21192119
##### Step 5: Generate meet-the-squad.md
21202120
21212121
Create `meet-the-squad.md` at repo root with: title, naming mode (`Descriptive` in descriptive mode; otherwise the exact universe name), active team table (Name|Role|Specialty|How to talk), How to Work With Your Squad (label-based assignment with `9B8FCC` color, iteration commands, routing reference), "What Happened Here" block with analysis rationale (languages, structure, CI/CD, rationale), footer with cast date. Do not advertise the four built-ins or any other inactive/support role as a selectable specialist.
2122+
Document `squad:{member}` labels as specialist assignment, using exact active registry names and `.squad/routing.md`. The bare `squad` label is a tracking marker, not specialist assignment. A name mention alone does not dispatch a native workflow; use documented `/squad` commands with their prerequisites.
21222123
21232124
##### Step 6: Build the Safe-Output Payload
21242125

‎workflows/squad-bootstrap.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,14 @@ sections. The coordinator has concrete Cast-source paths and one synchronized,
792792
nonzero Team Capabilities block. Remove bootstrap specialist directories not
793793
selected by this Cast, but never remove or rewrite the four built-ins.
794794

795+
In the coordinator and team introduction, describe Scribe as silently merging
796+
accepted durable decisions only, never session or orchestration logs. The
797+
canonical support charters govern when support agents run.
798+
Document `squad:{member}` labels as specialist assignment, using exact active
799+
registry names and `.squad/routing.md`. The bare `squad` label is a tracking marker,
800+
not specialist assignment. A name mention alone does not dispatch a native
801+
workflow; use documented `/squad` commands with their prerequisites.
802+
795803
## Shared payload
796804

797805
Write `.github/workflows/squad-bootstrap-payload.json` with this exact shape:

‎workflows/squad-review.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ safe-outputs:
7272
allowed-events: [COMMENT, REQUEST_CHANGES]
7373
commit-id: "${{ github.event.pull_request.head.sha }}"
7474
jobs:
75+
safe_outputs:
76+
permissions:
77+
actions: read
7578
review:
7679
name: review
7780
if: always()

0 commit comments

Comments
 (0)