feat(sdk,cli): add Devil's Advocate as a built-in always-on agent (companion to Fact Checker)#1294
Conversation
…mpanion to Fact Checker) squad init/cast/upgrade already auto-scaffold Scribe, Ralph, RAI, and Fact Checker as always-on built-ins (bradygaster#1223). This adds a fifth: Devil's Advocate (`devils-advocate`, emoji 😈) — a design challenger that owns counter-arguments, pre-mortems, assumption-surfacing, and alternative-approach exploration. Why separate from Fact Checker: The fact-checker role declared dual operating mode ("Devil's Advocate & Verification Agent") and its routingPatterns included `devil's advocate`. In practice these are two distinct skills: * Fact Checker — "Is this claim true? Does this URL/version/API exist?" (empirical verification) * Devil's Advocate — "Is this plan wise? What is the strongest argument against it? What would we do if X was forbidden?" (design challenge) Splitting them gives each agent a sharp scope and a distinct charter. Changes: * New role devils-advocate in roles/catalog-engineering.ts (category: quality, emoji 😈) with design-challenge routingPatterns (devil's advocate, steelman, pre-mortem, counter-argument, ...). * New charter template packages/squad-cli/templates/devils-advocate-charter.md used by `squad upgrade` (ensureBuiltinAgents). * init.ts — adds devils-advocate to the default agents[] array. Fresh `squad init` produces .squad/agents/devils-advocate/. * upgrade.ts — adds devils-advocate to ensureBuiltinAgents. Idempotent (never overwrites a customized charter). * cast.ts — devilsAdvocateMember/devilsAdvocateCharter, hasDevilsAdvocate check in createTeam, charter-dispatch branch, roster banner line. * AGENT_TEMPLATES entry for devils-advocate; fact-checker entry clarified to reflect its now verification-only focus. * TEMPLATE_MANIFEST entry for devils-advocate-charter.md. * Fact Checker cleanup: removed `devil's advocate` from fact-checker's routingPatterns so the new role owns that routing exclusively. Test coverage: * New test/devils-advocate-role.test.ts — 7 assertions (catalog presence, routing patterns, distinct expertise, boundary delegation to Fact Checker, template file existence, differs-from-Fact-Checker section, methodology). * Updated test/fact-checker-role.test.ts — asserts `devil's advocate` is no longer in fact-checker's routing. * Updated test/template-routing.test.ts — TEMPLATE_MANIFEST pin includes devils-advocate-charter.md. Verified: 310/310 tests pass across init/cli/init/sdk/cast/role/template suites. `npm run lint` clean. Out of scope: mirroring the new charter template to packages/squad-sdk/ templates/. Only Rai-charter.md is in both today; fact-checker-charter.md is only in CLI templates. The SDK runtime reads via CLI's getTemplatesDir(), so this works. A general charter-template mirroring cleanup is separate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🛫 PR Readiness Check
PR Scope: 📦🔧 Mixed (product + infrastructure)
|
| Status | Check | Details |
|---|---|---|
| ✅ | Single commit | 1 commit — clean history |
| ✅ | Not in draft | Ready for review |
| ✅ | Branch up to date | Up to date with dev |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | Changeset file found |
| ✅ | Scope clean | No .squad/ or docs/proposals/ files |
| ✅ | No merge conflicts | No merge conflicts |
| ✅ | Copilot threads resolved | No Copilot review threads |
| ❌ | CI passing | 7 check(s) still running |
Files Changed (11 files, +329 −3)
| File | +/− |
|---|---|
.changeset/add-devils-advocate-agent.md |
+42 −0 |
packages/squad-cli/src/cli/core/cast.ts |
+76 −0 |
packages/squad-cli/src/cli/core/init.ts |
+5 −0 |
packages/squad-cli/src/cli/core/templates.ts |
+6 −0 |
packages/squad-cli/src/cli/core/upgrade.ts |
+1 −0 |
packages/squad-cli/templates/devils-advocate-charter.md |
+69 −0 |
packages/squad-sdk/src/config/init.ts |
+5 −1 |
packages/squad-sdk/src/roles/catalog-engineering.ts |
+44 −1 |
test/devils-advocate-role.test.ts |
+74 −0 |
test/fact-checker-role.test.ts |
+6 −1 |
test/template-routing.test.ts |
+1 −0 |
Total: +329 −3
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🟡 Impact Analysis — PR #1294Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (1 file)
squad-cli (5 files)
squad-sdk (2 files)
tests (3 files)
This report is generated automatically for every PR. See #733 for details. |
🏗️ Architectural Review
Automated architectural review — informational only. |
There was a problem hiding this comment.
Pull request overview
This PR adds a new built-in, always-on Devil’s Advocate agent as a companion to Fact Checker, wiring it through the SDK role catalog and CLI onboarding flows (init, cast, upgrade) and extending the template manifest and tests accordingly.
Changes:
- Add
devils-advocaterole to the SDK engineering role catalog (with dedicated routing patterns) and remove Devil’s Advocate routing from Fact Checker. - Add a Devil’s Advocate charter template and propagate it via CLI template manifest +
ensureBuiltinAgents()duringsquad upgrade. - Wire Devil’s Advocate into CLI
initdefaults andcast(member creation, charter dispatch, and summary banner), plus add/adjust tests.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
packages/squad-sdk/src/roles/catalog-engineering.ts |
Adds the new devils-advocate role and removes DA routing from Fact Checker. |
packages/squad-sdk/src/config/init.ts |
Adds devils-advocate to AGENT_TEMPLATES and refocuses Fact Checker description. |
packages/squad-cli/templates/devils-advocate-charter.md |
New charter template for Devil’s Advocate. |
packages/squad-cli/src/cli/core/templates.ts |
Adds template-manifest entry for devils-advocate-charter.md. |
packages/squad-cli/src/cli/core/init.ts |
Adds Devil’s Advocate to the default always-on agents list for squad init. |
packages/squad-cli/src/cli/core/upgrade.ts |
Ensures Devil’s Advocate is scaffolded as a built-in on squad upgrade. |
packages/squad-cli/src/cli/core/cast.ts |
Adds Devil’s Advocate as an always-on built-in in squad cast (member + charter + banner). |
test/devils-advocate-role.test.ts |
New test coverage for the new role + charter template content. |
test/fact-checker-role.test.ts |
Updates assertion to ensure Fact Checker no longer routes Devil’s Advocate work. |
test/template-routing.test.ts |
Updates TEMPLATE_MANIFEST pin to include the new charter template. |
.changeset/add-devils-advocate-agent.md |
Declares minor bumps for SDK/CLI and documents the addition. |
| function devilsAdvocateMember(): CastMember { | ||
| return { name: "Devil's Advocate", role: "Devil's Advocate", scope: 'Design challenge, pre-mortem analysis, assumption surfacing, alternative-approach exploration', emoji: '😈' }; | ||
| } |
| } else if (member.name === "Devil's Advocate" && !hasDevilsAdvocate) { | ||
| charter = devilsAdvocateCharter(); |
| 'fact-checker': { | ||
| displayName: 'Fact Checker', | ||
| description: 'Devil\'s advocate and verification agent — validates claims, detects hallucinations, and runs counter-hypotheses.' | ||
| description: 'Verification agent — validates claims, detects hallucinations, checks that URLs / package versions / API endpoints actually exist.' | ||
| }, | ||
| 'devils-advocate': { | ||
| displayName: "Devil's Advocate", | ||
| description: 'Design challenger and pre-mortem analyst — surfaces hidden assumptions, constructs the strongest counter-argument, and sketches alternative approaches before the team converges.' | ||
| } |
|
Closing per design correction. I re-read the source-of-truth issues + PRs after Tamir's review feedback:
Fact Checker IS the Devil's Advocate. Splitting them into two separate agents (as this PR did) contradicts the design and creates a redundant role. The right approach for further work in this area is to enrich the existing single User input on 2026-06-13: "fact checker and devils advicate are one subagnet and not 2, look at the issues and other PRs that i did about it to make sure you understnad" The reverted change is also rolled back from the local stabilization stack branch ( |
…#1299) (#1300) * fix(docs): tell coordinator to roster Fact Checker on first-time cast (#1299) squad init correctly creates .squad/agents/fact-checker/ on disk (per merged PR #1223). But when the user opens copilot --agent squad and the coordinator runs first-time casting, it OMITS Fact Checker from the team.md ## Members table while including Scribe, Ralph, and Rai. Root cause: .squad-templates/squad.agent.md had two gaps: 1. Line 56 said "team size (typically 4-5 + Scribe)" — naming only Scribe 2. Rai had a dedicated ## Rai section with explicit "Rai always appears in team.md" instruction — Fact Checker had no equivalent section So the model added Rai (because instructed to) but had no instruction to add Fact Checker, even though the agent dir was scaffolded on disk. Fix: * Update team-size line to name all 4 always-on built-ins: Scribe + Ralph + Rai + Fact Checker * Add full ## Fact Checker — Verification & Devil's Advocate section mirroring the Rai pattern: roster-entry instruction, dual operating mode (per #789 + #1254), trigger phrase table, confidence ratings, DA brief structure, boundaries, state location Sync via sync-templates.mjs --sync propagates squad.agent.md changes to all 4 mirror targets: .squad-templates/, templates/, packages/squad-cli/ templates/, packages/squad-sdk/templates/, .github/agents/. Tests: new test/squad-agent-roster.test.ts runs against all 4 template targets and asserts: * The "Determine team size" line names all 4 built-ins * A ## Fact Checker section exists with "always appears in team.md" * The section declares dual operating mode (anchors #789 + #1254 design so a future PR can't accidentally split Fact Checker and Devil's Advocate again — cf. closed PR #1294) * Existing Ralph + Rai sections still present 16/16 pass. Closes #1299 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * doc(squad.agent.md): clarify Fact Checker is exempt from casting + correct on-demand reference path Reviewer follow-ups on #1300: 1. Team-size phrasing — the line read 'typically 4-5 + Scribe + Ralph + Rai + Fact Checker' which a model could parse as arithmetic (4-5 + 4 = 8-9, but it could also collapse). Rewrote it to make the composition explicit: '4-5 cast (user-domain) agents + 4 always-on built-ins = 8-9 total roster entries'. 2. Cast-exemption parity — Scribe, Ralph, and Rai each have an explicit 'exempt from casting' bullet but Fact Checker did not. Added the matching bullet right after Rai's. 3. Bad on-demand reference path — the FC section pointed at '.squad/templates/fact-checker-charter.md'. That file IS shipped (TEMPLATE_MANIFEST destination 'templates/fact-checker-charter.md') but only AFTER 'squad init' or 'squad upgrade' has populated .squad/templates/. A reader of squad.agent.md on an un-initialized repo (or in .github/agents/ on the cloud agent surface) would follow a dead link. Repointed to the '.squad/agents/fact-checker/charter.md' instance that ensureBuiltinAgents creates as part of the same init/upgrade path — that's where the rich charter actually lives at runtime per #1299 + #1301. All 4 mirrored copies re-synced via scripts/sync-templates.mjs. fact-checker-role.test.ts: 8/8 pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Tamir Dresher <tamirdresher@users.noreply.github.com>
squad init,squad cast, andsquad upgradealready auto-scaffold Scribe, Ralph, RAI, and Fact Checker as always-on built-ins (per merged #1223). This adds a fifth: Devil's Advocate (😈) — a design challenger that owns counter-arguments, pre-mortems, assumption-surfacing, and alternative-approach exploration.Why separate from Fact Checker
The pre-existing
fact-checkerrole declared dual operating mode ("Devil's Advocate & Verification Agent") and itsroutingPatternsincluded"devil's advocate". In practice these are two distinct skills:Mixing them blurs the role and makes it unclear which mode the agent is operating in. Splitting gives each a sharp scope.
Changes
devils-advocateinpackages/squad-sdk/src/roles/catalog-engineering.ts(category:quality, emoji 😈) with design-challenge routing patterns:devil's advocate,steelman,pre-mortem,counter-argument,what could go wrong,challenge the plan,alternative approach,play devil.packages/squad-cli/templates/devils-advocate-charter.mdused bysquad upgrade(ensureBuiltinAgents). Documents methodology (Steelman → Surface assumptions → Pre-mortem → Alternatives → Risk acceptance) and explicitly delineates from Fact Checker.init.ts— addsdevils-advocateto the defaultagents:array. Freshsquad initproduces.squad/agents/devils-advocate/.upgrade.ts— addsdevils-advocatetoensureBuiltinAgents. Idempotent: never overwrites a customized charter.cast.ts—devilsAdvocateMember(),devilsAdvocateCharter(),hasDevilsAdvocatecheck increateTeam, charter-dispatch branch, roster banner line. Interactivesquad castnow offers Devil's Advocate as an always-on advisory agent.AGENT_TEMPLATESmap entry fordevils-advocate;fact-checkerentry clarified to reflect its now verification-only focus.TEMPLATE_MANIFESTentry fordevils-advocate-charter.md→templates/devils-advocate-charter.md."devil's advocate"fromfact-checker'sroutingPatternsso the new role owns that routing exclusively. Fact Checker's expertise / voice remain unchanged.Test coverage
test/devils-advocate-role.test.ts— 7 assertions: catalog presence, routing patterns, distinct expertise (does NOT claim "claim verification"), boundary delegation to Fact Checker, template file existence, differs-from-Fact-Checker section, methodology section.test/fact-checker-role.test.ts— asserts"devil's advocate"is no longer in fact-checker's routing.test/template-routing.test.ts— TEMPLATE_MANIFEST pin includes the new entry.Verified:
npm run lint— passesOut of scope
packages/squad-sdk/templates/does not currently mirrorfact-checker-charter.mdeither (only Rai is in both); the SDK runtime path reads from CLI templates viagetTemplatesDir(). Mirroring all charter templates to SDK templates is a separate cleanup that should not block this addition.