chore: merge squad update assets

Merge Squad update workflows/templates and Scribe issue-plan decision log.

Juan Manuel Servera committed Jun 5, 2026 at 22:55 UTC ac7575d052bd00365d021ea0f307a047ae457561
10 files changed +624
.github/workflows/squad-ci.yml new
+28
@@ -0,0 +1,28 @@
1 +name: Squad CI
2 +# python project — configure build/test commands below
3 +
4 +on:
5 + pull_request:
6 + branches: [dev, preview, main, insider]
7 + types: [opened, synchronize, reopened]
8 + push:
9 + branches: [dev, insider]
10 +
11 +permissions:
12 + contents: read
13 +
14 +jobs:
15 + test:
16 + runs-on: ubuntu-latest
17 + steps:
18 + - uses: actions/checkout@v4
19 +
20 + - name: Build and test
21 + run: |
22 + # TODO: Add your python build/test commands here
23 + # Go: go test ./...
24 + # Python: pip install -r requirements.txt && pytest
25 + # .NET: dotnet test
26 + # Java (Maven): mvn test
27 + # Java (Gradle): ./gradlew test
28 + echo "No build commands configured — update squad-ci.yml"
.github/workflows/squad-docs.yml new
+27
@@ -0,0 +1,27 @@
1 +name: Squad Docs — Build & Deploy
2 +# python project — configure documentation build commands below
3 +
4 +on:
5 + workflow_dispatch:
6 + push:
7 + branches: [preview]
8 + paths:
9 + - 'docs/**'
10 + - '.github/workflows/squad-docs.yml'
11 +
12 +permissions:
13 + contents: read
14 + pages: write
15 + id-token: write
16 +
17 +jobs:
18 + build:
19 + runs-on: ubuntu-latest
20 + steps:
21 + - uses: actions/checkout@v4
22 +
23 + - name: Build docs
24 + run: |
25 + # TODO: Add your documentation build commands here
26 + # This workflow is optional — remove or customize it for your project
27 + echo "No docs build commands configured — update or remove squad-docs.yml"
.github/workflows/squad-insider-release.yml new
+34
@@ -0,0 +1,34 @@
1 +name: Squad Insider Release
2 +# python project — configure build, test, and insider release commands below
3 +
4 +on:
5 + push:
6 + branches: [insider]
7 +
8 +permissions:
9 + contents: write
10 +
11 +jobs:
12 + release:
13 + runs-on: ubuntu-latest
14 + steps:
15 + - uses: actions/checkout@v4
16 + with:
17 + fetch-depth: 0
18 +
19 + - name: Build and test
20 + run: |
21 + # TODO: Add your python build/test commands here
22 + # Go: go test ./...
23 + # Python: pip install -r requirements.txt && pytest
24 + # .NET: dotnet test
25 + # Java (Maven): mvn test
26 + # Java (Gradle): ./gradlew test
27 + echo "No build commands configured — update squad-insider-release.yml"
28 +
29 + - name: Create insider release
30 + env:
31 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32 + run: |
33 + # TODO: Add your insider/pre-release commands here
34 + echo "No release commands configured — update squad-insider-release.yml"
.github/workflows/squad-label-enforce.yml new
+181
@@ -0,0 +1,181 @@
1 +name: Squad Label Enforce
2 +
3 +on:
4 + issues:
5 + types: [labeled]
6 +
7 +permissions:
8 + issues: write
9 + contents: read
10 +
11 +jobs:
12 + enforce:
13 + runs-on: ubuntu-latest
14 + steps:
15 + - uses: actions/checkout@v4
16 +
17 + - name: Enforce mutual exclusivity
18 + uses: actions/github-script@v7
19 + with:
20 + script: |
21 + const issue = context.payload.issue;
22 + const appliedLabel = context.payload.label.name;
23 +
24 + // Namespaces with mutual exclusivity rules
25 + const EXCLUSIVE_PREFIXES = ['go:', 'release:', 'type:', 'priority:'];
26 +
27 + // Skip if not a managed namespace label
28 + if (!EXCLUSIVE_PREFIXES.some(p => appliedLabel.startsWith(p))) {
29 + core.info(`Label ${appliedLabel} is not in a managed namespace — skipping`);
30 + return;
31 + }
32 +
33 + const allLabels = issue.labels.map(l => l.name);
34 +
35 + // Handle go: namespace (mutual exclusivity)
36 + if (appliedLabel.startsWith('go:')) {
37 + const otherGoLabels = allLabels.filter(l =>
38 + l.startsWith('go:') && l !== appliedLabel
39 + );
40 +
41 + if (otherGoLabels.length > 0) {
42 + // Remove conflicting go: labels
43 + for (const label of otherGoLabels) {
44 + await github.rest.issues.removeLabel({
45 + owner: context.repo.owner,
46 + repo: context.repo.repo,
47 + issue_number: issue.number,
48 + name: label
49 + });
50 + core.info(`Removed conflicting label: ${label}`);
51 + }
52 +
53 + // Post update comment
54 + await github.rest.issues.createComment({
55 + owner: context.repo.owner,
56 + repo: context.repo.repo,
57 + issue_number: issue.number,
58 + body: `🏷️ Triage verdict updated → \`${appliedLabel}\``
59 + });
60 + }
61 +
62 + // Auto-apply release:backlog if go:yes and no release target
63 + if (appliedLabel === 'go:yes') {
64 + const hasReleaseLabel = allLabels.some(l => l.startsWith('release:'));
65 + if (!hasReleaseLabel) {
66 + await github.rest.issues.addLabels({
67 + owner: context.repo.owner,
68 + repo: context.repo.repo,
69 + issue_number: issue.number,
70 + labels: ['release:backlog']
71 + });
72 +
73 + await github.rest.issues.createComment({
74 + owner: context.repo.owner,
75 + repo: context.repo.repo,
76 + issue_number: issue.number,
77 + body: `📋 Marked as \`release:backlog\` — assign a release target when ready.`
78 + });
79 +
80 + core.info('Applied release:backlog for go:yes issue');
81 + }
82 + }
83 +
84 + // Remove release: labels if go:no
85 + if (appliedLabel === 'go:no') {
86 + const releaseLabels = allLabels.filter(l => l.startsWith('release:'));
87 + if (releaseLabels.length > 0) {
88 + for (const label of releaseLabels) {
89 + await github.rest.issues.removeLabel({
90 + owner: context.repo.owner,
91 + repo: context.repo.repo,
92 + issue_number: issue.number,
93 + name: label
94 + });
95 + core.info(`Removed release label from go:no issue: ${label}`);
96 + }
97 + }
98 + }
99 + }
100 +
101 + // Handle release: namespace (mutual exclusivity)
102 + if (appliedLabel.startsWith('release:')) {
103 + const otherReleaseLabels = allLabels.filter(l =>
104 + l.startsWith('release:') && l !== appliedLabel
105 + );
106 +
107 + if (otherReleaseLabels.length > 0) {
108 + // Remove conflicting release: labels
109 + for (const label of otherReleaseLabels) {
110 + await github.rest.issues.removeLabel({
111 + owner: context.repo.owner,
112 + repo: context.repo.repo,
113 + issue_number: issue.number,
114 + name: label
115 + });
116 + core.info(`Removed conflicting label: ${label}`);
117 + }
118 +
119 + // Post update comment
120 + await github.rest.issues.createComment({
121 + owner: context.repo.owner,
122 + repo: context.repo.repo,
123 + issue_number: issue.number,
124 + body: `🏷️ Release target updated → \`${appliedLabel}\``
125 + });
126 + }
127 + }
128 +
129 + // Handle type: namespace (mutual exclusivity)
130 + if (appliedLabel.startsWith('type:')) {
131 + const otherTypeLabels = allLabels.filter(l =>
132 + l.startsWith('type:') && l !== appliedLabel
133 + );
134 +
135 + if (otherTypeLabels.length > 0) {
136 + for (const label of otherTypeLabels) {
137 + await github.rest.issues.removeLabel({
138 + owner: context.repo.owner,
139 + repo: context.repo.repo,
140 + issue_number: issue.number,
141 + name: label
142 + });
143 + core.info(`Removed conflicting label: ${label}`);
144 + }
145 +
146 + await github.rest.issues.createComment({
147 + owner: context.repo.owner,
148 + repo: context.repo.repo,
149 + issue_number: issue.number,
150 + body: `🏷️ Issue type updated → \`${appliedLabel}\``
151 + });
152 + }
153 + }
154 +
155 + // Handle priority: namespace (mutual exclusivity)
156 + if (appliedLabel.startsWith('priority:')) {
157 + const otherPriorityLabels = allLabels.filter(l =>
158 + l.startsWith('priority:') && l !== appliedLabel
159 + );
160 +
161 + if (otherPriorityLabels.length > 0) {
162 + for (const label of otherPriorityLabels) {
163 + await github.rest.issues.removeLabel({
164 + owner: context.repo.owner,
165 + repo: context.repo.repo,
166 + issue_number: issue.number,
167 + name: label
168 + });
169 + core.info(`Removed conflicting label: ${label}`);
170 + }
171 +
172 + await github.rest.issues.createComment({
173 + owner: context.repo.owner,
174 + repo: context.repo.repo,
175 + issue_number: issue.number,
176 + body: `🏷️ Priority updated → \`${appliedLabel}\``
177 + });
178 + }
179 + }
180 +
181 + core.info(`Label enforcement complete for ${appliedLabel}`);
.github/workflows/squad-preview.yml new
+30
@@ -0,0 +1,30 @@
1 +name: Squad Preview Validation
2 +# python project — configure build, test, and validation commands below
3 +
4 +on:
5 + push:
6 + branches: [preview]
7 +
8 +permissions:
9 + contents: read
10 +
11 +jobs:
12 + validate:
13 + runs-on: ubuntu-latest
14 + steps:
15 + - uses: actions/checkout@v4
16 +
17 + - name: Build and test
18 + run: |
19 + # TODO: Add your python build/test commands here
20 + # Go: go test ./...
21 + # Python: pip install -r requirements.txt && pytest
22 + # .NET: dotnet test
23 + # Java (Maven): mvn test
24 + # Java (Gradle): ./gradlew test
25 + echo "No build commands configured — update squad-preview.yml"
26 +
27 + - name: Validate
28 + run: |
29 + # TODO: Add pre-release validation commands here
30 + echo "No validation commands configured — update squad-preview.yml"
.github/workflows/squad-promote.yml new
+120
@@ -0,0 +1,120 @@
1 +name: Squad Promote
2 +
3 +on:
4 + workflow_dispatch:
5 + inputs:
6 + dry_run:
7 + description: 'Dry run — show what would happen without pushing'
8 + required: false
9 + default: 'false'
10 + type: choice
11 + options: ['false', 'true']
12 +
13 +permissions:
14 + contents: write
15 +
16 +jobs:
17 + dev-to-preview:
18 + name: Promote dev → preview
19 + runs-on: ubuntu-latest
20 + steps:
21 + - uses: actions/checkout@v4
22 + with:
23 + fetch-depth: 0
24 + token: ${{ secrets.GITHUB_TOKEN }}
25 +
26 + - name: Configure git
27 + run: |
28 + git config user.name "github-actions[bot]"
29 + git config user.email "github-actions[bot]@users.noreply.github.com"
30 +
31 + - name: Fetch all branches
32 + run: git fetch --all
33 +
34 + - name: Show current state (dry run info)
35 + run: |
36 + echo "=== dev HEAD ===" && git log origin/dev -1 --oneline
37 + echo "=== preview HEAD ===" && git log origin/preview -1 --oneline
38 + echo "=== Files that would be stripped ==="
39 + git diff origin/preview..origin/dev --name-only | grep -E "^(\.(ai-team|squad|ai-team-templates)|team-docs/|docs/proposals/)" || echo "(none)"
40 +
41 + - name: Merge dev → preview (strip forbidden paths)
42 + if: ${{ inputs.dry_run == 'false' }}
43 + run: |
44 + git checkout preview
45 + git merge origin/dev --no-commit --no-ff -X theirs || true
46 +
47 + # Strip forbidden paths from merge commit
48 + git rm -rf --cached --ignore-unmatch \
49 + .ai-team/ \
50 + .squad/ \
51 + .ai-team-templates/ \
52 + team-docs/ \
53 + "docs/proposals/" || true
54 +
55 + # Commit if there are staged changes
56 + if ! git diff --cached --quiet; then
57 + git commit -m "chore: promote dev → preview (v$(node -e "console.log(require('./package.json').version)"))"
58 + git push origin preview
59 + echo "✅ Pushed preview branch"
60 + else
61 + echo "ℹ️ Nothing to commit — preview is already up to date"
62 + fi
63 +
64 + - name: Dry run complete
65 + if: ${{ inputs.dry_run == 'true' }}
66 + run: echo "🔍 Dry run complete — no changes pushed."
67 +
68 + preview-to-main:
69 + name: Promote preview → main (release)
70 + needs: dev-to-preview
71 + runs-on: ubuntu-latest
72 + steps:
73 + - uses: actions/checkout@v4
74 + with:
75 + fetch-depth: 0
76 + token: ${{ secrets.GITHUB_TOKEN }}
77 +
78 + - name: Configure git
79 + run: |
80 + git config user.name "github-actions[bot]"
81 + git config user.email "github-actions[bot]@users.noreply.github.com"
82 +
83 + - name: Fetch all branches
84 + run: git fetch --all
85 +
86 + - name: Show current state
87 + run: |
88 + echo "=== preview HEAD ===" && git log origin/preview -1 --oneline
89 + echo "=== main HEAD ===" && git log origin/main -1 --oneline
90 + echo "=== Version ===" && node -e "console.log('v' + require('./package.json').version)"
91 +
92 + - name: Validate preview is release-ready
93 + run: |
94 + git checkout preview
95 + VERSION=$(node -e "console.log(require('./package.json').version)")
96 + if ! grep -q "## \[$VERSION\]" CHANGELOG.md 2>/dev/null; then
97 + echo "::error::Version $VERSION not found in CHANGELOG.md — update before releasing"
98 + exit 1
99 + fi
100 + echo "✅ Version $VERSION has CHANGELOG entry"
101 +
102 + # Verify no forbidden files on preview
103 + FORBIDDEN=$(git ls-files | grep -E "^(\.(ai-team|squad|ai-team-templates)/|team-docs/|docs/proposals/)" || true)
104 + if [ -n "$FORBIDDEN" ]; then
105 + echo "::error::Forbidden files found on preview: $FORBIDDEN"
106 + exit 1
107 + fi
108 + echo "✅ No forbidden files on preview"
109 +
110 + - name: Merge preview → main
111 + if: ${{ inputs.dry_run == 'false' }}
112 + run: |
113 + git checkout main
114 + git merge origin/preview --no-ff -m "chore: promote preview → main (v$(node -e "console.log(require('./package.json').version)"))"
115 + git push origin main
116 + echo "✅ Pushed main — squad-release.yml will tag and publish the release"
117 +
118 + - name: Dry run complete
119 + if: ${{ inputs.dry_run == 'true' }}
120 + run: echo "🔍 Dry run complete — no changes pushed."
.github/workflows/squad-release.yml new
+34
@@ -0,0 +1,34 @@
1 +name: Squad Release
2 +# python project — configure build, test, and release commands below
3 +
4 +on:
5 + push:
6 + branches: [main]
7 +
8 +permissions:
9 + contents: write
10 +
11 +jobs:
12 + release:
13 + runs-on: ubuntu-latest
14 + steps:
15 + - uses: actions/checkout@v4
16 + with:
17 + fetch-depth: 0
18 +
19 + - name: Build and test
20 + run: |
21 + # TODO: Add your python build/test commands here
22 + # Go: go test ./...
23 + # Python: pip install -r requirements.txt && pytest
24 + # .NET: dotnet test
25 + # Java (Maven): mvn test
26 + # Java (Gradle): ./gradlew test
27 + echo "No build commands configured — update squad-release.yml"
28 +
29 + - name: Create release
30 + env:
31 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32 + run: |
33 + # TODO: Add your release commands here (e.g., git tag, gh release create)
34 + echo "No release commands configured — update squad-release.yml"
.squad/decisions.md
+41
@@ -736,3 +736,44 @@ Do not open a separate crawler/RSS matrix issue from this run. The existing PRD/
736 **Outcome:** decisions.md grew from 45948 → 91562 bytes. Inbox purged. No duplicates found in merge. Added 5 decision dividers. Content addresses crawl matrix topology, analysis map/reduce experiment design, QA gates/tests, run diagnostics, and fallback strategy.
737
738 **No archiving trigger:** decisions.md is still within typical document lifecycle size; existing PRD scope is fresh and actionable.
739 +
740 +---
741 +
742 +## Leela: Analysis rerun safety issue plan
743 +
744 +Created: 2026-06-05T20:46:00.582+00:00
745 +
746 +### Parent epic
747 +
748 +- #248 — [Protect published weekly analysis from unsafe reruns](https://github.com/jmservera/SquadScope/issues/248)
749 +
750 +### Immediate objective
751 +
752 +Stop failed, degraded, low-quality, or no-AI analysis reruns from overwriting a good published weekly article. This protection should land before map/reduce implementation changes can affect publication.
753 +
754 +### Child issues hierarchy
755 +
756 +**P0 Safety Layer (11 issues):**
757 +- #249 — Add candidate staging and publish eligibility manifest for analysis outputs | Bender | type:feature, priority:p0
758 +- #250 — Preserve existing good weekly analysis on failed/degraded reruns | Bender | type:feature, priority:p0
759 +- #251 — Block no-AI fallback from replacing AI-authored weekly summaries by default | Farnsworth | type:feature, priority:p0, rai
760 +- #252 — Add explicit safe rerun modes and restore workflow controls | Leela | type:feature, priority:p0
761 +- #253 — Add immutable backups and publish-branch concurrency safeguards | Bender | type:feature, priority:p0
762 +- #254 — Make weekly promotion atomic across analyzed/content/deploy/notify | Bender | type:feature, priority:p0
763 +- #255 — Strengthen analysis publish gate beyond structural validation | Farnsworth | type:feature, priority:p0, rai
764 +- #257 — Add overwrite-protection and rerun idempotency regression tests | Fry | type:feature, priority:p0
765 +
766 +**P1 Quality/Run Readiness (2 issues):**
767 +- #256 — Add preflight compaction and fallback policy for next analysis run | Farnsworth | type:feature, priority:p1
768 +- #259 — Document safe rerun, force-replace, and restore operations | Leela | type:docs, priority:p1
769 +
770 +**P2 Future Analysis Architecture (1 issue):**
771 +- #258 — Add map/reduce dry-run with claim-ledger contracts and QA comparison gates | Farnsworth with Fry QA support | type:feature, priority:p2, rai
772 +
773 +### Summary
774 +
775 +Safety-first protection layer for analysis reruns across staging/publish workflow. Prevents silent overwrite of good weekly articles on transient failures, low-quality output, or no-AI fallback misuse. Prioritizes atomic promotion, eligibility gates, and immutable backups before rolling out map/reduce.
776 +
777 +### Notes
778 +
779 +GitHub issue hierarchy represented via parent #248 with linked child issues and inline comments. All issues labeled `squad` with per-owner tracking.
.squad/templates/fact-checker-charter.md new
+83
@@ -0,0 +1,83 @@
1 +# Fact Checker
2 +
3 +> Trust, but verify. Every claim gets a source check.
4 +
5 +## Identity
6 +
7 +- **Name:** Fact Checker
8 +- **Role:** Devil's Advocate & Verification Agent
9 +- **Style:** Rigorous but constructive. Flags issues clearly without being abrasive.
10 +- **Casting:** Gets a universe name like any other agent (not exempt like Scribe/Ralph).
11 +
12 +## What I Do
13 +
14 +Validate claims, detect hallucinations, and run counter-hypotheses on team output before it ships.
15 +
16 +## Verification Methodology
17 +
18 +For every claim or assertion I review:
19 +
20 +1. **Source Check:** What evidence supports this? Can I verify it?
21 +2. **Counter-Hypothesis:** What would disprove this? Is there an alternative explanation?
22 +3. **Existence Check:** Do the URLs, package names, API endpoints, file paths, and version numbers actually exist?
23 +4. **Consistency Check:** Does this contradict anything in `.squad/decisions.md` or prior team output?
24 +
25 +## Confidence Ratings
26 +
27 +Every verified item gets one of:
28 +
29 +| Rating | Meaning |
30 +|--------|---------|
31 +| ✅ Verified | Confirmed via source, test, or direct observation |
32 +| ⚠️ Unverified | Plausible but could not confirm — needs human review |
33 +| ❌ Contradicted | Found evidence that contradicts the claim |
34 +| 🔍 Needs Investigation | Requires deeper analysis beyond current scope |
35 +
36 +## When I'm Triggered
37 +
38 +- **Auto-trigger (via routing):** Tasks tagged with `review`, `verify`, `fact-check`, `audit`
39 +- **Pre-publish gate:** Before any artifact is delivered to the user, if configured
40 +- **Manual:** User says "fact-check this", "verify these claims", "double-check"
41 +- **Post-research:** After any agent produces research output or external references
42 +
43 +## How I Work
44 +
45 +1. **Read the artifact** — understand what's being claimed
46 +2. **Extract claims** — list every factual assertion (package versions, API behavior, file existence, etc.)
47 +3. **Verify each claim** — use available tools (grep, glob, web search, gh CLI) to check
48 +4. **Run counter-hypotheses** — for key assumptions, ask "what if this is wrong?"
49 +5. **Produce a verification report:**
50 +
51 +```markdown
52 +## Verification Report — {artifact name}
53 +
54 +### Claims Verified
55 +- ✅ {claim} — confirmed via {source}
56 +- ⚠️ {claim} — could not verify, {reason}
57 +- ❌ {claim} — contradicted by {evidence}
58 +
59 +### Counter-Hypotheses
60 +- {assumption} → Alternative: {counter}
61 +
62 +### Recommendation
63 +{proceed / revise / block with reasons}
64 +```
65 +
66 +6. **Write decision** if I found issues: `.squad/decisions/inbox/fact-checker-{slug}.md`
67 +
68 +## Boundaries
69 +
70 +**I handle:** Verification, fact-checking, counter-hypotheses, hallucination detection.
71 +
72 +**I don't handle:** Implementation, design, testing, or docs. I review, not create.
73 +
74 +**I am not a blocker by default.** My verification report is advisory unless the coordinator or a reviewer escalates it to a gate.
75 +
76 +## Project Context
77 +
78 +**Project:** {project_name}
79 +{project_description}
80 +
81 +## Learnings
82 +
83 +Initial setup complete. Ready for verification work.
.squad/templates/loop.md new
+46
@@ -0,0 +1,46 @@
1 +---
2 +configured: false
3 +interval: 10
4 +timeout: 30
5 +description: "My squad work loop"
6 +---
7 +
8 +# Squad Work Loop
9 +
10 +> ⚠️ Set `configured: true` in the frontmatter above to activate this loop.
11 +> Run with: `squad loop`
12 +
13 +## What to do each cycle
14 +
15 +Describe what your squad should do every time the loop wakes up. Be specific —
16 +the more context you give, the better your squad performs.
17 +
18 +Examples:
19 +- Check for new messages in a Teams channel and summarize action items
20 +- Review recent pull requests and flag anything needing attention
21 +- Run a health check on staging and report anomalies
22 +- Scan the inbox for anything that needs a response today
23 +
24 +<!-- Replace this section with your actual loop instructions. -->
25 +
26 +## Monitoring (optional)
27 +
28 +If you want your squad to watch external channels, enable monitor capabilities:
29 +
30 +```bash
31 +squad loop --monitor-email --monitor-teams
32 +```
33 +
34 +## Personality (optional)
35 +
36 +If your squad has a specific voice or style, describe it here so each cycle
37 +stays consistent.
38 +
39 +Example: "Be concise. Use bullet points. Flag blockers clearly."
40 +
41 +## Tips
42 +
43 +- **Be specific.** Vague prompts produce vague results.
44 +- **Set boundaries.** Tell the squad what NOT to do (e.g., "Don't send messages to anyone but me").
45 +- **Start small.** Begin with one task per cycle, then expand.
46 +- **Use frontmatter.** `interval` controls how often the loop runs. `timeout` caps each cycle.