Skip to content

feat(gh-aw): add Squad shared component for GitHub Agentic Workflows - #1587

Closed
bradygaster wants to merge 8 commits into
devfrom
squad/gh-aw-shared-component
Closed

bradygaster wants to merge 8 commits into
devfrom
squad/gh-aw-shared-component

Conversation

@bradygaster

Copy link
Copy Markdown
Collaborator

What this does

Adapts Peli de Halleux's proven Squad × gh-aw (GitHub Agentic Workflows) integration from github/gh-aw into this repo. Peli built and demonstrated it and explicitly offered it for upstreaming — this is an adaptation of his design, not a rewrite. Credit to Peli.

There is no issue to close — this was dispatched directly by Brady off Peli's demonstration.

The architecture (Peli's — preserved exactly)

  1. Activation job (jobs.activation.pre-steps) runs npx @bradygaster/squad-cli init, then uploads .squad/ + .github/agents/squad.agent.md as a squad-state artifact.
  2. Agent job (steps:) downloads and restores that artifact. The Squad CLI never runs in the agent job.

This sidesteps gh-aw's network firewall, which only constrains the agent job — activation has open egress. The App-token → SQUAD_GITHUB_TOKEN → github.token auth ladder is kept intact.

Deliverables

  • .github/workflows/shared/squad.md — the importable shared component (no on: field).
  • .github/workflows/squad-backlog-triage.md (+ compiled .lock.yml) — sample workflow_dispatch workflow that imports the component and triages this repo's own issues.
  • docs/src/content/docs/features/gh-aw.md (+ nav entry) — feature documentation.

Four intentional changes from Peli's reference

  1. Import shapes documented — repo-local (shared/squad.md) and SHA-pinned remote (bradygaster/squad/.github/workflows/shared/squad.md@<sha>), verified against gh-aw's imports reference.
  2. --state-backend local pinned on the init command — the compiled agent invocation passes --disable-builtin-mcps, so Squad's state-mcp bridge can't load; a non-local backend would fail silently.
  3. Dropped the inaccurate --agent squad claim from Peli's header comment — replaced with an accurate description of gh-aw's .github/agents/*.agent.md sub-agent pickup (there is no --agent flag on the compiled Copilot invocation).
  4. SQUAD_CLI_VERSION default kept at 0.11.0 with a "bump every release" note. See follow-up below.

Validation

Compiled locally with gh aw compile (gh-aw v0.81.6, strict mode, 0 errors). The compiled lock confirmed:

  • npx … init --preset default --state-backend local (line 122)
  • GH_AW_SUB_AGENT_DIR=".github/agents" / GH_AW_SUB_AGENT_EXT=".agent.md" pickup (lines 519–520)
  • --disable-builtin-mcps on the agent invocation (line 814)
  • no --agent flag anywhere

test/docs-build.test.ts passes 22/22; cspell + markdownlint clean. Action versions (create-github-app-token@v3.2.0, upload-artifact@v7.0.1, download-artifact@v8.0.1) match Peli's reference exactly.

The committed .lock.yml was compiled with gh-aw v0.81.6. If the repo standardizes on a different gh-aw version, recompile with gh aw compile.

Known limitations (documented honestly, in the feature doc)

  • Cross-run state does not persist — the squad-state artifact is a within-run handoff (retention-days: 1). Casting registry, session logs, and Scribe output don't survive between runs. Solving this (artifacts / Actions cache / dedicated branch) is unsolved and out of scope.
  • npm required in the activation job — works on GitHub-hosted runners (open egress); a self-hosted runner behind a corporate npm-blocking firewall would fail. Out of scope.
  • No MCP state bridge in the agent job (--disable-builtin-mcps).

Drive-by fix

EXPECTED_GUIDES in test/docs-build.test.ts was missing agent-framework-integration (added in #1574, never synced) — the docs-build test was already failing on dev. Fixed here since EXPECTED_* sync is FIDO's charter responsibility and the test file was already being edited.

Scope / safety

repo-health-shaped: only .github/, docs/, test/, and cspell.json. No packages/*/src/ changes → no changeset required. Reference copies under .squad/proposals/reference/ were not touched.

Security note (from gh aw compile)

The compile flagged two new restricted secrets — SQUAD_GITHUB_APP_PRIVATE_KEY and SQUAD_GITHUB_TOKEN. Both come from the shared component's optional auth ladder (mint a GitHub App token / fall back to a PAT for cross-org squad init), are referenced only in the activation job, and default to unused (github.token) when unset. No credential exfiltration or unexpected egress introduced.

⚠️ Needs squad member review before merging.

Design credit: Peli de Halleux — https://github.com/github/gh-aw/blob/main/.github/workflows/shared/squad.md

bradygaster and others added 5 commits July 24, 2026 09:42
- Merged 5 pending decisions from .squad/decisions/inbox to .squad/decisions.md:
  - 2026-03-26: CI deletion guard and source tree canary (Booster)
  - 2026-03-26: Copilot git safety rules (RETRO)
  - 2026-03-29: Versioning Policy (Flight)
  - 2026-04-25: Release Process Skill Update (Booster)
  - 2026-06-24: Restore always-on coordinator inline-dispatch gate (Procedures)

- Added orchestration logs for Flight, FIDO, RETRO, Surgeon agents
  (Session: 2026-07-24T09:20:10.011Z)

- Added session log: locked PR merge strategy for 17 open PRs

- Deleted merged inbox files (.squad/decisions/inbox/*)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adapt Peli de Halleux's proven Squad × gh-aw integration
(github/gh-aw) into this repo. The shared component bootstraps Squad in
the gh-aw activation job and restores its state into the agent job, so
the Squad CLI never runs inside the network-restricted agent sandbox.

- .github/workflows/shared/squad.md: importable component (no `on:`),
  activation/agent split preserved, App-token > SQUAD_GITHUB_TOKEN >
  github.token auth ladder intact. Pins `--state-backend local` because
  the agent runs with `--disable-builtin-mcps`; documents repo-local and
  SHA-pinned remote import shapes; drops the inaccurate `--agent squad`
  claim in favor of gh-aw's `.github/agents/*.agent.md` pickup.
- .github/workflows/squad-backlog-triage.md (+ compiled .lock.yml):
  sample workflow_dispatch workflow that imports the component and
  triages this repo's issues. Compiled clean with gh aw compile v0.81.6
  (strict, 0 errors).
- docs: new features/gh-aw.md page (+ nav) covering the job split, both
  import shapes, optional App-token vars/secrets, SQUAD_CLI_VERSION, and
  the known limitations (no cross-run state, npm required in activation,
  no MCP state bridge).
- test: sync EXPECTED_GUIDES with agent-framework-integration (drifted
  since #1574) so docs-build passes; add cspell words (Peli, mcps).

Design credit: Peli de Halleux
https://github.com/github/gh-aw/blob/main/.github/workflows/shared/squad.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 4, 2026 00:20
@github-actions

github-actions Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

🟠 Impact Analysis — PR #1587

Risk tier: 🟠 HIGH

📊 Summary

Metric Count
Files changed 9
Files added 5
Files modified 4
Files deleted 0
Modules touched 5

🎯 Risk Factors

  • 9 files changed (6-20 → MEDIUM)
  • 5 modules touched (5-8 → HIGH)

📦 Modules Affected

ci-workflows (3 files)
  • .github/workflows/shared/squad.md
  • .github/workflows/squad-backlog-triage.lock.yml
  • .github/workflows/squad-backlog-triage.md
docs (2 files)
  • docs/src/content/docs/features/gh-aw.md
  • docs/src/navigation.ts
root (2 files)
  • aw.yml
  • cspell.json
squad-state (1 file)
  • .squad/agents/fido/history.md
tests (1 file)
  • test/docs-build.test.ts

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit 4fac1d8

PR Scope: 🔧 Infrastructure

⚠️ 5 item(s) to address before review

Status Check Details
❌ Single commit 8 commits — consider squashing before review
✅ Not in draft Ready for review
❌ Branch up to date dev is 1 commit(s) ahead — rebase recommended
❌ Copilot review No Copilot review yet — it may still be processing
✅ Changeset present No source files changed — changeset not required
✅ Scope clean ⚠️ PR includes 1 .squad/ file(s) — ensure these are intentional
✅ No merge conflicts No merge conflicts
❌ Copilot threads resolved 1 unresolved Copilot thread(s) — fix and resolve before merging
❌ CI passing 1 check(s) failing: Lint workflows and templates

Files Changed (9 files, +2099 −2)

File +/−
.github/workflows/shared/squad.md +150 −0
.github/workflows/squad-backlog-triage.lock.yml +1599 −0
.github/workflows/squad-backlog-triage.md +92 −0
.squad/agents/fido/history.md +38 −1
aw.yml +4 −0
cspell.json +2 −0
docs/src/content/docs/features/gh-aw.md +211 −0
docs/src/navigation.ts +1 −0
test/docs-build.test.ts +2 −1

Total: +2099 −2


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

⚠️ Squad File Leakage Detected

The following .squad/ files were modified in this PR:

  • .squad/agents/fido/history.md

These files affect team routing, agent charters, and decisions.
If intentional, ensure approval from the team lead.

@github-actions

github-actions Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

🔒 Security Review

🔒 Security review: 29 warning(s), 4 info.

Severity Category Finding Location
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:120
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:124
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:500
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:501
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:832
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:884
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:888
🟡 warning secrets-reference Non-standard secret reference in workflow — verify this secret is necessary and scoped correctly. .github/workflows/squad-backlog-triage.lock.yml:889
🟡 warning pii-env-var Environment variable with sensitive name pattern (PRIVATE_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:2
🟡 warning pii-env-var Environment variable with sensitive name pattern (PRIVATE_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:36
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:210
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:456
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:470
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:475
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:685
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:686
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:687
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:702
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:744
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:753
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:763
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:765
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:793
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:816
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:858
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:863
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:873
🟡 warning pii-env-var Environment variable with sensitive name pattern (CREDENTIALS) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:1300
🟡 warning pii-env-var Environment variable with sensitive name pattern (API_KEY) — ensure this isn't hardcoded. .github/workflows/squad-backlog-triage.lock.yml:1391
ℹ️ info workflow-permissions Workflow grants write permission — verify this is the minimum required scope. .github/workflows/squad-backlog-triage.lock.yml:401
ℹ️ info workflow-permissions Workflow grants write permission — verify this is the minimum required scope. .github/workflows/squad-backlog-triage.lock.yml:1016
ℹ️ info workflow-permissions Workflow grants write permission — verify this is the minimum required scope. .github/workflows/squad-backlog-triage.lock.yml:1259
ℹ️ info workflow-permissions Workflow grants write permission — verify this is the minimum required scope. .github/workflows/squad-backlog-triage.lock.yml:1508

Automated security review — informational only.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🏗️ Architectural Review

⚠️ Architectural review: 1 info.

Severity Category Finding Files
ℹ️ info template-sync Template files changed in .github/workflows/ but not in other template locations. If these templates should stay in sync, consider updating the others too. Changed: .github/workflows/, Unchanged: templates/, .squad-templates/, packages/squad-cli/templates/

Automated architectural review — informational only.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a Squad × GitHub Agentic Workflows (gh-aw) integration by adding a reusable shared workflow component, a sample consuming workflow (plus compiled lock), and end-user documentation so Squad can be bootstrapped in the activation job and then used in the agent job without running the Squad CLI inside the restricted sandbox.

Changes:

  • Added an importable shared component at .github/workflows/shared/squad.md that performs squad init during activation and hands off state via an artifact.
  • Added a sample workflow_dispatch backlog-triage workflow (.github/workflows/squad-backlog-triage.md) and committed its compiled .lock.yml.
  • Added docs + nav wiring for the new gh-aw feature page, and synced the docs-build test expectations.
Show a summary per file
File Description
test/docs-build.test.ts Updates expected docs directory lists to include new content.
docs/src/navigation.ts Adds “GitHub Agentic Workflows” to the Features nav section.
docs/src/content/docs/features/gh-aw.md Documents how the gh-aw activation/agent split works and how to import the shared component.
cspell.json Adds dictionary entries for new terminology/names used in docs.
.squad/agents/fido/history.md Records the gh-aw integration ship note in FIDO history.
.github/workflows/squad-backlog-triage.md Adds a sample gh-aw workflow source that imports the shared Squad component.
.github/workflows/squad-backlog-triage.lock.yml Adds the compiled gh-aw lock workflow that GitHub Actions will execute.
.github/workflows/shared/squad.md Adds the shared Squad bootstrap component used by gh-aw imports.

Review details

Suppressed comments (2)

.github/workflows/shared/squad.md:73

  • Action reference is a mutable tag (v7.0.1). Since this repo pins Actions to commit SHAs in workflows, it would be safer to pin upload-artifact to the immutable SHA (matching the compiled lock file) so remote importers aren’t relying on a moving tag.
        uses: actions/upload-artifact@v7.0.1

.github/workflows/shared/squad.md:85

  • Action reference is a mutable tag (v8.0.1). To align with the repo’s workflow security posture, pin download-artifact to an immutable commit SHA (matching the compiled lock file) so consumers get a reproducible dependency graph.
    uses: actions/download-artifact@v8.0.1
  • Files reviewed: 7/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

- name: Mint Squad GitHub App token
id: squad-app-token
if: ${{ vars.SQUAD_GITHUB_APP_ID != '' }}
uses: actions/create-github-app-token@v3.2.0
@bradygaster

Copy link
Copy Markdown
Collaborator Author

Verification note — the SQUAD_CLI_VERSION pin in this component has a known caveat that is not a defect in this PR.

Published @bradygaster/squad-cli@0.11.0 declares "@bradygaster/squad-sdk": "*", so pinning the CLI does not pin the SDK — and the SDK carries the presets and agent charters. Today * resolves to 0.11.0 by coincidence; the first 0.12.0 SDK publish will silently change what this workflow runs.

Tracked separately in #1588. Out of scope here (this PR touches no packages/*/src/), but it affects how much the version pin in shared/squad.md is actually worth.

Everything else verified against the published tarballs rather than local source, since that is what the workflow consumes:

  • --state-backend and --preset both present in published squad-cli@0.11.0
  • published squad-sdk@0.11.0 ships dist/presets/builtin/default/ with 5 charters
  • gh aw compile v0.81.6 strict → 0 errors

Still unproven: no end-to-end gh-aw run has ever executed.

Add `--ignore-scripts` to the activation-job `npx` in the gh-aw Squad shared
component so npm lifecycle scripts don't execute during CI install — a
supply-chain hardening measure suggested by Peli de Halleux in review of #1587.

squad-cli@0.11.0 ships a postinstall (patch-esm-imports.mjs +
patch-ink-rendering.mjs). patch-esm-imports patches vscode-jsonrpc for Copilot
SDK sessions (#449), but `squad init` is pure file scaffolding and never opens a
session. Verified byte-identical `.squad/` output with and without the flag on
Node 24, so skipping scripts is safe. Header comment added so it isn't removed.

Recompiled the lock with `gh aw compile` (v0.81.6, strict) — the lock run line
matches the source byte-for-byte. Docs table updated to match. docs-build 22/22.

Refs #1587
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bradygaster

Copy link
Copy Markdown
Collaborator Author

Applied Peli's review suggestion: added --ignore-scripts to the activation-job npx (commit 20d6681).

What changed

  • .github/workflows/shared/squad.md — flag added before the package spec (npx --yes --ignore-scripts "@bradygaster/squad-cli@…"), plus a header comment explaining why it's safe so nobody removes it later. Credit to @pelikhan for the suggestion.
  • .github/workflows/squad-backlog-triage.lock.yml — recompiled with gh aw compile (v0.81.6, strict, 0 errors). The lock's run line now matches the source byte-for-byte.
  • docs/src/content/docs/features/gh-aw.md — activation-job row updated so docs and workflow agree.

Why it's safe (not a blind accept)
@bradygaster/squad-cli@0.11.0 does ship a postinstall (patch-esm-imports.mjs + patch-ink-rendering.mjs, both in the tarball). patch-esm-imports is load-bearing — it patches vscode-jsonrpc so Node 22+ strict ESM resolves subpath imports for Copilot SDK sessions (#449). But squad init is pure file scaffolding and never opens an SDK session.

A/B verified on Node v24.16.0 (the version the patch targets):

Test Command Result
A npx --yes --ignore-scripts …@0.11.0 init --preset default --state-backend local exit 0, 5 agents, 100 md files
B same without --ignore-scripts exit 0

diff -r of the two .squad/ trees: only differences are the .first-run/updated_at timestamps and the temp-dir name. Agent lists identical; .github/agents/squad.agent.md byte-identical. So --ignore-scripts skips no real work for init while blocking lifecycle-script execution from any compromised transitive dep — exactly what security scanners look for.

Validation: docs-build.test.ts 22/22, cspell clean on in-scope docs. Landed on squad/gh-aw-shared-component only.

(Re: the 109KB compiled lock shrinking in the next AW release — noted, informational, no action here.)

# handoff with retention-days: 1) — every run starts from a fresh `squad init`.
engine:
id: copilot

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot add agent: squad to emit --agent squad

bradygaster and others added 2 commits August 4, 2026 08:32
…ream

Sync PR #1587 with Peli de Halleux's revised gh-aw Squad component, taking
only the parts that compile on our pinned gh aw v0.81.6:

- shared/squad.md: add engine.agent: squad (compiler emits --agent squad);
  correct the stale header note that misattributed a '--agent' error; note
  that ambient-folders (gh-aw main, unreleased) will supersede the manual
  artifact upload/download once it ships.
- squad-backlog-triage.md: adopt the fail-fast pre-agent-steps preflight that
  emits a noop safe-output when Squad files are missing; align prompt body.
- Recompiled lock (gh aw compile --strict: 0 errors, 0 warnings); lock now
  carries --agent squad.
- docs/features/gh-aw.md: document engine.agent, the preflight, and the
  ambient-folders note. docs-build test passes.

Deliberately NOT adopted: top-level ambient-folders key (unknown property on
v0.81.6/v0.84.3; rejected by --strict). Kept --ignore-scripts and
--state-backend local.

Closes #1587

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…quad'

Package Squad's ready-made agentic workflows so users can install them with
'gh aw add bradygaster/squad' (Peli de Halleux's suggestion). Uses the verified
v0.81.6 schema:

- name: required (published docs omit it — validation fails without it)
- includes: (not the deprecated 'files:', which emits a codemod warning)
- root README.md required for validation (already present)

gh aw compile --strict: 0 errors, 0 warnings, no files:-deprecation warning.
Docs updated with a 'gh aw add' install section crediting Peli; docs-build
test passes (22/22). No new cspell entries required.

Closes #1587

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tamirdresher

Copy link
Copy Markdown
Collaborator

Reviewed. Two things — one safety note, one suggestion.

1. --state-backend local is protecting more than the PR credits it for

The PR justifies the pin as "the agent job runs with --disable-builtin-mcps, so the state-mcp bridge can't load." True — but it's also gating this, in packages/squad-cli/src/cli/core/init.ts:

// Lift those mutable files onto the squad-state orphan branch
// and remove the working-tree copies so the backend is the
// single source of truth post-init.
const lifted = liftInitMutableStateOntoOrphan(dest);

That strips decisions.md and every agent's history.md out of the working tree. It's guarded by if (options.stateBackend === 'orphan' || options.stateBackend === 'two-layer') — so on a repo with committed state, the local pin is the only thing stopping init from deleting it from the checkout before the agent ever sees it.

Not a bug in this PR — it's correct as written. But the reason is undocumented, so whoever fixes the MCP issue later could reasonably drop the pin as no-longer-needed and silently reintroduce it. Worth a line in the header comment.

2. Could we detect an existing squad and skip the bootstrap entirely?

On a repo that already commits .squad/, init is close to a no-op — skipExisting: true, templates are overwriteOnUpgrade: false, and squad.agent.md only gets stampVersion(). Everything else prints already exists — skipping.

So today, for those repos, we install from npm, resolve the SDK unpinned (#1588), skip everything, and upload an artifact duplicating files already in git.

detectSquadDir() already exists in src/cli/core/detect-squad-dir.ts, but the workflow only needs a file test:

- name: Detect existing Squad
  id: squad-detect
  run: |
    if [ -f .squad/team.md ] && [ -f .github/agents/squad.agent.md ]; then
      echo "present=true" >> "$GITHUB_OUTPUT"
    else
      echo "present=false" >> "$GITHUB_OUTPUT"
    fi

# then add to the three existing activation steps:
#   Mint token:  if: steps.squad-detect.outputs.present != 'true' && vars.SQUAD_GITHUB_APP_ID != ''
#   Init:        if: steps.squad-detect.outputs.present != 'true'
#   Upload:      if: success() && steps.squad-detect.outputs.present != 'true'

The agent job needs no changes — its restore step is already continue-on-error: true, so with no artifact it quietly no-ops and the checked-out files win.

That takes npm off this path entirely for committed-squad repos, which also takes #1588 off it. Probably wants a vars.SQUAD_FORCE_INIT escape hatch for repos that would rather float on the latest CLI, and a note that the committed version becomes authoritative — which I think is the right default, just worth stating.

Also worth flagging separately: this is independent of ambient-folders. That optimizes how the bootstrap ships files; detection removes the need to ship them at all for the committed case.

@tamirdresher

Copy link
Copy Markdown
Collaborator

Follow-up to my previous comment — here's the concrete patch for the detection idea, so it's directly usable rather than just a question.

1. .github/workflows/shared/squad.md — activation job

 jobs:
   activation:
     pre-steps:
+      - name: Detect committed Squad
+        id: squad-detect
+        env:
+          SQUAD_FORCE_INIT: ${{ vars.SQUAD_FORCE_INIT }}
+        run: |
+          set -euo pipefail
+          if [ "${SQUAD_FORCE_INIT:-}" = "true" ]; then
+            echo "present=false" >> "$GITHUB_OUTPUT"
+            echo "SQUAD_FORCE_INIT is set — bootstrapping from npm."
+          elif [ -f .squad/team.md ] && [ -f .github/agents/squad.agent.md ]; then
+            echo "present=true" >> "$GITHUB_OUTPUT"
+            echo "Committed Squad detected — skipping npm bootstrap."
+          else
+            echo "present=false" >> "$GITHUB_OUTPUT"
+            echo "No committed Squad — bootstrapping from npm."
+          fi
+
       - name: Mint Squad GitHub App token
         id: squad-app-token
-        if: ${{ vars.SQUAD_GITHUB_APP_ID != '' }}
+        if: ${{ steps.squad-detect.outputs.present != 'true' && vars.SQUAD_GITHUB_APP_ID != '' }}
         uses: actions/create-github-app-token@v3.2.0
         ...

       - name: Initialize Squad team
+        if: ${{ steps.squad-detect.outputs.present != 'true' }}
         env:
           SQUAD_CLI_VERSION: ${{ vars.SQUAD_CLI_VERSION }}
         ...

       - name: Upload Squad state artifact
-        if: success()
+        if: ${{ success() && steps.squad-detect.outputs.present != 'true' }}
         uses: actions/upload-artifact@v7.0.1
         ...

The agent job needs no changes. Its restore step is already continue-on-error: true, so when no artifact was uploaded it no-ops and the checked-out files are used as-is.

SQUAD_FORCE_INIT is read via env: rather than inlined into the script, to keep the repo variable off the shell command line.

2. Header comment — two additions

# Detection: if the consuming repo already commits `.squad/` and
# `.github/agents/squad.agent.md`, the activation job skips the npm install,
# `squad init`, and the artifact upload entirely — the agent job's own
# checkout already has them. Set `vars.SQUAD_FORCE_INIT=true` to bootstrap
# from npm anyway (e.g. to float on the latest CLI rather than the committed
# version). Note this makes the committed Squad version authoritative for
# these repos; upgrade deliberately with `squad upgrade` and commit the diff.
#
# `--state-backend local` is load-bearing for TWO reasons, not one:
#   1. the agent runs with `--disable-builtin-mcps`, so the state-mcp bridge
#      cannot load and a non-local backend would fail silently; and
#   2. `init` calls `liftInitMutableStateOntoOrphan()` for the `orphan` and
#      `two-layer` backends, which REMOVES `decisions.md` and every
#      `agents/<n>/history.md` from the working tree. On a repo with committed
#      state that would strip it out of the checkout before the agent runs.
# Do not drop this pin when (1) is eventually fixed.

3. Prompt text — two lines currently assume init always ran

-Squad's team state (`.squad/`) and its Copilot custom agent
-(`.github/agents/squad.agent.md`) were already initialized during activation and
-restored into this checkout before you started — do **not** install Squad or run
-`squad init` yourself, and do **not** try to reach the npm registry.
+Squad's team state (`.squad/`) and its Copilot custom agent
+(`.github/agents/squad.agent.md`) are already present in this checkout — either
+committed to the repository, or generated during activation — do **not** install
+Squad or run `squad init` yourself, and do **not** try to reach the npm registry.
-- State does **not** carry over between runs. The casting registry, session
-  logs, and any Scribe output you produce live only for this run. Do not assume
-  a prior run's decisions are still on disk — start from what `squad init`
-  produced plus the repository's own tracked history (issues, PRs, `.squad/`
-  files committed to the repo).
+- Anything you write to `.squad/` during this run is discarded when the run ends.
+  The casting registry and session logs do not carry over. Base your work on what
+  is on disk now (the committed `.squad/` files, or what `squad init` produced)
+  plus the repository's tracked history — do not assume a prior run's decisions
+  survived.

Net effect for a repo that commits .squad/: no npm, no SDK resolve at run time (so #1588 stops applying to this path), no artifact round-trip, and no ambiguity in the prompt about which team is in play. For repos that don't commit it, behaviour is unchanged.

pull Bot pushed a commit to nagyist/squad that referenced this pull request Aug 5, 2026
Adds a build path that produces self-contained Squad bundles vendoring their
own Node.js runtime, so installing Squad touches github.com only and never
registry.npmjs.org.

This is the foundation for the distribution work proposed in github#1593, and it
closes the limitation Brady documented and deferred in github#1587: the gh-aw
activation job currently requires npx, so it cannot run on a self-hosted
runner behind a firewall that blocks the npm registry.

What is here:

- scripts/build-standalone.mjs — builds a bundle for a platform/arch pair.
  Packs the workspace packages, installs them with --omit=dev --omit=optional,
  vendors an official Node runtime, and writes a launcher.
- scripts/install.sh — POSIX installer that resolves the platform, downloads
  the matching release asset, verifies it against SHA256SUMS.txt, and links
  squad onto PATH. Mirrors the shape of gh.io/copilot-install.
- .github/workflows/squad-standalone-release.yml — 6-target matrix build that
  attaches archives plus a combined SHA256SUMS.txt to a release.
- docs/src/content/docs/features/standalone-install.md — feature docs, with
  known limitations stated.

Design notes:

squad-cli and squad-sdk locate templates/, presets/builtin/ and package.json
by walking up from import.meta.url at runtime. The bundle therefore preserves
a real directory layout instead of compiling to a single executable, which is
why this needs zero source changes. It is also the same shape GitHub's own
Copilot CLI publishes.

Optional dependencies are omitted by default. That drops the Copilot CLI
platform binary pulled in transitively through @github/copilot-sdk (~318 MB),
node-pty prebuilds, the OpenTelemetry SDK and sql.js — 546 MB down to 114 MB,
in line with Copilot CLI's own ~110 MB archives. Squad already requires the
copilot CLI on PATH and Copilot CLI ships npm-free installers of its own, so
vendoring a second copy would only add weight and version skew.

--ignore-scripts is safe here: the vscode-jsonrpc ESM fix that squad-cli's
postinstall applies is also applied at runtime by cli-entry.js via a
Module._resolveFilename hook, so the bundle does not depend on it having run.

Verified locally on win32-x64:

- squad --version from the bundle returns 0.11.0 on its own vendored runtime
- squad init --preset default --state-backend local scaffolds 5 agents in a
  clean directory with no npm involved
- generated .github/workflows/squad-heartbeat.yml is byte-identical (sha256
  5CE0F5D1...) to the repo template source, confirming the bundle reproduces
  its source faithfully
- docs-build test passes, cspell and markdownlint clean on the new page

No packages/*/src changes, so no changeset is required.

Refs github#1593, github#1587

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
tamirdresher added a commit that referenced this pull request Aug 5, 2026
…#1593)

Second slice of the standalone distribution work. Builds on the bundle
mechanism from the previous commit and closes the gap that made it
incomplete.

The gap: installing Squad from a bundle removed npm from *installation*, but
`squad init` still wrote an npx-based `squad_state` MCP entry into
`.mcp.json`, and probed registry.npmjs.org to choose the version. On a
firewalled machine the CLI installed fine and then the MCP server silently
failed to launch on first use — the exact failure mode this work exists to
prevent.

What changed:

- mcp-spec.ts gains a tier-0 ahead of the two npx tiers. When
  SQUAD_STANDALONE_HOME is set (exported by the bundle launchers) the spec
  points at that bundle's launcher by absolute path with args ['state-mcp'].
  It short-circuits before the registry probe, so a machine with no route to
  npm makes no network call here at all. Absolute because Copilot spawns the
  MCP server in its own environment, where neither PATH nor that variable is
  guaranteed.
- describeMcpSpec() previously read args[1] to name the package; standalone
  specs have no package there and would have printed '<unknown>'. It now
  reports the launcher path.
- build-standalone.mjs launchers (sh, cmd, ps1) export SQUAD_STANDALONE_HOME.
- .github/actions/squad-init — composite action that installs from a release
  bundle and runs squad init. This is the direct replacement for the
  `npx @bradygaster/squad-cli init` step in the gh-aw activation job (#1587).
  It also fails the build if the resulting .mcp.json still mentions npx.
- Dockerfile + .dockerignore — image built from a bundle instead of
  `npm install -g`, honoring the existing container contract in
  docs/reference/container-image.md (WORKDIR /app, non-root uid 1001,
  SIGTERM, `squad watch --execute`). Sets SQUAD_STANDALONE_HOME=/opt/squad.
  Built with --include-optional because the contract documents OTLP export,
  which lives in the optional OpenTelemetry packages.
- .gitattributes pins LF for Dockerfile and .dockerignore. CRLF breaks RUN
  line continuations when the image builds on Linux.

Verified:

- 13 new tests in mcp-spec-init.test.ts, including an explicit assertion that
  the registry probe never fires in standalone mode. 23/23 pass.
- Built a bundle and ran `squad init` from it on win32-x64: the generated
  .mcp.json contains command=<bundle>/squad.cmd, args=['state-mcp'], no npx.
- Runtime stage of the Dockerfile built and exercised on linux/amd64 with a
  host-built bundle: `squad --version` returns 0.11.0 as the non-root squad
  user, `which squad` resolves to /opt/squad/squad, `squad init` writes
  command=/opt/squad/squad, and `squad state-mcp` answers a JSON-RPC
  initialize handshake with serverInfo.name=squad-state.
- lint, docs-build, mcp-root-write, state-mcp, cspell, markdownlint all clean.

Not verified: the Dockerfile builder stage. This machine intercepts TLS, so
every container gets ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE reaching
registry.npmjs.org — reproducible with a bare
`docker run node:22-slim npm view lodash`. Not a Dockerfile defect, but the
full image has never been built end to end and CI must prove it. Likewise the
squad-init action cannot run until a release with bundle assets exists.

Refs #1593, #1587

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
bradygaster added a commit that referenced this pull request Aug 5, 2026
)

* fix(ci): skip DO-NOT-EDIT gh-aw generated workflows in lint; widen agents guard

actionlint v1.7.12 is the current latest release — upgrading cannot fix
this. The tool lags GitHub's evolving feature set on two specific
constructs:

  • 'copilot-requests: write' — a real current Actions permission scope
    used by GitHub's own gh-aw toolchain.
  • 'concurrency.queue: max' — a newer Actions concurrency feature.

The files triggering these errors are DO-NOT-EDIT compiler output from
gh-aw's 'gh aw compile' toolchain (*.lock.yml) and gh-aw's code
generators (e.g. agentics-maintenance.yml). Editing them is not
acceptable — they are clobbered on each compile/generate cycle and are
validated upstream by gh-aw's own toolchain.

Fix 1 (.github/workflows/squad-workflow-lint.yml):
  Detect generated files by their header ('automatically generated' /
  'DO NOT EDIT' within the first 5 lines) and skip them. Header-based
  detection automatically covers future gh-aw generated files without
  manual maintenance. Hand-authored workflows are always linted. Verified
  with a sabotage test: intentionally broken hand-authored workflow still
  fails the gate. Skipped files are echoed in CI logs for debuggability.

Fix 2 (test/template-sync.test.ts):
  The '.github/agents/ negative guard' asserted strict directory equality,
  which over-reaches. 'gh aw init' legitimately installs its own
  agentic-workflows.md in that directory. Narrowed the guard to its actual
  intent: squad.agent.md must be present, no *unexpected* files allowed,
  with an explicit allowlist of known non-Squad agents. A truly random
  stray file still fails the test.

Unblocks PRs #1587 and #1592 (does NOT close them).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(test): add section-1 beforeAll sync to guard against init-scaffolding race

test/init-scaffolding.test.ts runs runInit() from a subdirectory of the repo
root.  In monorepo mode the init command detects the git root and places
squad.agent.md at .github/agents/ in the REAL repo root, then calls
stampVersion() to rewrite that file in place.  This runs concurrently with
template-sync.test.ts in a different Vitest worker.

The file-level beforeAll sync in template-sync.test.ts is correct but leaves
a race window: if init-scaffolding's stampVersion write lands between the
file-level sync and section 1's readFileBytes calls, the byte comparison sees
the stamped file (which differs from the canonical) and fails.

Fix: add a describe-scoped beforeAll inside section 1 that re-runs the sync
immediately before the byte comparisons start.  This collapses the race
window from 'entire beforeAll + test-collection time' to near-zero.

The byte-for-byte assertion itself is unchanged; this only makes the setup
more resilient.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bradygaster pushed a commit that referenced this pull request Aug 5, 2026
…#1593) (#1610)

* feat(dist): npm-free MCP spec, squad-init action, and container image (#1593)

Second slice of the standalone distribution work. Builds on the bundle
mechanism from the previous commit and closes the gap that made it
incomplete.

The gap: installing Squad from a bundle removed npm from *installation*, but
`squad init` still wrote an npx-based `squad_state` MCP entry into
`.mcp.json`, and probed registry.npmjs.org to choose the version. On a
firewalled machine the CLI installed fine and then the MCP server silently
failed to launch on first use — the exact failure mode this work exists to
prevent.

What changed:

- mcp-spec.ts gains a tier-0 ahead of the two npx tiers. When
  SQUAD_STANDALONE_HOME is set (exported by the bundle launchers) the spec
  points at that bundle's launcher by absolute path with args ['state-mcp'].
  It short-circuits before the registry probe, so a machine with no route to
  npm makes no network call here at all. Absolute because Copilot spawns the
  MCP server in its own environment, where neither PATH nor that variable is
  guaranteed.
- describeMcpSpec() previously read args[1] to name the package; standalone
  specs have no package there and would have printed '<unknown>'. It now
  reports the launcher path.
- build-standalone.mjs launchers (sh, cmd, ps1) export SQUAD_STANDALONE_HOME.
- .github/actions/squad-init — composite action that installs from a release
  bundle and runs squad init. This is the direct replacement for the
  `npx @bradygaster/squad-cli init` step in the gh-aw activation job (#1587).
  It also fails the build if the resulting .mcp.json still mentions npx.
- Dockerfile + .dockerignore — image built from a bundle instead of
  `npm install -g`, honoring the existing container contract in
  docs/reference/container-image.md (WORKDIR /app, non-root uid 1001,
  SIGTERM, `squad watch --execute`). Sets SQUAD_STANDALONE_HOME=/opt/squad.
  Built with --include-optional because the contract documents OTLP export,
  which lives in the optional OpenTelemetry packages.
- .gitattributes pins LF for Dockerfile and .dockerignore. CRLF breaks RUN
  line continuations when the image builds on Linux.

Verified:

- 13 new tests in mcp-spec-init.test.ts, including an explicit assertion that
  the registry probe never fires in standalone mode. 23/23 pass.
- Built a bundle and ran `squad init` from it on win32-x64: the generated
  .mcp.json contains command=<bundle>/squad.cmd, args=['state-mcp'], no npx.
- Runtime stage of the Dockerfile built and exercised on linux/amd64 with a
  host-built bundle: `squad --version` returns 0.11.0 as the non-root squad
  user, `which squad` resolves to /opt/squad/squad, `squad init` writes
  command=/opt/squad/squad, and `squad state-mcp` answers a JSON-RPC
  initialize handshake with serverInfo.name=squad-state.
- lint, docs-build, mcp-root-write, state-mcp, cspell, markdownlint all clean.

Not verified: the Dockerfile builder stage. This machine intercepts TLS, so
every container gets ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE reaching
registry.npmjs.org — reproducible with a bare
`docker run node:22-slim npm view lodash`. Not a Dockerfile defect, but the
full image has never been built end to end and CI must prove it. Likewise the
squad-init action cannot run until a release with bundle assets exists.

Refs #1593, #1587

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(action): run the bundled installer and fail on npx MCP specs

Addresses both Copilot review comments on #1610.

1. The action fetched scripts/install.sh from the repository HEAD over the
   network. That is unpinned remote code execution, and the installer could
   drift from the release tag being installed. It now runs the install.sh that
   ships alongside the action, resolved from github.action_path. Consumers pin
   the action to a SHA, so the installer is pinned with it.

2. The npx check only wrote a warning to the step summary and exited 0, which
   did not match what the PR claimed. An npx-based squad_state entry is
   unrunnable on exactly the firewalled runners this action targets, and it
   fails later at Copilot start rather than here, so it now fails the step.

Verified the action_path walk resolves correctly on Linux (alpine container:
/repo/.github/actions/squad-init -> /repo/scripts/install.sh) rather than
assuming the relative depth.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(dist): cross-build posix bundles from Windows, add build/install tests

Found while running the installer end to end for the first time: building a
linux or darwin bundle from a Windows host failed.

  node-v22.20.0-linux-x64/bin/npx: Can't create '...': Invalid argument
  tar: Error exit delayed from previous errors.

The builder extracted the whole Node.js archive, and the posix archives carry
symlinks (bin/npm, bin/npx, bin/corepack) that Windows cannot create. Only the
node binary is ever shipped, so extraction is now scoped to that single
member. Extraction failures also report the underlying tar error instead of a
bare non-zero exit.

This would have bitten anyone building non-linux targets from a Windows
runner, and the release workflow's structural checks would not have caught it.

Also adds test/standalone-build.test.ts (11 cases) covering the behaviors that
break silently: selective extraction, SQUAD_STANDALONE_HOME in all three
launchers, symlink resolution in the posix launcher, optional-dep omission,
checksum verification, VERSION/PREFIX/REPO overrides, and an assertion that
install.sh never reaches the npm registry.

Verified the full install path in a container with no Node and no npm: served
a packaged bundle over HTTP, ran the real install.sh against it, and got
squad --version -> 0.11.0 through the PATH symlink. Corrupting the asset
made the installer abort with a checksum mismatch, as intended.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

---------

Co-authored-by: Tamir Dresher <342800+tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
@bradygaster

Copy link
Copy Markdown
Collaborator Author

Closing — superseded by the new unified /squad workflow design (see epic #1612). The shared component will be rebuilt as #1613.

@bradygaster bradygaster closed this Aug 5, 2026
bradygaster added a commit that referenced this pull request Aug 5, 2026
…) (#1611)

* feat(dist): npm-free MCP spec, squad-init action, and container image (#1593)

Second slice of the standalone distribution work. Builds on the bundle
mechanism from the previous commit and closes the gap that made it
incomplete.

The gap: installing Squad from a bundle removed npm from *installation*, but
`squad init` still wrote an npx-based `squad_state` MCP entry into
`.mcp.json`, and probed registry.npmjs.org to choose the version. On a
firewalled machine the CLI installed fine and then the MCP server silently
failed to launch on first use — the exact failure mode this work exists to
prevent.

What changed:

- mcp-spec.ts gains a tier-0 ahead of the two npx tiers. When
  SQUAD_STANDALONE_HOME is set (exported by the bundle launchers) the spec
  points at that bundle's launcher by absolute path with args ['state-mcp'].
  It short-circuits before the registry probe, so a machine with no route to
  npm makes no network call here at all. Absolute because Copilot spawns the
  MCP server in its own environment, where neither PATH nor that variable is
  guaranteed.
- describeMcpSpec() previously read args[1] to name the package; standalone
  specs have no package there and would have printed '<unknown>'. It now
  reports the launcher path.
- build-standalone.mjs launchers (sh, cmd, ps1) export SQUAD_STANDALONE_HOME.
- .github/actions/squad-init — composite action that installs from a release
  bundle and runs squad init. This is the direct replacement for the
  `npx @bradygaster/squad-cli init` step in the gh-aw activation job (#1587).
  It also fails the build if the resulting .mcp.json still mentions npx.
- Dockerfile + .dockerignore — image built from a bundle instead of
  `npm install -g`, honoring the existing container contract in
  docs/reference/container-image.md (WORKDIR /app, non-root uid 1001,
  SIGTERM, `squad watch --execute`). Sets SQUAD_STANDALONE_HOME=/opt/squad.
  Built with --include-optional because the contract documents OTLP export,
  which lives in the optional OpenTelemetry packages.
- .gitattributes pins LF for Dockerfile and .dockerignore. CRLF breaks RUN
  line continuations when the image builds on Linux.

Verified:

- 13 new tests in mcp-spec-init.test.ts, including an explicit assertion that
  the registry probe never fires in standalone mode. 23/23 pass.
- Built a bundle and ran `squad init` from it on win32-x64: the generated
  .mcp.json contains command=<bundle>/squad.cmd, args=['state-mcp'], no npx.
- Runtime stage of the Dockerfile built and exercised on linux/amd64 with a
  host-built bundle: `squad --version` returns 0.11.0 as the non-root squad
  user, `which squad` resolves to /opt/squad/squad, `squad init` writes
  command=/opt/squad/squad, and `squad state-mcp` answers a JSON-RPC
  initialize handshake with serverInfo.name=squad-state.
- lint, docs-build, mcp-root-write, state-mcp, cspell, markdownlint all clean.

Not verified: the Dockerfile builder stage. This machine intercepts TLS, so
every container gets ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE reaching
registry.npmjs.org — reproducible with a bare
`docker run node:22-slim npm view lodash`. Not a Dockerfile defect, but the
full image has never been built end to end and CI must prove it. Likewise the
squad-init action cannot run until a release with bundle assets exists.

Refs #1593, #1587

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(action): run the bundled installer and fail on npx MCP specs

Addresses both Copilot review comments on #1610.

1. The action fetched scripts/install.sh from the repository HEAD over the
   network. That is unpinned remote code execution, and the installer could
   drift from the release tag being installed. It now runs the install.sh that
   ships alongside the action, resolved from github.action_path. Consumers pin
   the action to a SHA, so the installer is pinned with it.

2. The npx check only wrote a warning to the step summary and exited 0, which
   did not match what the PR claimed. An npx-based squad_state entry is
   unrunnable on exactly the firewalled runners this action targets, and it
   fails later at Copilot start rather than here, so it now fails the step.

Verified the action_path walk resolves correctly on Linux (alpine container:
/repo/.github/actions/squad-init -> /repo/scripts/install.sh) rather than
assuming the relative depth.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(dist): cross-build posix bundles from Windows, add build/install tests

Found while running the installer end to end for the first time: building a
linux or darwin bundle from a Windows host failed.

  node-v22.20.0-linux-x64/bin/npx: Can't create '...': Invalid argument
  tar: Error exit delayed from previous errors.

The builder extracted the whole Node.js archive, and the posix archives carry
symlinks (bin/npm, bin/npx, bin/corepack) that Windows cannot create. Only the
node binary is ever shipped, so extraction is now scoped to that single
member. Extraction failures also report the underlying tar error instead of a
bare non-zero exit.

This would have bitten anyone building non-linux targets from a Windows
runner, and the release workflow's structural checks would not have caught it.

Also adds test/standalone-build.test.ts (11 cases) covering the behaviors that
break silently: selective extraction, SQUAD_STANDALONE_HOME in all three
launchers, symlink resolution in the posix launcher, optional-dep omission,
checksum verification, VERSION/PREFIX/REPO overrides, and an assertion that
install.sh never reaches the npm registry.

Verified the full install path in a container with no Node and no npm: served
a packaged bundle over HTTP, ran the real install.sh against it, and got
squad --version -> 0.11.0 through the PATH symlink. Corrupting the asset
made the installer abort with a checksum mismatch, as intended.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* feat(dist): Homebrew and winget packaging, plus a real squad.exe (#1593)

Third slice of the distribution work. Adds the two package managers people
actually expect, and the Windows executable that makes one of them possible.

The blocker I found first: winget's portable installer only creates command
aliases for .exe targets — .cmd and .bat are explicitly unsupported. Our
Windows bundle's entry point was squad.cmd, so a winget manifest would have
installed something that could not be invoked. Homebrew has no such
restriction and works with the existing shell launcher.

So Windows bundles now ship a real squad.exe, built with Node's single
executable application support. It is the vendored runtime with a small
launcher embedded, so it REPLACES runtime/node.exe rather than adding to the
bundle — a Windows bundle is still 114 MB. The launcher resolves the bundle
from its own location and follows a symlink if it finds one, because winget
installs portables by symlinking the exe into a links directory.

Also here:

- scripts/generate-packaging.mjs renders the Homebrew cask and the three
  winget manifests from a release's SHA256SUMS.txt. Both ecosystems embed a
  version and a per-artifact SHA-256, so hand-maintaining them guarantees they
  go stale — this is exactly the standing cost flagged in #1593.
- The release workflow generates them after publishing and attaches them as an
  artifact for a maintainer to submit.
- mcp-spec now prefers squad.exe over squad.cmd on Windows. Since the fix for
  CVE-2024-27980 Node refuses to spawn a .cmd without shell:true, so an MCP
  client spawning the command directly would fail to start squad_state.

Two real bugs found while testing, both of which CI would have hit:

1. GNU tar cannot read zip archives; bsdtar (Windows, macOS) can. The release
   workflow builds every target on ubuntu-latest, so every win32 bundle would
   have failed to unpack its Node runtime. Now uses unzip on posix hosts, and
   the workflow installs it for win32 targets.
2. execFileSync with shell:true splits an unquoted command path on spaces, so
   invoking process.execPath from "C:\Program Files\nodejs" failed. The run()
   helper now quotes.

Verified:

- squad.exe, squad.cmd and squad.ps1 all return 0.11.0 from a real bundle
- squad init from squad.exe scaffolds 5 agents and writes an npx-free
  .mcp.json pointing at the bundle launcher
- SEA blob is version-locked: a blob built by Node 23.5 injected into the
  vendored 22.20.0 produced an exe that died with an access violation
  (0xC0000005). The builder now downloads a matching host Node to generate it.
- Cross-build works: a Linux container unzipped the Windows runtime, generated
  the blob with Linux Node 22.20.0, injected it with postject, and the
  resulting PE ran correctly on Windows. That is the production path, since
  the workflow builds all six targets on ubuntu-latest.
- Generator output parses as YAML, all three winget manifests agree on
  identifier/version/schema, and both installers point at .exe
- 67 tests pass (18 new for the generator, 8 new for the exe and extraction)

Dropped the shebang from generate-packaging.mjs so vitest can import it on
Windows; scripts/pr-readiness.mjs, the repo's other importable script, has no
shebang either. Worth noting separately: the same shebang interaction is why
test/promote-insider-tag.test.ts is one of the pre-existing Windows failures.
Left alone as out of scope.

Not verified: no winget or Homebrew submission has been made, because neither
can be tested without a published release carrying bundle assets. Code signing
and notarization are still absent, and injecting the SEA blob invalidates the
Node runtime's Authenticode signature, so SmartScreen will warn on Windows.
That is the main gap before either package should be recommended widely.

Refs #1593

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf

* fix(ci): suppress shellcheck SC2016 on Actions expression in release workflow

The single-quoted backticks are intentional markdown formatting in the
step summary, not shell expressions. Disable SC2016 for those lines.

Closes #1611

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Tamir Dresher <342800+tamirdresher@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Brady Gaster <41929050+bradygaster@users.noreply.github.com>
Copilot-Session: d2a1f506-af41-4d6d-b497-d0a5b255d4cf
bradygaster added a commit that referenced this pull request Aug 9, 2026
…e workflows)

gh aw init had never been run in this repo; this adds the standard scaffolding
gh-aw generates for a repository that authors and ships agentic workflows.

We are now a gh-aw publisher (.github/workflows/shared/squad.md,
squad-backlog-triage.md, aw.yml in PR #1587), so this tooling protects the
correctness of what we ship:

- .github/skills/agentic-workflows/ and agentic-workflow-designer/: designer /
  dispatcher skills that keep gh-aw frontmatter and compiled .lock.yml correct.
- .github/agents/agentic-workflows.md: the gh-aw authoring sub-agent.
- .github/workflows/copilot-setup-steps.yml: lets @copilot run 'gh aw compile'
  in CI rather than hand-editing generated .lock.yml files.
- .github/workflows/agentics-maintenance.yml: REQUIRED because
  squad-backlog-triage.md uses 'expires: 7' — this workflow closes the
  gh-aw-created expiring issues/PRs/discussions.
- .github/mcp.json: configures the gh-aw MCP server for the CLOUD coding agent.
  The local-CLI equivalent lives in root .mcp.json and is intentionally NOT
  included here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bradygaster added a commit that referenced this pull request Aug 9, 2026
…e workflows)

gh aw init had never been run in this repo; this adds the standard scaffolding
gh-aw generates for a repository that authors and ships agentic workflows.

We are now a gh-aw publisher (.github/workflows/shared/squad.md,
squad-backlog-triage.md, aw.yml in PR #1587), so this tooling protects the
correctness of what we ship:

- .github/skills/agentic-workflows/ and agentic-workflow-designer/: designer /
  dispatcher skills that keep gh-aw frontmatter and compiled .lock.yml correct.
- .github/agents/agentic-workflows.md: the gh-aw authoring sub-agent.
- .github/workflows/copilot-setup-steps.yml: lets @copilot run 'gh aw compile'
  in CI rather than hand-editing generated .lock.yml files.
- .github/workflows/agentics-maintenance.yml: REQUIRED because
  squad-backlog-triage.md uses 'expires: 7' — this workflow closes the
  gh-aw-created expiring issues/PRs/discussions.
- .github/mcp.json: configures the gh-aw MCP server for the CLOUD coding agent.
  The local-CLI equivalent lives in root .mcp.json and is intentionally NOT
  included here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bradygaster added a commit that referenced this pull request Aug 9, 2026
…e workflows) (#1592)

* chore(gh-aw): add gh aw init tooling (skills, MCP, setup + maintenance workflows)

gh aw init had never been run in this repo; this adds the standard scaffolding
gh-aw generates for a repository that authors and ships agentic workflows.

We are now a gh-aw publisher (.github/workflows/shared/squad.md,
squad-backlog-triage.md, aw.yml in PR #1587), so this tooling protects the
correctness of what we ship:

- .github/skills/agentic-workflows/ and agentic-workflow-designer/: designer /
  dispatcher skills that keep gh-aw frontmatter and compiled .lock.yml correct.
- .github/agents/agentic-workflows.md: the gh-aw authoring sub-agent.
- .github/workflows/copilot-setup-steps.yml: lets @copilot run 'gh aw compile'
  in CI rather than hand-editing generated .lock.yml files.
- .github/workflows/agentics-maintenance.yml: REQUIRED because
  squad-backlog-triage.md uses 'expires: 7' — this workflow closes the
  gh-aw-created expiring issues/PRs/discussions.
- .github/mcp.json: configures the gh-aw MCP server for the CLOUD coding agent.
  The local-CLI equivalent lives in root .mcp.json and is intentionally NOT
  included here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: address Copilot review threads on gh-aw init PR

- Pin actions/checkout to SHA (supply-chain hardening)
- Fix MCP tool name: agentic-workflows → github-agentic-workflows
- Remove reference to non-existent skills/otel-queries/SKILL.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bradygaster
bradygaster deleted the squad/gh-aw-shared-component branch August 20, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants