From e10f8668c6eb2cd04017338b0c8ac8b93f6c7850 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Fri, 2 Oct 2026 04:39:43 -0700 Subject: [PATCH 1/3] fix(gh-aw): remove mutable generated router Reject gh-aw's unowned mutable agentic-workflows skill in final and staged installation verification while preserving the exact Squad enlistment skill. Finding: octodemo/zava-social-queue-worker-20261002092250#1 comment 4164580830 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .changeset/remove-mutable-gh-aw-router.md | 6 ++ .github/agents.md | 6 ++ .github/skills/agentic-workflows/SKILL.md | 98 ------------------- .github/workflows/squad-ci.yml | 1 + .../skills/gh-aw-enlistment/SKILL.md | 29 +++++- README.md | 7 +- docs/src/content/docs/guide/gh-aw.md | 37 +++++-- .../skills/gh-aw-enlistment/SKILL.md | 29 +++++- .../skills/gh-aw-enlistment/SKILL.md | 29 +++++- scripts/gh-aw-hosted-e2e.mjs | 1 + templates/skills/gh-aw-enlistment/SKILL.md | 29 +++++- test/gh-aw-enlistment-skill.test.ts | 19 ++++ test/gh-aw-hosted-e2e.test.ts | 4 +- test/gh-aw-install-staging.test.ts | 63 +++++++++++- test/gh-aw-quality.test.ts | 9 ++ workflows/package/squad-bootstrap.md | 2 +- workflows/shared/squad-install-verifier.mjs | 23 ++++- workflows/skills/gh-aw-enlistment/SKILL.md | 29 +++++- workflows/squad-bootstrap.md | 2 +- workflows/squad-workflows.manifest.json | 10 +- 20 files changed, 302 insertions(+), 131 deletions(-) create mode 100644 .changeset/remove-mutable-gh-aw-router.md delete mode 100644 .github/skills/agentic-workflows/SKILL.md diff --git a/.changeset/remove-mutable-gh-aw-router.md b/.changeset/remove-mutable-gh-aw-router.md new file mode 100644 index 000000000..0b0800362 --- /dev/null +++ b/.changeset/remove-mutable-gh-aw-router.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Remove gh-aw's generated mutable `agentic-workflows` router during Squad enlistment and fail installation verification when that unowned skill remains. diff --git a/.github/agents.md b/.github/agents.md index 97c107503..4fbb66ac6 100644 --- a/.github/agents.md +++ b/.github/agents.md @@ -50,8 +50,14 @@ The quick start installs this workflow set: ```bash SQUAD_SHA="<40-character-commit-sha>" # explicit, maintainer-approved; never `commits/dev` gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md ``` +The removal is intentional: gh-aw v0.89.22 generates that generic tool-owned +router, which loads mutable prompts from the current `github/gh-aw` repository. +It is not Squad-owned or bound to `SQUAD_SHA`. Keep the exact +`.github/skills/gh-aw-enlistment/SKILL.md` installed by Squad. + This command: 1. Fetches the Squad dispatcher, general and dependency workers, independent reviewer with a required-check gate, retrospective, and approval-gated improvement worker diff --git a/.github/skills/agentic-workflows/SKILL.md b/.github/skills/agentic-workflows/SKILL.md deleted file mode 100644 index 498d3096e..000000000 --- a/.github/skills/agentic-workflows/SKILL.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -name: agentic-workflows -description: Route gh-aw workflow design/create/debug/upgrade requests to the right prompts. ---- - -# Agentic Workflows Router - -Use this skill when a user asks to design, create, update, debug, or upgrade GitHub Agentic Workflows in this repository. - -This skill is a dispatcher: identify the task type, load the matching workflow prompt/skill file, and follow it directly. Keep responses concise and ask a clarifying question if the correct prompt is unclear. - -Read only the files you need: -Load these files from `github/gh-aw` (they are not available locally). -- `.github/aw/action-container-substitutions.md` -- `.github/aw/agentic-chat.md` -- `.github/aw/agentic-workflows-mcp.md` -- `.github/aw/asciicharts.md` -- `.github/aw/campaign.md` -- `.github/aw/charts-trending.md` -- `.github/aw/charts.md` -- `.github/aw/cli-commands.md` -- `.github/aw/configure-agentic-engine.md` -- `.github/aw/context.md` -- `.github/aw/create-agentic-workflow-trigger-details.md` -- `.github/aw/create-agentic-workflow.md` -- `.github/aw/create-shared-agentic-workflow.md` -- `.github/aw/debug-agentic-workflow.md` -- `.github/aw/dependabot.md` -- `.github/aw/deployment-status.md` -- `.github/aw/designer.md` -- `.github/aw/evals.md` -- `.github/aw/experiments.md` -- `.github/aw/github-agentic-workflows.md` -- `.github/aw/github-mcp-server-pagination.md` -- `.github/aw/github-mcp-server.md` -- `.github/aw/instructions.md` -- `.github/aw/linter-workflows.md` -- `.github/aw/llms.md` -- `.github/aw/loop.md` -- `.github/aw/lsp.md` -- `.github/aw/mcp-clis.md` -- `.github/aw/memory-stateful-patterns.md` -- `.github/aw/memory.md` -- `.github/aw/messages.md` -- `.github/aw/multi-agent-research.md` -- `.github/aw/network.md` -- `.github/aw/optimize-agentic-workflow.md` -- `.github/aw/patterns.md` -- `.github/aw/pr-reviewer.md` -- `.github/aw/release-workflow.md` -- `.github/aw/report.md` -- `.github/aw/reuse.md` -- `.github/aw/safe-outputs-automation.md` -- `.github/aw/safe-outputs-content.md` -- `.github/aw/safe-outputs-management.md` -- `.github/aw/safe-outputs-runtime.md` -- `.github/aw/safe-outputs.md` -- `.github/aw/serena-tool.md` -- `.github/aw/shared-safe-jobs.md` -- `.github/aw/skills.md` -- `.github/aw/subagents.md` -- `.github/aw/syntax-agentic.md` -- `.github/aw/syntax-core.md` -- `.github/aw/syntax-engine.md` -- `.github/aw/syntax-tools-imports.md` -- `.github/aw/syntax.md` -- `.github/aw/test-coverage.md` -- `.github/aw/test-expression.md` -- `.github/aw/token-optimization-caching-budgets.md` -- `.github/aw/token-optimization-observability.md` -- `.github/aw/token-optimization.md` -- `.github/aw/triggers.md` -- `.github/aw/update-agentic-workflow.md` -- `.github/aw/upgrade-agentic-workflows.md` -- `.github/aw/visual-regression.md` -- `.github/aw/workflow-constraints.md` -- `.github/aw/workflow-editing.md` -- `.github/aw/workflow-patterns.md` - -- `.github/skills/agentic-workflow-designer/SKILL.md` -After loading the matching workflow prompt or skill, follow it directly: -- Enlist a repository into Squad via gh-aw (install the supported `/squad` workflow bootstrap): `.squad/skills/gh-aw-enlistment/SKILL.md` — **this one is local to this repository**, not fetched from `github/gh-aw`. Use for "set up Squad agentic workflows", "enlist this repo in Squad", or "install Squad gh-aw workflows" -- Design workflows from scratch via interview: `skills/agentic-workflow-designer/SKILL.md` -- Create new workflows: `.github/aw/create-agentic-workflow.md` -- Update existing workflows: `.github/aw/update-agentic-workflow.md` -- Debug, audit, or investigate workflows: `.github/aw/debug-agentic-workflow.md` -- Validate workflow contracts and safe outputs: `.github/skills/gh-aw-reliability/SKILL.md` -- Upgrade workflows and fix deprecations: `.github/aw/upgrade-agentic-workflows.md` -- Create shared components or MCP wrappers: `.github/aw/create-shared-agentic-workflow.md` -- Create report-generating workflows: `.github/aw/report.md` -- Fix Dependabot manifest PRs: `.github/aw/dependabot.md` -- Analyze coverage workflows: `.github/aw/test-coverage.md` -- Render compact markdown charts: `.github/aw/asciicharts.md` -- Map CLI commands to MCP usage: `.github/aw/cli-commands.md` -- Choose workflow architecture and patterns: `.github/aw/patterns.md` -- Optimize token usage and cost: `.github/aw/token-optimization.md` - -When the task involves OTEL, OTLP, traces, observability backends, or telemetry-driven analysis, consult the relevant agentic workflow patterns for observability (the `skills/otel-queries/` skill is planned but not yet available). diff --git a/.github/workflows/squad-ci.yml b/.github/workflows/squad-ci.yml index dcf619f0a..777a3a7b4 100644 --- a/.github/workflows/squad-ci.yml +++ b/.github/workflows/squad-ci.yml @@ -784,6 +784,7 @@ jobs: ".github/workflows/${workflow}.md" done gh aw add --force "$GITHUB_WORKSPACE/workflows" + rm -f .github/skills/agentic-workflows/SKILL.md node .github/workflows/shared/squad-install-verifier.mjs \ --write-local-test-ownership \ --source-revision "$SQUAD_SOURCE_REVISION" diff --git a/.squad-templates/skills/gh-aw-enlistment/SKILL.md b/.squad-templates/skills/gh-aw-enlistment/SKILL.md index 77a5b4bd0..0a5b4eef9 100644 --- a/.squad-templates/skills/gh-aw-enlistment/SKILL.md +++ b/.squad-templates/skills/gh-aw-enlistment/SKILL.md @@ -194,6 +194,7 @@ published release guidance, then set it once: exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md ``` The nested `workflows/aw.yml` is the only supported distribution registration. @@ -216,6 +217,15 @@ governance-scoped retrospective proposal (see the gh-aw guide's retrospective auto-implementation section); installing it alongside the other seven keeps the full stack consistent and avoids a second bootstrap pass later. +gh-aw v0.89.22 also materializes +`.github/skills/agentic-workflows/SKILL.md`. That generic tool-owned router is +not part of the Squad package and directs agents to mutable prompts from the +current `github/gh-aw` repository rather than the pinned Squad revision. Remove +that exact file after every `gh aw add`. Keep +`.github/skills/gh-aw-enlistment/SKILL.md`: it is the one Squad-owned skill and +the verifier requires its exact package bytes. Do not adopt or vendor the rest +of gh-aw's generic scaffold. + Report/proposal-only is the default. Ordinary fixes require the explicit `"squadRetroAutoImplement": "allow"` setting in `.squad/config.json`; five action issues and three dispatches per wake-up remain separate caps. @@ -337,11 +347,20 @@ git add -- .gitattributes .github/aw/ .github/workflows/ .github/skills/ node .github/workflows/shared/squad-install-verifier.mjs \ --verify-staged-install --stage-ownership --source-revision "${SQUAD_SHA}" || exit 1 git diff --cached --stat -# No deletions should be staged: -test -z "$(git diff --cached --diff-filter=D --name-only)" || { echo "STOP: staged deletions"; exit 1; } +unexpected_deletions="$( + git diff --cached --diff-filter=D --name-only | + grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true +)" +test -z "${unexpected_deletions}" || { + printf 'STOP: unexpected staged deletions:\n%s\n' "${unexpected_deletions}" >&2 + exit 1 +} ``` -- **STOP** if the staged diff shows **unexpected deletions**, **unexpected secrets**, +- The only permitted staged deletion is + `.github/skills/agentic-workflows/SKILL.md`, when upgrading a repository that + previously committed gh-aw's mutable router. +- **STOP** if the staged diff shows any other **unexpected deletions**, **unexpected secrets**, edits to **unrelated files**, or committed **log/diagnostic output**. Re-scope with explicit `git add -- ` — never `git add .`, `git add -A`, or `git commit -a`. @@ -461,6 +480,7 @@ git switch -c chore/squad-gh-aw-bootstrap exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md # Safe-update report shows ONLY the two documented secrets + squad-init → approve once gh aw compile --strict --approve @@ -517,6 +537,9 @@ gh pr merge --squash # auto-merge before human review. NEVER. before installation if repository administration permission is unavailable. - ❌ **Blanket staging** (`git add .` / `-A` / `git commit -a`). Stage only `.gitattributes`, `.github/aw/`, `.github/workflows/`, `.github/skills/`, by path. +- ❌ **Committing gh-aw's mutable router.** Remove only + `.github/skills/agentic-workflows/SKILL.md`; keep the exact Squad-owned + `.github/skills/gh-aw-enlistment/SKILL.md`. - ❌ **Approving unknown safe updates.** Approve ONLY `SQUAD_GITHUB_APP_PRIVATE_KEY`, `SQUAD_GITHUB_TOKEN`, and `bradygaster/squad/.github/actions/squad-init`. Anything else is a STOP. diff --git a/README.md b/README.md index d61051d5c..b6614aa07 100644 --- a/README.md +++ b/README.md @@ -588,6 +588,7 @@ test "$(gh api "repos/${owner_repo}" --jq '.has_issues')" = "true" || { SQUAD_SHA="<40-character-commit-sha>" # explicit, maintainer-approved; never `commits/dev` gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md git add -- \ .github/aw/ \ .github/skills/ \ @@ -606,12 +607,16 @@ Review the complete generated diff before you commit: |------|-------------------|---------| | `.github/workflows/` | The Squad workflow sources, shared imports, compiled lock files, and `agentics-maintenance.yml` | Yes | | `.github/aw/` | Supporting gh-aw state, including pinned action versions and SHAs | Yes | -| `.github/skills/` | The agentic-workflows dispatcher skill | Yes | +| `.github/skills/` | The exact Squad-owned `gh-aw-enlistment` skill; remove gh-aw's generated mutable `agentic-workflows` router before staging | Yes | | `.gitattributes` | Marks compiled `.lock.yml` workflows as generated | Yes | | `.vscode/` | Workspace settings that enable GitHub Copilot for Markdown files in VS Code | Optional — commit only if you want to share this workspace setting | `agentics-maintenance.yml` is a second installed workflow. Squad configures its created pull request safe output to expire after 14 days, so this workflow runs scheduled expiration cleanup and also exposes manual maintenance operations. To omit it, create `.github/workflows/aw.json` with `{"maintenance": false}` before installing. gh-aw then warns that expiration is disabled and removes the maintenance workflow. +Unlike the mutable router skill, `agentics-maintenance.yml` is compiled runtime +output required for the configured 14-day safe-output expiration behavior. Keep +it unless you explicitly disable maintenance before installation. + #### Retrospective auto-implementation (opt-in behavior) `squad-improvement-worker` installs as part of the standard seven-workflow diff --git a/docs/src/content/docs/guide/gh-aw.md b/docs/src/content/docs/guide/gh-aw.md index 9db1ac464..71ca1f445 100644 --- a/docs/src/content/docs/guide/gh-aw.md +++ b/docs/src/content/docs/guide/gh-aw.md @@ -90,6 +90,7 @@ SQUAD_SHA="<40-character-commit-sha>" } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md # 5. On first install, review the safe-update report. # If it contains only the documented Squad secrets and init action, approve it: @@ -111,7 +112,11 @@ git add -- .gitattributes .github/aw/ .github/workflows/ .github/skills/ node .github/workflows/shared/squad-install-verifier.mjs \ --verify-staged-install --stage-ownership --source-revision "${SQUAD_SHA}" || exit 1 git diff --cached --stat -test -z "$(git diff --cached --diff-filter=D --name-only)" +unexpected_deletions="$( + git diff --cached --diff-filter=D --name-only | + grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true +)" +test -z "${unexpected_deletions}" git commit -m "ci: add Squad agentic workflow" git push -u origin HEAD gh pr create \ @@ -158,8 +163,10 @@ PR could merge but the bootstrap journey could not complete. Reading that update fails, stop before installation and ask an administrator to enable **Settings → General → Features → Issues**, then rerun the quick start. -> Step 7 stages `.github/skills/` because `gh aw add` installs the Squad skills -> alongside the workflows, and it deliberately does not stage `.github/aw/logs/`. +> Step 7 stages `.github/skills/` because `gh aw add` installs the Squad skill +> alongside the workflows and may need to stage deletion of gh-aw's unowned +> `.github/skills/agentic-workflows/SKILL.md`. It deliberately does not stage +> `.github/aw/logs/`. > Downloaded workflow logs are local diagnostic output — see [ignoring downloaded > logs](#open-the-bootstrap-pull-request) before you commit. @@ -336,6 +343,7 @@ SQUAD_SHA="<40-character-commit-sha>" exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md ``` The nested `workflows/aw.yml` is the canonical package registration. Its @@ -374,8 +382,14 @@ add-on — it stays dormant until a maintainer approves a governance-scoped retrospective proposal (see [Retrospective auto-implementation](#retrospective-auto-implementation-opt-in) below). -`gh aw add` also installs the Squad skills under `.github/skills/`, which is why -the bootstrap commit stages that path alongside the workflows. +`gh aw add` also installs the Squad skill under `.github/skills/`, which is why +the bootstrap commit stages that path alongside the workflows. gh-aw v0.89.22 +additionally generates `.github/skills/agentic-workflows/SKILL.md`. That +tool-owned router loads mutable prompt files from the current `github/gh-aw` +repository and is not bound to the pinned Squad revision or compiler version. +Remove that exact file after every install. Do not vendor the generic gh-aw +prompt corpus. The verifier rejects the router if it remains and still requires +the exact Squad-owned `.github/skills/gh-aw-enlistment/SKILL.md`. > **Revision note:** `SQUAD_SHA` is an explicit, maintainer-approved commit — > never resolved from `dev`'s moving tip. The package install itself uses only @@ -479,7 +493,11 @@ git add -- .gitattributes .github/aw/ .github/workflows/ .github/skills/ node .github/workflows/shared/squad-install-verifier.mjs \ --verify-staged-install --stage-ownership --source-revision "${SQUAD_SHA}" || exit 1 git diff --cached --stat -test -z "$(git diff --cached --diff-filter=D --name-only)" +unexpected_deletions="$( + git diff --cached --diff-filter=D --name-only | + grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true +)" +test -z "${unexpected_deletions}" git commit -m "ci: add Squad agentic workflow" git push -u origin HEAD gh pr create \ @@ -495,6 +513,10 @@ state under `.github/aw/`, the installed skills, and `.gitattributes`. Review the complete generated diff in the bootstrap PR, address Copilot review feedback, and wait for required checks. Merge only after human approval. +The exact router deletion above is the only permitted staged deletion, for an +upgrade from a repository that previously committed it. Any other deletion is +a hard stop. + Consumer ignore rules such as `packages/` can silently omit the required `.github/aw/packages/` ownership JSON from directory staging. The staged verifier force-adds only the exact native package ownership JSON when ignored and @@ -575,7 +597,7 @@ Use this checklist for the initial bootstrap and after any workflow update: | Stage | Action | Expected evidence | |-------|--------|-------------------| | Repository readiness | Confirm `.has_issues` is `true`; if it is `false`, enable it before installing workflows | GitHub Issues are available for `/squad` comments and the bootstrap research/proposals issue; insufficient administration permission stops the install before a bootstrap PR is created | -| Install | Run the eight-workflow `gh aw add` command on a bootstrap branch | All eight `.md`/`.lock.yml` pairs exist, with shared imports, `.github/aw/`, installed skills, and `.gitattributes` included in the diff | +| Install | Run the eight-workflow `gh aw add` command on a bootstrap branch, then remove the exact tool-owned mutable router | All eight `.md`/`.lock.yml` pairs exist, with shared imports, `.github/aw/`, the exact Squad-owned `gh-aw-enlistment` skill, and `.gitattributes` included in the diff; `.github/skills/agentic-workflows/SKILL.md` is absent | | Compile | Review any first-install safe-update report, approve only the documented entries, then run `gh aw compile --strict` without approval | All eight workflows succeed, only documented warnings remain, and all sixteen source/lock files exist | | Bootstrap review | Open the installation PR, request `@copilot`, inspect verifier/compile evidence, and merge only after human approval | The run reports the explicit first-install manual boundary; no `Squad-Review-Verdict:` record or PR-controlled check is treated as trusted | | Activation canary | Merge the workflow-installation PR and inspect the Cast PR — automatically opened, or manually opened by a human from the bootstrap fallback issue's compare-URL link when `can_approve_pull_request_reviews=false` blocks direct creation | `Squad Review / review` succeeds with reserved bootstrap roles only after loading the guard and manifest from the exact base commit and validating the default-branch bootstrap run — including, for a manually opened PR, the signed fallback-issue provenance record | @@ -1919,6 +1941,7 @@ same native package as one unit: SQUAD_SHA="<40-character-commit-sha>" gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" --force +rm -f .github/skills/agentic-workflows/SKILL.md node .github/workflows/shared/squad-install-verifier.mjs --materialize-runtime gh aw compile --strict node .github/workflows/shared/squad-install-verifier.mjs \ diff --git a/packages/squad-cli/templates/skills/gh-aw-enlistment/SKILL.md b/packages/squad-cli/templates/skills/gh-aw-enlistment/SKILL.md index 77a5b4bd0..0a5b4eef9 100644 --- a/packages/squad-cli/templates/skills/gh-aw-enlistment/SKILL.md +++ b/packages/squad-cli/templates/skills/gh-aw-enlistment/SKILL.md @@ -194,6 +194,7 @@ published release guidance, then set it once: exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md ``` The nested `workflows/aw.yml` is the only supported distribution registration. @@ -216,6 +217,15 @@ governance-scoped retrospective proposal (see the gh-aw guide's retrospective auto-implementation section); installing it alongside the other seven keeps the full stack consistent and avoids a second bootstrap pass later. +gh-aw v0.89.22 also materializes +`.github/skills/agentic-workflows/SKILL.md`. That generic tool-owned router is +not part of the Squad package and directs agents to mutable prompts from the +current `github/gh-aw` repository rather than the pinned Squad revision. Remove +that exact file after every `gh aw add`. Keep +`.github/skills/gh-aw-enlistment/SKILL.md`: it is the one Squad-owned skill and +the verifier requires its exact package bytes. Do not adopt or vendor the rest +of gh-aw's generic scaffold. + Report/proposal-only is the default. Ordinary fixes require the explicit `"squadRetroAutoImplement": "allow"` setting in `.squad/config.json`; five action issues and three dispatches per wake-up remain separate caps. @@ -337,11 +347,20 @@ git add -- .gitattributes .github/aw/ .github/workflows/ .github/skills/ node .github/workflows/shared/squad-install-verifier.mjs \ --verify-staged-install --stage-ownership --source-revision "${SQUAD_SHA}" || exit 1 git diff --cached --stat -# No deletions should be staged: -test -z "$(git diff --cached --diff-filter=D --name-only)" || { echo "STOP: staged deletions"; exit 1; } +unexpected_deletions="$( + git diff --cached --diff-filter=D --name-only | + grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true +)" +test -z "${unexpected_deletions}" || { + printf 'STOP: unexpected staged deletions:\n%s\n' "${unexpected_deletions}" >&2 + exit 1 +} ``` -- **STOP** if the staged diff shows **unexpected deletions**, **unexpected secrets**, +- The only permitted staged deletion is + `.github/skills/agentic-workflows/SKILL.md`, when upgrading a repository that + previously committed gh-aw's mutable router. +- **STOP** if the staged diff shows any other **unexpected deletions**, **unexpected secrets**, edits to **unrelated files**, or committed **log/diagnostic output**. Re-scope with explicit `git add -- ` — never `git add .`, `git add -A`, or `git commit -a`. @@ -461,6 +480,7 @@ git switch -c chore/squad-gh-aw-bootstrap exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md # Safe-update report shows ONLY the two documented secrets + squad-init → approve once gh aw compile --strict --approve @@ -517,6 +537,9 @@ gh pr merge --squash # auto-merge before human review. NEVER. before installation if repository administration permission is unavailable. - ❌ **Blanket staging** (`git add .` / `-A` / `git commit -a`). Stage only `.gitattributes`, `.github/aw/`, `.github/workflows/`, `.github/skills/`, by path. +- ❌ **Committing gh-aw's mutable router.** Remove only + `.github/skills/agentic-workflows/SKILL.md`; keep the exact Squad-owned + `.github/skills/gh-aw-enlistment/SKILL.md`. - ❌ **Approving unknown safe updates.** Approve ONLY `SQUAD_GITHUB_APP_PRIVATE_KEY`, `SQUAD_GITHUB_TOKEN`, and `bradygaster/squad/.github/actions/squad-init`. Anything else is a STOP. diff --git a/packages/squad-sdk/templates/skills/gh-aw-enlistment/SKILL.md b/packages/squad-sdk/templates/skills/gh-aw-enlistment/SKILL.md index 77a5b4bd0..0a5b4eef9 100644 --- a/packages/squad-sdk/templates/skills/gh-aw-enlistment/SKILL.md +++ b/packages/squad-sdk/templates/skills/gh-aw-enlistment/SKILL.md @@ -194,6 +194,7 @@ published release guidance, then set it once: exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md ``` The nested `workflows/aw.yml` is the only supported distribution registration. @@ -216,6 +217,15 @@ governance-scoped retrospective proposal (see the gh-aw guide's retrospective auto-implementation section); installing it alongside the other seven keeps the full stack consistent and avoids a second bootstrap pass later. +gh-aw v0.89.22 also materializes +`.github/skills/agentic-workflows/SKILL.md`. That generic tool-owned router is +not part of the Squad package and directs agents to mutable prompts from the +current `github/gh-aw` repository rather than the pinned Squad revision. Remove +that exact file after every `gh aw add`. Keep +`.github/skills/gh-aw-enlistment/SKILL.md`: it is the one Squad-owned skill and +the verifier requires its exact package bytes. Do not adopt or vendor the rest +of gh-aw's generic scaffold. + Report/proposal-only is the default. Ordinary fixes require the explicit `"squadRetroAutoImplement": "allow"` setting in `.squad/config.json`; five action issues and three dispatches per wake-up remain separate caps. @@ -337,11 +347,20 @@ git add -- .gitattributes .github/aw/ .github/workflows/ .github/skills/ node .github/workflows/shared/squad-install-verifier.mjs \ --verify-staged-install --stage-ownership --source-revision "${SQUAD_SHA}" || exit 1 git diff --cached --stat -# No deletions should be staged: -test -z "$(git diff --cached --diff-filter=D --name-only)" || { echo "STOP: staged deletions"; exit 1; } +unexpected_deletions="$( + git diff --cached --diff-filter=D --name-only | + grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true +)" +test -z "${unexpected_deletions}" || { + printf 'STOP: unexpected staged deletions:\n%s\n' "${unexpected_deletions}" >&2 + exit 1 +} ``` -- **STOP** if the staged diff shows **unexpected deletions**, **unexpected secrets**, +- The only permitted staged deletion is + `.github/skills/agentic-workflows/SKILL.md`, when upgrading a repository that + previously committed gh-aw's mutable router. +- **STOP** if the staged diff shows any other **unexpected deletions**, **unexpected secrets**, edits to **unrelated files**, or committed **log/diagnostic output**. Re-scope with explicit `git add -- ` — never `git add .`, `git add -A`, or `git commit -a`. @@ -461,6 +480,7 @@ git switch -c chore/squad-gh-aw-bootstrap exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md # Safe-update report shows ONLY the two documented secrets + squad-init → approve once gh aw compile --strict --approve @@ -517,6 +537,9 @@ gh pr merge --squash # auto-merge before human review. NEVER. before installation if repository administration permission is unavailable. - ❌ **Blanket staging** (`git add .` / `-A` / `git commit -a`). Stage only `.gitattributes`, `.github/aw/`, `.github/workflows/`, `.github/skills/`, by path. +- ❌ **Committing gh-aw's mutable router.** Remove only + `.github/skills/agentic-workflows/SKILL.md`; keep the exact Squad-owned + `.github/skills/gh-aw-enlistment/SKILL.md`. - ❌ **Approving unknown safe updates.** Approve ONLY `SQUAD_GITHUB_APP_PRIVATE_KEY`, `SQUAD_GITHUB_TOKEN`, and `bradygaster/squad/.github/actions/squad-init`. Anything else is a STOP. diff --git a/scripts/gh-aw-hosted-e2e.mjs b/scripts/gh-aw-hosted-e2e.mjs index fc8ec183e..d610bacdf 100644 --- a/scripts/gh-aw-hosted-e2e.mjs +++ b/scripts/gh-aw-hosted-e2e.mjs @@ -1006,6 +1006,7 @@ function hosted(args, repositoryRoot) { allowGitHubToken: true, env: { GH_TOKEN: sourceReadToken }, }); + rmSync(resolve(checkout, '.github/skills/agentic-workflows/SKILL.md'), { force: true }); runChild('node', ['.github/workflows/shared/squad-install-verifier.mjs', '--materialize-runtime'], { cwd: checkout, capture: false, diff --git a/templates/skills/gh-aw-enlistment/SKILL.md b/templates/skills/gh-aw-enlistment/SKILL.md index 77a5b4bd0..0a5b4eef9 100644 --- a/templates/skills/gh-aw-enlistment/SKILL.md +++ b/templates/skills/gh-aw-enlistment/SKILL.md @@ -194,6 +194,7 @@ published release guidance, then set it once: exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md ``` The nested `workflows/aw.yml` is the only supported distribution registration. @@ -216,6 +217,15 @@ governance-scoped retrospective proposal (see the gh-aw guide's retrospective auto-implementation section); installing it alongside the other seven keeps the full stack consistent and avoids a second bootstrap pass later. +gh-aw v0.89.22 also materializes +`.github/skills/agentic-workflows/SKILL.md`. That generic tool-owned router is +not part of the Squad package and directs agents to mutable prompts from the +current `github/gh-aw` repository rather than the pinned Squad revision. Remove +that exact file after every `gh aw add`. Keep +`.github/skills/gh-aw-enlistment/SKILL.md`: it is the one Squad-owned skill and +the verifier requires its exact package bytes. Do not adopt or vendor the rest +of gh-aw's generic scaffold. + Report/proposal-only is the default. Ordinary fixes require the explicit `"squadRetroAutoImplement": "allow"` setting in `.squad/config.json`; five action issues and three dispatches per wake-up remain separate caps. @@ -337,11 +347,20 @@ git add -- .gitattributes .github/aw/ .github/workflows/ .github/skills/ node .github/workflows/shared/squad-install-verifier.mjs \ --verify-staged-install --stage-ownership --source-revision "${SQUAD_SHA}" || exit 1 git diff --cached --stat -# No deletions should be staged: -test -z "$(git diff --cached --diff-filter=D --name-only)" || { echo "STOP: staged deletions"; exit 1; } +unexpected_deletions="$( + git diff --cached --diff-filter=D --name-only | + grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true +)" +test -z "${unexpected_deletions}" || { + printf 'STOP: unexpected staged deletions:\n%s\n' "${unexpected_deletions}" >&2 + exit 1 +} ``` -- **STOP** if the staged diff shows **unexpected deletions**, **unexpected secrets**, +- The only permitted staged deletion is + `.github/skills/agentic-workflows/SKILL.md`, when upgrading a repository that + previously committed gh-aw's mutable router. +- **STOP** if the staged diff shows any other **unexpected deletions**, **unexpected secrets**, edits to **unrelated files**, or committed **log/diagnostic output**. Re-scope with explicit `git add -- ` — never `git add .`, `git add -A`, or `git commit -a`. @@ -461,6 +480,7 @@ git switch -c chore/squad-gh-aw-bootstrap exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md # Safe-update report shows ONLY the two documented secrets + squad-init → approve once gh aw compile --strict --approve @@ -517,6 +537,9 @@ gh pr merge --squash # auto-merge before human review. NEVER. before installation if repository administration permission is unavailable. - ❌ **Blanket staging** (`git add .` / `-A` / `git commit -a`). Stage only `.gitattributes`, `.github/aw/`, `.github/workflows/`, `.github/skills/`, by path. +- ❌ **Committing gh-aw's mutable router.** Remove only + `.github/skills/agentic-workflows/SKILL.md`; keep the exact Squad-owned + `.github/skills/gh-aw-enlistment/SKILL.md`. - ❌ **Approving unknown safe updates.** Approve ONLY `SQUAD_GITHUB_APP_PRIVATE_KEY`, `SQUAD_GITHUB_TOKEN`, and `bradygaster/squad/.github/actions/squad-init`. Anything else is a STOP. diff --git a/test/gh-aw-enlistment-skill.test.ts b/test/gh-aw-enlistment-skill.test.ts index 35e91876b..8aec33f0b 100644 --- a/test/gh-aw-enlistment-skill.test.ts +++ b/test/gh-aw-enlistment-skill.test.ts @@ -272,6 +272,17 @@ describe('gh-aw-enlistment skill', () => { expect(content).toContain('stale source/resource digest'); }); + it('removes only gh-aw\'s mutable router and preserves the exact Squad skill', () => { + expect(content.match(/rm -f \.github\/skills\/agentic-workflows\/SKILL\.md/g)) + .toHaveLength(2); + expect(content).toContain('not part of the Squad package'); + expect(content).toContain('Do not adopt or vendor the rest'); + expect(content).toContain('.github/skills/gh-aw-enlistment/SKILL.md'); + expect(content).toContain( + "grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true", + ); + }); + it('requires a final strict compile without --approve', () => { // The standalone command must appear as its own line (start-of-line in a // fenced bash block), not merely as a prose/backtick mention. The @@ -431,6 +442,14 @@ describe('gh-aw-enlistment skill', () => { .toHaveLength(2); }); + it('removes the mutable gh-aw router before verification and permits only that staged deletion', () => { + expect(guide.match(/rm -f \.github\/skills\/agentic-workflows\/SKILL\.md/g)) + .toHaveLength(3); + expect(guide).toContain('The exact router deletion above is the only permitted staged deletion'); + expect(guide).toContain('.github/skills/gh-aw-enlistment/SKILL.md'); + expect(agentGuide).toContain('rm -f .github/skills/agentic-workflows/SKILL.md'); + }); + it('requires agents to execute the complete quick start through PR creation', () => { expect(guide).toContain('**Agent action required:**'); expect(guide).toContain('the agent must execute this entire block'); diff --git a/test/gh-aw-hosted-e2e.test.ts b/test/gh-aw-hosted-e2e.test.ts index 79a1c1a78..8c8a72cd4 100644 --- a/test/gh-aw-hosted-e2e.test.ts +++ b/test/gh-aw-hosted-e2e.test.ts @@ -198,6 +198,9 @@ describe('Squad gh-aw hosted E2E controller', () => { it('verifies and narrowly repairs staged ownership metadata before the installation commit', () => { const gate = SCRIPT.indexOf('const staged = verifyStagedInstall(checkout,'); const commit = SCRIPT.indexOf("['commit', '-m', 'ci: install Squad agentic workflows']"); + const removal = SCRIPT.indexOf("'.github/skills/agentic-workflows/SKILL.md'"); + expect(removal).toBeGreaterThan(SCRIPT.indexOf("['aw', 'add'")); + expect(removal).toBeLessThan(gate); expect(gate).toBeGreaterThan(SCRIPT.indexOf("['add', '--', ...installPaths]")); expect(gate).toBeLessThan(commit); expect(SCRIPT.slice(gate, commit)).toContain('stageOwnership: true'); @@ -1211,4 +1214,3 @@ describe('Squad gh-aw hosted E2E manual pull request fallback outcome', () => { }); }); - diff --git a/test/gh-aw-install-staging.test.ts b/test/gh-aw-install-staging.test.ts index 670a49add..7de5023e4 100644 --- a/test/gh-aw-install-staging.test.ts +++ b/test/gh-aw-install-staging.test.ts @@ -6,6 +6,7 @@ import { dirname, join, resolve } from 'node:path'; import { CONTRACT_DESTINATION, OWNERSHIP_DESTINATION, + UNOWNED_MUTABLE_ROUTER_SKILL, materializeRuntime, verifyInstall, verifyStagedInstall, @@ -53,6 +54,17 @@ function stagedPaths(root: string): string[] { return git(root, 'ls-files', '-z').split('\0').filter(Boolean).sort(); } +function seedUnownedMutableRouter(root: string): void { + write(root, UNOWNED_MUTABLE_ROUTER_SKILL, [ + '---', + 'name: agentic-workflows', + '---', + '', + 'Load mutable prompts from the current github/gh-aw repository.', + '', + ].join('\n')); +} + afterEach(() => { vi.clearAllMocks(); for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); @@ -89,7 +101,9 @@ describe('gh-aw: verified ownership staging under consumer ignore rules', () => expect(verifyStagedInstall(root, { expectedRevision: revision, stageOwnership: true }).failures).toEqual([]); const queries = spawn.mock.calls.filter(([command, args]) => command === 'git' && args?.[0] === 'ls-tree'); expect(queries).toHaveLength(1); - expect(queries[0][1]).toEqual(['ls-tree', '-z', expect.any(String), '--', ...new Set(required)]); + expect(queries[0][1]).toEqual([ + 'ls-tree', '-z', expect.any(String), '--', ...new Set(required), UNOWNED_MUTABLE_ROUTER_SKILL, + ]); const unrelatedObject = git(root, 'rev-parse', `HEAD:${unrelated}/ordinary-file-00000.txt`).trim(); const blobReads = spawn.mock.calls.filter(([command, args]) => command === 'git' && args?.[0] === 'cat-file'); expect(blobReads).toHaveLength(new Set(required).size); @@ -136,6 +150,53 @@ describe('gh-aw: verified ownership staging under consumer ignore rules', () => expect(failed.stderr).toMatch(/ownership record|ownership metadata/); }); + it('rejects the clean-install gh-aw router until it is removed, then accepts the exact Squad skill', () => { + const root = consumer(); + seedUnownedMutableRouter(root); + const args = [verifier, '--verify-install', '--source-revision', revision]; + const rejected = spawnSync(process.execPath, args, { cwd: root, encoding: 'utf8' }); + expect(rejected.status).toBe(1); + expect(rejected.stderr).toContain( + `Unowned mutable gh-aw router skill must be removed: ${UNOWNED_MUTABLE_ROUTER_SKILL}`, + ); + + rmSync(join(root, UNOWNED_MUTABLE_ROUTER_SKILL)); + const accepted = spawnSync(process.execPath, args, { cwd: root, encoding: 'utf8' }); + expect(accepted.status, accepted.stderr).toBe(0); + expect(readFileSync(join(root, '.github/skills/gh-aw-enlistment/SKILL.md'))) + .toEqual(readFileSync(resolve('workflows/skills/gh-aw-enlistment/SKILL.md'))); + }); + + it('accepts an explicitly staged router deletion and rejects an unstaged retained index entry', () => { + const root = consumer(); + seedUnownedMutableRouter(root); + git(root, 'add', '--', UNOWNED_MUTABLE_ROUTER_SKILL); + git(root, 'add', '--force', '--', OWNERSHIP_DESTINATION); + git(root, '-c', 'user.name=Test', '-c', 'user.email=test@example.invalid', + '-c', 'core.hooksPath=/dev/null', 'commit', '--quiet', '-m', 'pre-existing gh-aw scaffold'); + + rmSync(join(root, UNOWNED_MUTABLE_ROUTER_SKILL)); + expect(verifyInstall(root).failures).toEqual([]); + expect(verifyStagedInstall(root).failures.join('\n')).toContain( + `Unowned mutable gh-aw router skill remains in staged tree: ${UNOWNED_MUTABLE_ROUTER_SKILL}`, + ); + + git(root, 'add', '--', UNOWNED_MUTABLE_ROUTER_SKILL); + expect(verifyStagedInstall(root).failures).toEqual([]); + expect(git(root, 'diff', '--cached', '--diff-filter=D', '--name-only').trim()) + .toBe(UNOWNED_MUTABLE_ROUTER_SKILL); + expect(stagedPaths(root)).not.toContain(UNOWNED_MUTABLE_ROUTER_SKILL); + expect(stagedPaths(root)).toContain('.github/skills/gh-aw-enlistment/SKILL.md'); + }); + + it('rejects a mutated Squad-owned enlistment skill after removing the tool-owned router', () => { + const root = consumer(); + write(root, '.github/skills/gh-aw-enlistment/SKILL.md', 'mutated\n'); + expect(verifyInstall(root).failures.join('\n')).toContain( + 'Installed digest mismatch for .github/skills/gh-aw-enlistment/SKILL.md', + ); + }); + it('fails clearly when Git cannot force-stage the required ignored metadata', () => { const root = consumer(); write(root, '.git/index.lock', 'locked\n'); diff --git a/test/gh-aw-quality.test.ts b/test/gh-aw-quality.test.ts index 9c12f45ca..dfc7d8fed 100644 --- a/test/gh-aw-quality.test.ts +++ b/test/gh-aw-quality.test.ts @@ -3866,6 +3866,7 @@ describe('gh-aw: canonical package integrity contract', () => { expect(ci.slice(seedStart, addStart)).toContain( '"$GITHUB_WORKSPACE/workflows/package/${workflow}.md"', ); + expect(ci.slice(addStart)).toContain('rm -f .github/skills/agentic-workflows/SKILL.md'); expect(contract.shared_runtime).toContainEqual(expect.objectContaining({ path: 'shared/squad-review-guard.mjs', destination: '.github/workflows/shared/squad-review-guard.mjs', @@ -3876,6 +3877,14 @@ describe('gh-aw: canonical package integrity contract', () => { expect(contract.bootstrap.trigger_probe).toBe(TRIGGER_PROBE); expect(contract.shared_runtime.some((entry: { path: string }) => entry.path === TRIGGER_PROBE)).toBe(false); expect(readText(join(process.cwd(), 'workflows/aw.yml'))).toContain(' - skills/gh-aw-enlistment'); + expect(existsSync(join( + process.cwd(), + '.github/skills/agentic-workflows/SKILL.md', + ))).toBe(false); + expect(existsSync(join( + process.cwd(), + '.github/workflows/agentics-maintenance.yml', + ))).toBe(true); for (const workflow of contract.workflows) { expect(workflow.source).toBe(`workflows/package/${workflow.name}.md`); expect(readText(join(process.cwd(), workflow.source))).not.toMatch(/^resources:/m); diff --git a/workflows/package/squad-bootstrap.md b/workflows/package/squad-bootstrap.md index c7c3dfc82..e35214eaa 100644 --- a/workflows/package/squad-bootstrap.md +++ b/workflows/package/squad-bootstrap.md @@ -152,7 +152,7 @@ pre-agent-steps: node --check "$path" >/dev/null } # BEGIN GENERATED RESOURCE DIGESTS - check_hash "$install_verifier" "da3f2ef1fc7efc8676eb654643bd0d0c0e59606538eeb2ebe7cf25792c63d1c1" + check_hash "$install_verifier" "5c28835d66e784c4848061337ec9c945029ac44ad68d74949cdbeb7fcf874aed" check_hash "$cast_validator" "c6d0b92aac71dc6f6d5727cac418a323b0bc9c12047400faa12d96150d548ada" check_hash "$bootstrap_validator" "1af6ab267eaf1148501a949b8b9ac65a1929130b03a1d7f757b156fd2de63fbb" # END GENERATED RESOURCE DIGESTS diff --git a/workflows/shared/squad-install-verifier.mjs b/workflows/shared/squad-install-verifier.mjs index 7bd39b8d3..63ca8dea8 100644 --- a/workflows/shared/squad-install-verifier.mjs +++ b/workflows/shared/squad-install-verifier.mjs @@ -26,6 +26,8 @@ export const MIN_GH_AW_VERSION = 'v0.89.22'; export const OWNERSHIP_ENTRY_COUNT = 26; export const OWNERSHIP_DESTINATION = '.github/aw/packages/bradygaster-squad-workflows-3632054824e8.json'; +export const UNOWNED_MUTABLE_ROUTER_SKILL = + '.github/skills/agentic-workflows/SKILL.md'; export const TRIGGER_PROBE = 'shared/squad-bootstrap-trigger-probe.json'; export const TRIGGER_PROBE_DESTINATION = '.github/workflows/shared/squad-bootstrap-trigger-probe.json'; @@ -768,6 +770,15 @@ function verifyInstalledBytes(root, contract, revision) { } } +function verifyUnownedMutableRouterAbsent(root) { + const path = safePath(root, UNOWNED_MUTABLE_ROUTER_SKILL); + if (existsSync(path)) { + throw new Error( + `Unowned mutable gh-aw router skill must be removed: ${UNOWNED_MUTABLE_ROUTER_SKILL}`, + ); + } +} + function verifyTriggerNamespace(root) { const directory = '.github/workflows/shared'; const files = readdirSync(safePath(root, directory)); @@ -879,6 +890,7 @@ export function verifyInstall(root, { expectedRevision = '', strictCompile = fal const { contract } = parseInstalledContract(root); revision = verifyOwnership(root, contract, expectedRevision); verifyInstalledBytes(root, contract, revision); + verifyUnownedMutableRouterAbsent(root); verifyTriggerNamespace(root); verifyTriggerProbe(root, revision); if (strictCompile) strictCompileMatches(root); @@ -917,7 +929,11 @@ export function verifyStagedInstall(root, { expectedRevision = '', stageOwnershi // Snapshot the index, not HEAD or the working tree, including unchanged tracked files. const tree = spawnChecked('git', ['write-tree'], root).stdout.trim(); // Exact allowlisted paths bound output; no recursion into unrelated or substituted trees. - const entries = spawnChecked('git', ['ls-tree', '-z', tree, '--', ...required], root).stdout + const entries = spawnChecked( + 'git', + ['ls-tree', '-z', tree, '--', ...required, UNOWNED_MUTABLE_ROUTER_SKILL], + root, + ).stdout .split('\0').filter(Boolean); const staged = new Map(entries.map(entry => { const tab = entry.indexOf('\t'); @@ -935,6 +951,11 @@ export function verifyStagedInstall(root, { expectedRevision = '', stageOwnershi throw new Error(`Staged digest mismatch for ${path}; stage the verified file before commit/push.`); } } + if (staged.has(UNOWNED_MUTABLE_ROUTER_SKILL)) { + throw new Error( + `Unowned mutable gh-aw router skill remains in staged tree: ${UNOWNED_MUTABLE_ROUTER_SKILL}`, + ); + } } catch (error) { result.failures.push(error instanceof Error ? error.message : String(error)); } diff --git a/workflows/skills/gh-aw-enlistment/SKILL.md b/workflows/skills/gh-aw-enlistment/SKILL.md index 77a5b4bd0..0a5b4eef9 100644 --- a/workflows/skills/gh-aw-enlistment/SKILL.md +++ b/workflows/skills/gh-aw-enlistment/SKILL.md @@ -194,6 +194,7 @@ published release guidance, then set it once: exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md ``` The nested `workflows/aw.yml` is the only supported distribution registration. @@ -216,6 +217,15 @@ governance-scoped retrospective proposal (see the gh-aw guide's retrospective auto-implementation section); installing it alongside the other seven keeps the full stack consistent and avoids a second bootstrap pass later. +gh-aw v0.89.22 also materializes +`.github/skills/agentic-workflows/SKILL.md`. That generic tool-owned router is +not part of the Squad package and directs agents to mutable prompts from the +current `github/gh-aw` repository rather than the pinned Squad revision. Remove +that exact file after every `gh aw add`. Keep +`.github/skills/gh-aw-enlistment/SKILL.md`: it is the one Squad-owned skill and +the verifier requires its exact package bytes. Do not adopt or vendor the rest +of gh-aw's generic scaffold. + Report/proposal-only is the default. Ordinary fixes require the explicit `"squadRetroAutoImplement": "allow"` setting in `.squad/config.json`; five action issues and three dispatches per wake-up remain separate caps. @@ -337,11 +347,20 @@ git add -- .gitattributes .github/aw/ .github/workflows/ .github/skills/ node .github/workflows/shared/squad-install-verifier.mjs \ --verify-staged-install --stage-ownership --source-revision "${SQUAD_SHA}" || exit 1 git diff --cached --stat -# No deletions should be staged: -test -z "$(git diff --cached --diff-filter=D --name-only)" || { echo "STOP: staged deletions"; exit 1; } +unexpected_deletions="$( + git diff --cached --diff-filter=D --name-only | + grep -vxF '.github/skills/agentic-workflows/SKILL.md' || true +)" +test -z "${unexpected_deletions}" || { + printf 'STOP: unexpected staged deletions:\n%s\n' "${unexpected_deletions}" >&2 + exit 1 +} ``` -- **STOP** if the staged diff shows **unexpected deletions**, **unexpected secrets**, +- The only permitted staged deletion is + `.github/skills/agentic-workflows/SKILL.md`, when upgrading a repository that + previously committed gh-aw's mutable router. +- **STOP** if the staged diff shows any other **unexpected deletions**, **unexpected secrets**, edits to **unrelated files**, or committed **log/diagnostic output**. Re-scope with explicit `git add -- ` — never `git add .`, `git add -A`, or `git commit -a`. @@ -461,6 +480,7 @@ git switch -c chore/squad-gh-aw-bootstrap exit 1 } gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" +rm -f .github/skills/agentic-workflows/SKILL.md # Safe-update report shows ONLY the two documented secrets + squad-init → approve once gh aw compile --strict --approve @@ -517,6 +537,9 @@ gh pr merge --squash # auto-merge before human review. NEVER. before installation if repository administration permission is unavailable. - ❌ **Blanket staging** (`git add .` / `-A` / `git commit -a`). Stage only `.gitattributes`, `.github/aw/`, `.github/workflows/`, `.github/skills/`, by path. +- ❌ **Committing gh-aw's mutable router.** Remove only + `.github/skills/agentic-workflows/SKILL.md`; keep the exact Squad-owned + `.github/skills/gh-aw-enlistment/SKILL.md`. - ❌ **Approving unknown safe updates.** Approve ONLY `SQUAD_GITHUB_APP_PRIVATE_KEY`, `SQUAD_GITHUB_TOKEN`, and `bradygaster/squad/.github/actions/squad-init`. Anything else is a STOP. diff --git a/workflows/squad-bootstrap.md b/workflows/squad-bootstrap.md index 4e088bc4c..7ae5cf682 100644 --- a/workflows/squad-bootstrap.md +++ b/workflows/squad-bootstrap.md @@ -159,7 +159,7 @@ pre-agent-steps: node --check "$path" >/dev/null } # BEGIN GENERATED RESOURCE DIGESTS - check_hash "$install_verifier" "da3f2ef1fc7efc8676eb654643bd0d0c0e59606538eeb2ebe7cf25792c63d1c1" + check_hash "$install_verifier" "5c28835d66e784c4848061337ec9c945029ac44ad68d74949cdbeb7fcf874aed" check_hash "$cast_validator" "c6d0b92aac71dc6f6d5727cac418a323b0bc9c12047400faa12d96150d548ada" check_hash "$bootstrap_validator" "1af6ab267eaf1148501a949b8b9ac65a1929130b03a1d7f757b156fd2de63fbb" # END GENERATED RESOURCE DIGESTS diff --git a/workflows/squad-workflows.manifest.json b/workflows/squad-workflows.manifest.json index fe56b2195..3fbb1c3be 100644 --- a/workflows/squad-workflows.manifest.json +++ b/workflows/squad-workflows.manifest.json @@ -68,9 +68,9 @@ "source": "workflows/package/squad-bootstrap.md", "destination": ".github/workflows/squad-bootstrap.md", "lock": ".github/workflows/squad-bootstrap.lock.yml", - "source_sha256": "e1f45d8e0038550f437602a00fa40c2629ed28f5295afb40179fe68c1fe27e30", - "lock_sha256": "a72aa20fc6ce95ca5dbab08d0531c7d670c93bb129d1931b6ff471571eefe386", - "package_lock_sha256": "5be4f342fd4593e70ca39825e24469a19895949aece7364686d1d2aaedd0ce8c" + "source_sha256": "05fa3faed373330a7914f2b0c9bf32731aea70cd0f5a27deae7383d28b587704", + "lock_sha256": "df8ec357b54bc7403f1ce490ce6052ea0d7e12d4906f2189f12e3bd46c5da602", + "package_lock_sha256": "8edd5d09430c054d9ae97dc425c64e7d54dc13d805b68828f59e80e59af00eff" }, { "name": "squad-command-router", @@ -97,7 +97,7 @@ "package_destination": ".github/workflows/shared/squad-install-verifier.mjs", "destination": ".github/workflows/shared/squad-install-verifier.mjs", "ownership": "manifest", - "sha256": "da3f2ef1fc7efc8676eb654643bd0d0c0e59606538eeb2ebe7cf25792c63d1c1" + "sha256": "5c28835d66e784c4848061337ec9c945029ac44ad68d74949cdbeb7fcf874aed" }, { "path": "shared/squad-command-contract.mjs", @@ -225,7 +225,7 @@ "name": "gh-aw-enlistment", "source": "workflows/skills/gh-aw-enlistment/SKILL.md", "destination": ".github/skills/gh-aw-enlistment/SKILL.md", - "sha256": "8a3f2d22a110d73f8a89e4f0645547354a35e185d3d2f49235265a4d017464f4" + "sha256": "fc6b63c3156d7861d28fcf459abee9e7f041d3581516ec38a684d5a902a27ac5" } ], "bootstrap": { From ba2e2e7ba31da01209b14604db866540d5d18aec Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Fri, 2 Oct 2026 04:50:38 -0700 Subject: [PATCH 2/3] test(gh-aw): prove router removal independence Document and enforce that repository routing uses the owned agentic-workflows specialist, designer, and reliability skill without the mutable gh-aw router. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/agents.md | 7 +++++++ test/gh-aw-quality.test.ts | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/agents.md b/.github/agents.md index 4fbb66ac6..c34867e76 100644 --- a/.github/agents.md +++ b/.github/agents.md @@ -57,6 +57,13 @@ The removal is intentional: gh-aw v0.89.22 generates that generic tool-owned router, which loads mutable prompts from the current `github/gh-aw` repository. It is not Squad-owned or bound to `SQUAD_SHA`. Keep the exact `.github/skills/gh-aw-enlistment/SKILL.md` installed by Squad. +This repository routes gh-aw work directly through the +`agentic-workflows-dev` specialist in `.squad/routing.md`, with the local +`agentic-workflow-designer` and `gh-aw-reliability` skills for design and +contract validation. None depends on the deleted router. Retaining a rewritten +or pinned copy would make Squad the owner of a forked generic gh-aw prompt +router and its upstream corpus; removal preserves the package boundary without +vendoring or synchronizing that mutable scaffold. This command: diff --git a/test/gh-aw-quality.test.ts b/test/gh-aw-quality.test.ts index dfc7d8fed..f47a7cdc7 100644 --- a/test/gh-aw-quality.test.ts +++ b/test/gh-aw-quality.test.ts @@ -3881,6 +3881,17 @@ describe('gh-aw: canonical package integrity contract', () => { process.cwd(), '.github/skills/agentic-workflows/SKILL.md', ))).toBe(false); + expect(readText(join(process.cwd(), '.squad/routing.md'))).toContain( + '| Agentic workflows | agentic-workflows-dev |', + ); + expect(existsSync(join( + process.cwd(), + '.github/skills/agentic-workflow-designer/SKILL.md', + ))).toBe(true); + expect(existsSync(join( + process.cwd(), + '.github/skills/gh-aw-reliability/SKILL.md', + ))).toBe(true); expect(existsSync(join( process.cwd(), '.github/workflows/agentics-maintenance.yml', From 43601b4a37e917ba880d4175d81ae986c2d82684 Mon Sep 17 00:00:00 2001 From: Brady Gaster Date: Fri, 2 Oct 2026 06:08:45 -0700 Subject: [PATCH 3/3] fix(gh-aw): repair verifier recovery cleanup Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- test/gh-aw-enlistment-skill.test.ts | 9 +++++++++ test/gh-aw-install-staging.test.ts | 7 +++++++ workflows/package/squad-bootstrap.md | 2 +- workflows/shared/squad-install-verifier.mjs | 1 + workflows/squad-bootstrap.md | 2 +- workflows/squad-workflows.manifest.json | 8 ++++---- 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/test/gh-aw-enlistment-skill.test.ts b/test/gh-aw-enlistment-skill.test.ts index 8aec33f0b..6f1a9173c 100644 --- a/test/gh-aw-enlistment-skill.test.ts +++ b/test/gh-aw-enlistment-skill.test.ts @@ -533,6 +533,15 @@ describe('gh-aw-enlistment skill', () => { expect(guide).toContain('--source-revision "${SQUAD_SHA}"'); expect(guide).toContain('--strict-compile'); expect(guide).toContain('package ownership metadata'); + const upgrade = guide.slice(guide.indexOf('## Upgrading')); + const add = upgrade.indexOf('gh aw add "bradygaster/squad/workflows@${SQUAD_SHA}" --force'); + const cleanup = upgrade.indexOf('rm -f .github/skills/agentic-workflows/SKILL.md'); + const compile = upgrade.indexOf('gh aw compile --strict'); + const verify = upgrade.indexOf('squad-install-verifier.mjs \\\n --verify-install'); + expect(add).toBeGreaterThan(-1); + expect(cleanup).toBeGreaterThan(add); + expect(compile).toBeGreaterThan(cleanup); + expect(verify).toBeGreaterThan(compile); }); }); diff --git a/test/gh-aw-install-staging.test.ts b/test/gh-aw-install-staging.test.ts index 7de5023e4..a06ab2e71 100644 --- a/test/gh-aw-install-staging.test.ts +++ b/test/gh-aw-install-staging.test.ts @@ -159,6 +159,13 @@ describe('gh-aw: verified ownership staging under consumer ignore rules', () => expect(rejected.stderr).toContain( `Unowned mutable gh-aw router skill must be removed: ${UNOWNED_MUTABLE_ROUTER_SKILL}`, ); + expect(rejected.stderr).toContain([ + 'Safe recovery:', + ` gh aw add bradygaster/squad/workflows@${revision} --force`, + ` rm -f ${UNOWNED_MUTABLE_ROUTER_SKILL}`, + ' gh aw compile --strict', + ' node .github/workflows/shared/squad-install-verifier.mjs --verify-install --strict-compile', + ].join('\n')); rmSync(join(root, UNOWNED_MUTABLE_ROUTER_SKILL)); const accepted = spawnSync(process.execPath, args, { cwd: root, encoding: 'utf8' }); diff --git a/workflows/package/squad-bootstrap.md b/workflows/package/squad-bootstrap.md index e35214eaa..698811a4f 100644 --- a/workflows/package/squad-bootstrap.md +++ b/workflows/package/squad-bootstrap.md @@ -152,7 +152,7 @@ pre-agent-steps: node --check "$path" >/dev/null } # BEGIN GENERATED RESOURCE DIGESTS - check_hash "$install_verifier" "5c28835d66e784c4848061337ec9c945029ac44ad68d74949cdbeb7fcf874aed" + check_hash "$install_verifier" "a279cd5c4adeb613ceb90c1bfbb9818265aeb6bc8986e2e3799e96f7c2d787e5" check_hash "$cast_validator" "c6d0b92aac71dc6f6d5727cac418a323b0bc9c12047400faa12d96150d548ada" check_hash "$bootstrap_validator" "1af6ab267eaf1148501a949b8b9ac65a1929130b03a1d7f757b156fd2de63fbb" # END GENERATED RESOURCE DIGESTS diff --git a/workflows/shared/squad-install-verifier.mjs b/workflows/shared/squad-install-verifier.mjs index 63ca8dea8..0438bd155 100644 --- a/workflows/shared/squad-install-verifier.mjs +++ b/workflows/shared/squad-install-verifier.mjs @@ -1037,6 +1037,7 @@ function recoveryMessage(revision) { return [ 'Safe recovery:', ` gh aw add ${PACKAGE_NAME}@${ref} --force`, + ` rm -f ${UNOWNED_MUTABLE_ROUTER_SKILL}`, ' gh aw compile --strict', ' node .github/workflows/shared/squad-install-verifier.mjs --verify-install --strict-compile', ].join('\n'); diff --git a/workflows/squad-bootstrap.md b/workflows/squad-bootstrap.md index 7ae5cf682..59c323b37 100644 --- a/workflows/squad-bootstrap.md +++ b/workflows/squad-bootstrap.md @@ -159,7 +159,7 @@ pre-agent-steps: node --check "$path" >/dev/null } # BEGIN GENERATED RESOURCE DIGESTS - check_hash "$install_verifier" "5c28835d66e784c4848061337ec9c945029ac44ad68d74949cdbeb7fcf874aed" + check_hash "$install_verifier" "a279cd5c4adeb613ceb90c1bfbb9818265aeb6bc8986e2e3799e96f7c2d787e5" check_hash "$cast_validator" "c6d0b92aac71dc6f6d5727cac418a323b0bc9c12047400faa12d96150d548ada" check_hash "$bootstrap_validator" "1af6ab267eaf1148501a949b8b9ac65a1929130b03a1d7f757b156fd2de63fbb" # END GENERATED RESOURCE DIGESTS diff --git a/workflows/squad-workflows.manifest.json b/workflows/squad-workflows.manifest.json index 3fbb1c3be..876421f9b 100644 --- a/workflows/squad-workflows.manifest.json +++ b/workflows/squad-workflows.manifest.json @@ -68,9 +68,9 @@ "source": "workflows/package/squad-bootstrap.md", "destination": ".github/workflows/squad-bootstrap.md", "lock": ".github/workflows/squad-bootstrap.lock.yml", - "source_sha256": "05fa3faed373330a7914f2b0c9bf32731aea70cd0f5a27deae7383d28b587704", - "lock_sha256": "df8ec357b54bc7403f1ce490ce6052ea0d7e12d4906f2189f12e3bd46c5da602", - "package_lock_sha256": "8edd5d09430c054d9ae97dc425c64e7d54dc13d805b68828f59e80e59af00eff" + "source_sha256": "387aeba6ba53e1e5fac90862a8730e49495c831dc9a33729a049d907f20008fe", + "lock_sha256": "dce16d010019f158a48b007afa4ef607b0cd0abec4ac8de1bef5bb0b358f3fb8", + "package_lock_sha256": "1988358848082ad1660d618c0bb96fb1db11de6413a86796966fc00ed081fc92" }, { "name": "squad-command-router", @@ -97,7 +97,7 @@ "package_destination": ".github/workflows/shared/squad-install-verifier.mjs", "destination": ".github/workflows/shared/squad-install-verifier.mjs", "ownership": "manifest", - "sha256": "5c28835d66e784c4848061337ec9c945029ac44ad68d74949cdbeb7fcf874aed" + "sha256": "a279cd5c4adeb613ceb90c1bfbb9818265aeb6bc8986e2e3799e96f7c2d787e5" }, { "path": "shared/squad-command-contract.mjs",