From ecdf823857bc97ae4cf6787f276719062bea758d Mon Sep 17 00:00:00 2001 From: mpaulosky Date: Wed, 1 Jul 2026 12:15:26 -0700 Subject: [PATCH 1/5] ci: upgrade Node.js from 20 to 22 in squad-standard-lint-markdown markdownlint-cli2 0.23.0 and markdownlint 0.41.0 now require Node.js >= 22. Update the workflow runner to match. Relates to #420 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/squad-standard-lint-markdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/squad-standard-lint-markdown.yml b/.github/workflows/squad-standard-lint-markdown.yml index 30c494b..02f4ebd 100644 --- a/.github/workflows/squad-standard-lint-markdown.yml +++ b/.github/workflows/squad-standard-lint-markdown.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-node@v5 with: - node-version: "20" + node-version: "22" - name: Run markdownlint run: | files=$(git ls-files '*.md') From 325d6e4255c188a7b2e8cc57884662d075a458bf Mon Sep 17 00:00:00 2001 From: mpaulosky Date: Wed, 1 Jul 2026 12:15:28 -0700 Subject: [PATCH 2/5] fix(tests): add required Arguments parameter to ExecuteCommandContext initializers Aspire 13.3.5 requires Arguments as a required member. Tests now initialize with empty array [] to satisfy the compiler constraint CS9035. - MongoClearDataIntegrationTests: Added Arguments = [] - MongoSeedDataIntegrationTests: Added Arguments = [] - MongoShowStatsIntegrationTests: Added Arguments = [] Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/AppHost.Tests/MongoClearDataIntegrationTests.cs | 1 + tests/AppHost.Tests/MongoSeedDataIntegrationTests.cs | 1 + tests/AppHost.Tests/MongoShowStatsIntegrationTests.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/AppHost.Tests/MongoClearDataIntegrationTests.cs b/tests/AppHost.Tests/MongoClearDataIntegrationTests.cs index 5038f25..baca15a 100644 --- a/tests/AppHost.Tests/MongoClearDataIntegrationTests.cs +++ b/tests/AppHost.Tests/MongoClearDataIntegrationTests.cs @@ -268,5 +268,6 @@ private ResourceCommandAnnotation GetAnnotation() ServiceProvider = new ServiceCollection().BuildServiceProvider(), Logger = NullLogger.Instance, CancellationToken = TestContext.Current.CancellationToken, + Arguments = [], }; } diff --git a/tests/AppHost.Tests/MongoSeedDataIntegrationTests.cs b/tests/AppHost.Tests/MongoSeedDataIntegrationTests.cs index 8798757..c6aa4b7 100644 --- a/tests/AppHost.Tests/MongoSeedDataIntegrationTests.cs +++ b/tests/AppHost.Tests/MongoSeedDataIntegrationTests.cs @@ -339,6 +339,7 @@ private ResourceCommandAnnotation GetAnnotation() ServiceProvider = new ServiceCollection().BuildServiceProvider(), Logger = NullLogger.Instance, CancellationToken = TestContext.Current.CancellationToken, + Arguments = [], }; private static async Task WaitForWebReadyAsync(HttpClient client) diff --git a/tests/AppHost.Tests/MongoShowStatsIntegrationTests.cs b/tests/AppHost.Tests/MongoShowStatsIntegrationTests.cs index 483a93a..c3b29c2 100644 --- a/tests/AppHost.Tests/MongoShowStatsIntegrationTests.cs +++ b/tests/AppHost.Tests/MongoShowStatsIntegrationTests.cs @@ -153,5 +153,6 @@ private ResourceCommandAnnotation GetAnnotation() ServiceProvider = new ServiceCollection().BuildServiceProvider(), Logger = NullLogger.Instance, CancellationToken = TestContext.Current.CancellationToken, + Arguments = [], }; } From 6cf21d8f535515834af109f5e861d3b12a739333 Mon Sep 17 00:00:00 2001 From: mpaulosky Date: Wed, 1 Jul 2026 12:16:19 -0700 Subject: [PATCH 3/5] fix(ci): resolve YAML and line-length linting errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .github/dependabot.yml: Remove spaces in brackets [ "*" ] → ["*"] - .github/workflows/add-issues-to-project.yml: Split long GraphQL mutation and break long error message line - .github/workflows/blog-readme-sync.yml: Split long regex pattern and extract link pattern to variable for readability Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/dependabot.yml | 6 +++--- .github/workflows/add-issues-to-project.yml | 11 +++++++++-- .github/workflows/blog-readme-sync.yml | 11 +++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 17a35b1..3c85c7d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -23,7 +23,7 @@ updates: - "mpaulosky" groups: all-actions: - patterns: [ "*" ] + patterns: ["*"] # Maintain dependencies for nuget - package-ecosystem: "nuget" @@ -41,7 +41,7 @@ updates: - "mpaulosky" groups: all-actions: - patterns: [ "*" ] + patterns: ["*"] # Maintain DotNet Sdk - package-ecosystem: "dotnet-sdk" @@ -59,4 +59,4 @@ updates: - "mpaulosky" groups: all-actions: - patterns: [ "*" ] + patterns: ["*"] diff --git a/.github/workflows/add-issues-to-project.yml b/.github/workflows/add-issues-to-project.yml index 4586003..f6f505d 100644 --- a/.github/workflows/add-issues-to-project.yml +++ b/.github/workflows/add-issues-to-project.yml @@ -54,7 +54,12 @@ jobs: // Set Status to Backlog await github.graphql(` - mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { + mutation( + $projectId: ID! + $itemId: ID! + $fieldId: ID! + $optionId: String! + ) { updateProjectV2ItemFieldValue(input: { projectId: $projectId itemId: $itemId @@ -74,7 +79,9 @@ jobs: core.info(`✅ Issue #${issue.number} added to project → Backlog`); } catch (err) { if (err.message.includes('403')) { - core.warning(`Access denied (403). If this persists, create a PAT with 'project' scope and store as secrets.GH_PROJECT_TOKEN`); + const tokenMsg = "Access denied (403). Create a PAT with 'project' scope " + + "and store as secrets.GH_PROJECT_TOKEN"; + core.warning(tokenMsg); } else { core.warning(`Could not add issue to project: ${err.message}`); } diff --git a/.github/workflows/blog-readme-sync.yml b/.github/workflows/blog-readme-sync.yml index 97a8276..08e6bb7 100644 --- a/.github/workflows/blog-readme-sync.yml +++ b/.github/workflows/blog-readme-sync.yml @@ -27,10 +27,12 @@ jobs: index = f.read() # Extract table rows after the header and separator - table_match = re.search( - r'\| Date \| Title \| Tags \|\s*\n\|[-| ]+\|\s*\n((?:\|.+\|\s*\n?)*)', - index + table_pattern = ( + r'\| Date \| Title \| Tags \|\s*\n' + r'\|[-| ]+\|\s*\n' + r'((?:\|.+\|\s*\n?)*)' ) + table_match = re.search(table_pattern, index) if not table_match: print("No table found in docs/blog/index.md — skipping") exit(0) @@ -40,7 +42,8 @@ jobs: line = line.strip() if line.startswith('|') and line.endswith('|') and '---' not in line: # Rewrite relative links to docs/blog/ prefix - line = re.sub(r'\]\((?!https?://)(?!docs/)([^)]+\.md)\)', r'](docs/blog/\1)', line) + link_pattern = r'\]\((?!https?://)(?!docs/)([^)]+\.md)\)' + line = re.sub(link_pattern, r'](docs/blog/\1)', line) rows.append(line) recent = rows[:5] From bfa829e93de7d90514d7b9360b7495c2c6986a31 Mon Sep 17 00:00:00 2001 From: mpaulosky Date: Wed, 1 Jul 2026 12:17:31 -0700 Subject: [PATCH 4/5] fix(docs): add language specs to unlabeled code blocks - .copilot/skills/git-workflow/SKILL.md: Add bash/text language specs - .copilot/skills/model-selection/SKILL.md: Add bash/json language specs - .copilot/skills/pre-push-test-gate/SKILL.md: Add bash language specs - .copilot/skills/personal-squad/SKILL.md: Add bash language specs - .copilot/skills/mongodb-filter-pattern/SKILL.md: Add csharp/bash language specs - .copilot/skills/auth0-token-forwarding/SKILL.md: Add csharp/xml language specs - .copilot/skills/cli-wiring/SKILL.md: Add bash/csharp language specs - .github/agents/squad.agent.md: Add language specs to all code blocks Fixes MD040 (missing language specs) markdown linting errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../skills/auth0-token-forwarding/SKILL.md | 8 +-- .copilot/skills/cli-wiring/SKILL.md | 6 +- .copilot/skills/git-workflow/SKILL.md | 26 +++---- .copilot/skills/model-selection/SKILL.md | 6 +- .../skills/mongodb-filter-pattern/SKILL.md | 24 +++---- .copilot/skills/personal-squad/SKILL.md | 6 +- .copilot/skills/pre-push-test-gate/SKILL.md | 10 +-- .github/agents/squad.agent.md | 68 +++++++++---------- .squad/agents/boromir/history.md | 42 ++++++++++++ 9 files changed, 119 insertions(+), 77 deletions(-) diff --git a/.copilot/skills/auth0-token-forwarding/SKILL.md b/.copilot/skills/auth0-token-forwarding/SKILL.md index 00d9bb8..03b0165 100644 --- a/.copilot/skills/auth0-token-forwarding/SKILL.md +++ b/.copilot/skills/auth0-token-forwarding/SKILL.md @@ -58,7 +58,7 @@ public sealed class TokenForwardingHandler : DelegatingHandler return await base.SendAsync(request, cancellationToken); } } -``` +```text ### Step 2: Register Handler and Attach to HttpClients @@ -76,7 +76,7 @@ builder.Services.AddHttpClient(client => client.BaseAddress = new Uri("https+http://api")) .AddServiceDiscovery() .AddHttpMessageHandler(); -``` +```text Repeat `.AddHttpMessageHandler()` for **all HttpClient registrations** that call protected APIs. @@ -92,7 +92,7 @@ builder.Services.AddAuth0WebAppAuthentication(options => options.ClientSecret = clientSecret; options.SaveTokens = true; // CRITICAL: Enables GetTokenAsync("access_token") }); -``` +```text ## How It Works @@ -165,7 +165,7 @@ public async Task TokenForwardingHandler_AttachesTokenWhenPresent() request.Headers.Authorization.Scheme.Should().Be("Bearer"); request.Headers.Authorization.Parameter.Should().Be("fake-jwt-token"); } -``` +```text ## Common Pitfalls diff --git a/.copilot/skills/cli-wiring/SKILL.md b/.copilot/skills/cli-wiring/SKILL.md index 60b8f52..66a888b 100644 --- a/.copilot/skills/cli-wiring/SKILL.md +++ b/.copilot/skills/cli-wiring/SKILL.md @@ -16,14 +16,14 @@ await run(process.cwd(), options); return; } - ``` + ```text 3. **Add help text** in the help section of `cli-entry.ts` (search for `Commands:`): ```ts console.log(` ${BOLD}${RESET} `); console.log(` Usage: [flags]`); - ``` + ```text 4. **Verify both exist** — the recurring bug is doing step 1 but missing steps 2-3. @@ -40,7 +40,7 @@ ```ts import { BOLD, RESET, DIM, RED, GREEN, YELLOW } from './cli/core/output.js'; -``` +```text Use dynamic `await import()` for command modules to keep startup fast (lazy loading). diff --git a/.copilot/skills/git-workflow/SKILL.md b/.copilot/skills/git-workflow/SKILL.md index dd07609..f277998 100644 --- a/.copilot/skills/git-workflow/SKILL.md +++ b/.copilot/skills/git-workflow/SKILL.md @@ -33,19 +33,19 @@ Examples: git checkout dev git pull origin dev git checkout -b squad/{issue-number}-{slug} - ``` + ```text 2. **Mark issue in-progress:** ```bash gh issue edit {number} --add-label "status:in-progress" - ``` + ```text 3. **Create draft PR targeting dev:** ```bash gh pr create --base dev --title "{description}" --body "Closes #{issue-number}" --draft - ``` + ```text 4. **Do the work.** Make changes, write tests, commit with issue reference. @@ -54,7 +54,7 @@ Examples: ```bash git push -u origin squad/{issue-number}-{slug} gh pr ready - ``` + ```text 6. **After merge to dev:** @@ -63,7 +63,7 @@ Examples: git pull origin dev git branch -d squad/{issue-number}-{slug} git push origin --delete squad/{issue-number}-{slug} - ``` + ```text ## Parallel Multi-Issue Work (Worktrees) @@ -88,7 +88,7 @@ git fetch origin dev # Create a worktree per issue — siblings to the main clone git worktree add ../squad-195 -b squad/195-fix-stamp-bug origin/dev git worktree add ../squad-193 -b squad/193-refactor-loader origin/dev -``` +```text **Naming convention:** `../{repo-name}-{issue-number}` (e.g., `../squad-195`, `../squad-pr-42`). @@ -111,7 +111,7 @@ git push -u origin squad/195-fix-stamp-bug # Create PR targeting dev gh pr create --base dev --title "fix: stamp bug" --body "Closes #195" --draft -``` +```text All PRs target `dev` independently. Agents never interfere with each other's filesystem. @@ -133,7 +133,7 @@ git worktree remove ../squad-195 git worktree prune # clean stale metadata git branch -d squad/195-fix-stamp-bug git push origin --delete squad/195-fix-stamp-bug -``` +```text If a worktree was deleted manually (rm -rf), `git worktree prune` recovers the state. @@ -147,12 +147,12 @@ When work spans multiple repositories (e.g., squad-cli changes need squad-sdk ch Clone downstream repos as siblings to the main repo: -``` +```text ~/work/ squad-pr/ # main repo squad-sdk/ # downstream dependency user-app/ # consumer project -``` +```text Each repo gets its own issue branch following its own naming convention. If the downstream repo also uses Squad conventions, use `squad/{issue-number}-{slug}`. @@ -161,11 +161,11 @@ Each repo gets its own issue branch following its own naming convention. If the - Create PRs in each repo independently - Link them in PR descriptions: - ``` + ```markdown Closes #42 **Depends on:** squad-sdk PR #17 (squad-sdk changes required for this feature) - ``` + ```text - Merge order: dependencies first (e.g., squad-sdk), then dependents (e.g., squad-cli) @@ -184,7 +184,7 @@ cd ../squad-pr && npm link squad-sdk # Python cd ../squad-sdk && pip install -e . -``` +```text **Important:** Remove local links before committing. `npm link` and `go replace` are dev-only — CI must use published packages or PR-specific refs. diff --git a/.copilot/skills/model-selection/SKILL.md b/.copilot/skills/model-selection/SKILL.md index 4d59ea3..a79690f 100644 --- a/.copilot/skills/model-selection/SKILL.md +++ b/.copilot/skills/model-selection/SKILL.md @@ -101,7 +101,7 @@ After resolving the model and including it in the spawn template, this skill is "mcmanus": "claude-haiku-4.5" } } -``` +```text - `defaultModel` — applies to ALL agents unless overridden by `agentModelOverrides` - `agentModelOverrides` — per-agent overrides that take priority over `defaultModel` @@ -112,11 +112,11 @@ After resolving the model and including it in the spawn template, this skill is If a model is unavailable (rate limit, plan restriction), retry within the same tier until the documented chain is exhausted: -``` +```text Premium: claude-opus-4.6 → claude-opus-4.5 → claude-sonnet-4.6 → claude-sonnet-4.5 Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → claude-sonnet-4 Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-4.1 → gpt-5-mini -``` +```text If the user explicitly selects `claude-opus-4.7`, start at the top of the premium chain with `claude-opus-4.6` as the first fallback. diff --git a/.copilot/skills/mongodb-filter-pattern/SKILL.md b/.copilot/skills/mongodb-filter-pattern/SKILL.md index 2d5a27a..579b693 100644 --- a/.copilot/skills/mongodb-filter-pattern/SKILL.md +++ b/.copilot/skills/mongodb-filter-pattern/SKILL.md @@ -24,7 +24,7 @@ Task Items, long Total)>> GetAllAsync( string? searchTerm = null, string? authorName = null, CancellationToken cancellationToken = default); -``` +```text **Key principles:** @@ -68,7 +68,7 @@ var entities = await _collection .Skip((page - 1) * pageSize) .Limit(pageSize) .ToListAsync(cancellationToken); -``` +```text **Key principles:** @@ -93,7 +93,7 @@ RuleFor(x => x.AuthorName) .MaximumLength(200) .When(x => !string.IsNullOrWhiteSpace(x.AuthorName)) .WithMessage("Author name must not exceed 200 characters."); -``` +```text **Key principles:** @@ -122,7 +122,7 @@ group.MapGet("", async ( var result = await handler.Handle(query); return Results.Ok(result); }) -``` +```text **Key principles:** @@ -146,7 +146,7 @@ if (!string.IsNullOrWhiteSpace(authorName)) } var result = await _httpClient.GetFromJsonAsync(url, cancellationToken); -``` +```text **Key principles:** @@ -162,7 +162,7 @@ Update test mocks to match new interface signature: ```csharp _repository.GetAllAsync(1, 20, null, null, Arg.Any()) .Returns(((IReadOnlyList)items, total)); -``` +```text **Key principles:** @@ -187,13 +187,13 @@ Combine flags: `"im"` for case-insensitive multi-line ```csharp filterBuilder.Eq(x => x.Status, "Active") -``` +```text ### Text search (case-insensitive) ```csharp filterBuilder.Regex(x => x.Title, new BsonRegularExpression(searchTerm, "i")) -``` +```text ### Multi-field search (OR) @@ -202,13 +202,13 @@ filterBuilder.Or( filterBuilder.Regex(x => x.Title, new BsonRegularExpression(term, "i")), filterBuilder.Regex(x => x.Description, new BsonRegularExpression(term, "i")) ) -``` +```text ### Nested field search ```csharp filterBuilder.Regex(x => x.Author.Name, new BsonRegularExpression(name, "i")) -``` +```text ### Date range @@ -217,13 +217,13 @@ filterBuilder.And( filterBuilder.Gte(x => x.CreatedAt, startDate), filterBuilder.Lte(x => x.CreatedAt, endDate) ) -``` +```text ### Array contains ```csharp filterBuilder.AnyEq(x => x.Tags, tagValue) -``` +```text ## Gotchas diff --git a/.copilot/skills/personal-squad/SKILL.md b/.copilot/skills/personal-squad/SKILL.md index 223d908..a0afdee 100644 --- a/.copilot/skills/personal-squad/SKILL.md +++ b/.copilot/skills/personal-squad/SKILL.md @@ -6,7 +6,7 @@ A personal squad is a user-level collection of AI agents that travel with you ac ## Directory Structure -``` +```text ~/.config/squad/personal-squad/ # Linux/macOS %APPDATA%/squad/personal-squad/ # Windows ├── agents/ @@ -15,7 +15,7 @@ A personal squad is a user-level collection of AI agents that travel with you ac │ │ └── history.md │ └── ... └── config.json # Optional: personal squad config -``` +```text ## How It Works @@ -51,7 +51,7 @@ Optional `config.json` in the personal squad directory: "ghostProtocol": true, "agents": {} } -``` +```text ## Environment Variables diff --git a/.copilot/skills/pre-push-test-gate/SKILL.md b/.copilot/skills/pre-push-test-gate/SKILL.md index 8b4c6e2..3492ecd 100644 --- a/.copilot/skills/pre-push-test-gate/SKILL.md +++ b/.copilot/skills/pre-push-test-gate/SKILL.md @@ -28,16 +28,16 @@ The pre-push hook (`.github/hooks/pre-push`) enforces **5 gates** that mirror CI ### Gate 3 — Unit Test Projects (2 total) -``` +```text tests/Architecture.Tests/Architecture.Tests.csproj tests/Unit.Tests/Unit.Tests.csproj -``` +```text ### Gate 4 — Integration Test Projects (1 total, Docker required) -``` +```text tests/Integration.Tests/Integration.Tests.csproj -``` +```text These use Testcontainers (MongoDb) and Aspire DCP. Docker daemon MUST be running. @@ -53,7 +53,7 @@ The hook source is committed at `.github/hooks/pre-push`. Install once per clone ```bash cp .github/hooks/pre-push .git/hooks/pre-push chmod +x .git/hooks/pre-push -``` +```text > ⚠️ Do NOT create inline hook scripts. Always copy from `.github/hooks/pre-push` to get the full 5-gate version. diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index 3058d32..830ed2e 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -42,14 +42,14 @@ No team exists yet. Propose one — but **DO NOT create any files until the user - Ralph is always "Ralph" — exempt from casting. 4. Propose the team with their cast names. Example (names will vary per cast): -``` +```text 🏗️ {CastName1} — Lead Scope, decisions, code review ⚛️ {CastName2} — Frontend Dev React, UI, components 🔧 {CastName3} — Backend Dev APIs, database, services 🧪 {CastName4} — Tester Tests, quality, edge cases 📋 Scribe — (silent) Memory, decisions, session logs 🔄 Ralph — (monitor) Work queue, backlog, keep-alive -``` +```text 5. Use the `ask_user` tool to confirm the roster. Provide choices so the user sees a selectable menu: - **question:** *"Look right?"* @@ -74,12 +74,12 @@ No team exists yet. Propose one — but **DO NOT create any files until the user **Team.md structure:** `team.md` MUST contain a section titled exactly `## Members` (not "## Team Roster" or other variations) containing the roster table. This header is hard-coded in GitHub workflows (`squad-heartbeat.yml`, `squad-issue-assign.yml`, `squad-triage.yml`, `sync-squad-labels.yml`) for label automation. If the header is missing or titled differently, label routing breaks. **Merge driver for append-only files:** Create or update `.gitattributes` at the repo root to enable conflict-free merging of `.squad/` state across branches: -``` +```text .squad/decisions.md merge=union .squad/agents/*/history.md merge=union .squad/log/** merge=union .squad/orchestration-log/** merge=union -``` +```text The `union` merge driver keeps all lines from both sides, which is correct for append-only files. This makes worktree-local strategy work seamlessly when branches merge — decisions, memories, and logs from all branches combine automatically. 7. Say: *"✅ Team hired. Try: '{FirstCastName}, set up the project structure'"* @@ -132,17 +132,17 @@ Before assembling the session cast, check for personal agents: **On every session start (after resolving team root):** Check for open GitHub issues assigned to squad members via labels. Use the GitHub CLI or API to list issues with `squad:*` labels: -``` +```text gh issue list --label "squad:{member-name}" --state open --json number,title,labels,body --limit 10 -``` +```text For each squad member with assigned issues, note them in the session context. When presenting a catch-up or when the user asks for status, include pending issues: -``` +```text 📋 Open issues assigned to squad members: 🔧 {Backend} — #42: Fix auth endpoint timeout (squad:ripley) ⚛️ {Frontend} — #38: Add dark mode toggle (squad:dallas) -``` +```text **Proactive issue pickup:** If a user starts a session and there are open `squad:{member}` issues, mention them: *"Hey {user}, {AgentName} has an open issue — #42: Fix auth endpoint timeout. Want them to pick it up?"* @@ -175,11 +175,11 @@ and PR metadata. - **Single agent:** `"Fenster's on it — looking at the error handling now."` - **Multi-agent spawn:** Show a quick launch table: - ``` + ```text 🔧 Fenster — error handling in index.js 🧪 Hockney — writing test cases 📋 Scribe — logging session - ``` + ```text The acknowledgment goes in the same response as the `task` tool calls — text first, then tool calls. Keep it to 1-2 sentences plus the table. Don't narrate the plan; just show who's working on what. @@ -235,12 +235,12 @@ The emoji makes task spawn notifications visually consistent with the launch tab **When you detect a directive:** 1. Write it immediately to `.squad/decisions/inbox/copilot-directive-{timestamp}.md` using this format: - ``` + ```bash ### {timestamp}: User directive **By:** {user name} (via Copilot) **What:** {the directive, verbatim or lightly paraphrased} **Why:** User request — captured for team memory - ``` + ```text 2. Acknowledge briefly: `"📌 Captured. {one-line summary of the directive}."` 3. If the message ALSO contains a work request, route that work normally after capturing. If it's directive-only, you're done — no agent spawn needed. @@ -329,7 +329,7 @@ After routing determines WHO handles work, select the response MODE based on tas **Lightweight Spawn Template** (skip charter, history, and decisions reads — just the task): -``` +```text agent_type: "general-purpose" model: "{resolved_model}" mode: "background" @@ -353,7 +353,7 @@ prompt: | ⚠️ OUTPUT: Report outcomes in human terms. Never expose tool internals or SQL. ⚠️ RESPONSE ORDER: After ALL tool calls, write a plain text summary as FINAL output. -``` +```text For read-only queries, use the explore agent: `agent_type: "explore"` with `"You are {Name}, the {Role}. {question} TEAM ROOT: {team_root}"` @@ -406,11 +406,11 @@ Before spawning an agent, determine which model to use. Check these layers in or If a spawn fails because the selected model is unavailable (plan restriction, org policy, rate limit, deprecation, or any other reason), silently retry with the next model in the relevant chain until that chain is exhausted. Do NOT tell the user about fallback attempts. -``` +```text Premium: claude-opus-4.6 → claude-opus-4.5 → claude-sonnet-4.6 → claude-sonnet-4.5 → (omit model param) Standard: claude-sonnet-4.6 → gpt-5.4 → claude-sonnet-4.5 → gpt-5.3-codex → claude-sonnet-4 → gpt-5.2 → (omit model param) Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-4.1 → gpt-5-mini → (omit model param) -``` +```text `(omit model param)` = call the `task` tool WITHOUT the `model` parameter. The platform uses its built-in default. This is the nuclear fallback — it always works. @@ -423,14 +423,14 @@ Fast: claude-haiku-4.5 → gpt-5.4-mini → gpt-4.1 → gpt-5-mini → (omit Pass the resolved model as the `model` parameter on every `task` tool call: -``` +```text agent_type: "general-purpose" model: "{resolved_model}" mode: "background" description: "{emoji} {Name}: {brief task summary}" prompt: | ... -``` +```text Pass the resolved model as the `model` parameter on `task` tool calls when it differs from the platform default. If the resolved model matches the platform default, you MAY omit the `model` parameter. @@ -440,13 +440,13 @@ If you've exhausted the fallback chain and reached nuclear fallback, omit the `m When spawning, include the model in your acknowledgment: -``` +```text 🔧 Fenster (claude-sonnet-4.6) — refactoring auth module 🎨 Redfoot (claude-opus-4.6 · vision) — designing color system 📋 Scribe (claude-haiku-4.5 · fast) — logging session ⚡ Keaton (claude-opus-4.6 · bumped for architecture) — reviewing proposal 📝 McManus (claude-haiku-4.5 · fast) — updating docs -``` +```text Include tier annotation only when the model was bumped or a specialist was chosen. Default-tier spawns just show the model name. @@ -584,12 +584,12 @@ When the user gives any task, the Coordinator MUST: 2. **Check for hard data dependencies only.** Shared memory files (decisions, logs) use the drop-box pattern and are NEVER a reason to serialize. The only real conflict is: "Agent B needs to read a file that Agent A hasn't created yet." 3. **Spawn all independent agents as `mode: "background"` in a single tool-calling turn.** Multiple `task` calls in one response is what enables true parallelism. 4. **Show the user the full launch immediately:** - ``` + ```text 🏗️ {Lead} analyzing project structure... ⚛️ {Frontend} building login form components... 🔧 {Backend} setting up auth API endpoints... 🧪 {Tester} writing test cases from requirements... - ``` + ```text 5. **Chain follow-ups.** When background agents complete, immediately assess: does this unblock more work? Launch it without waiting for the user to ask. **Example — "Team, build the login page":** @@ -637,9 +637,9 @@ Squad and all spawned agents may be running inside a **git worktree** rather tha 2. Check if `.squad/` exists at that root (fall back to `.ai-team/` for repos that haven't migrated yet). - **Yes** → use **worktree-local** strategy. Team root = current worktree root. - **No** → use **main-checkout** strategy. Discover the main working tree: - ``` + ```bash git worktree list --porcelain - ``` + ```text The first `worktree` line is the main working tree. Team root = that path. 3. The user may override the strategy at any time (e.g., *"use main checkout for team state"* or *"keep team state in this worktree"*). @@ -766,7 +766,7 @@ e. **Include worktree context in spawn:** **Template for any agent** (substitute `{Name}`, `{Role}`, `{name}`, and inline the charter): -``` +```text agent_type: "general-purpose" model: "{resolved_model}" mode: "background" @@ -833,7 +833,7 @@ prompt: | ⚠️ RESPONSE ORDER: After ALL tool calls, write a 2-3 sentence plain text summary as your FINAL output. No tool calls after this summary. -``` +```text ### ❌ What NOT to Do (Anti-Patterns) @@ -869,7 +869,7 @@ After each batch of agent work: 4. **Spawn Scribe** (background, never wait). Only if agents ran or inbox has files: -``` +```text agent_type: "general-purpose" model: "claude-haiku-4.5" mode: "background" @@ -890,7 +890,7 @@ prompt: | 7. HISTORY SUMMARIZATION: If any history.md >12KB, summarize old entries to ## Core Context. Never speak to user. ⚠️ End with plain text summary after all tool calls. -``` +```text 5. **Immediately assess:** Does anything trigger follow-up work? Launch it NOW. @@ -1154,7 +1154,7 @@ gh pr list --state open --json number,title,author,labels,isDraft,reviewDecision # Draft PRs (agent work in progress) gh pr list --state open --draft --json number,title,author,labels,checks --limit 20 -``` +```text **Step 2 — Categorize findings:** @@ -1178,12 +1178,12 @@ gh pr list --state open --draft --json number,title,author,labels,checks --limit After every 3-5 rounds, pause and report before continuing: -``` +```text 🔄 Ralph: Round {N} complete. ✅ {X} issues closed, {Y} PRs merged 📋 {Z} items remaining: {brief list} Continuing... (say "Ralph, idle" to stop) -``` +```text **Do NOT ask for permission to continue.** Just report and keep going. The user must explicitly say "idle" or "stop" to break the loop. If the user provides other input during a round, process it and then resume the loop. @@ -1195,7 +1195,7 @@ Ralph's in-session loop processes work while it exists, then idles. For **persis npx @bradygaster/squad-cli watch # polls every 10 minutes (default) npx @bradygaster/squad-cli watch --interval 5 # polls every 5 minutes npx @bradygaster/squad-cli watch --interval 30 # polls every 30 minutes -``` +```text This runs as a standalone local process (not inside Copilot) that: - Checks GitHub every N minutes for untriaged squad work @@ -1223,7 +1223,7 @@ Ralph's state is session-scoped (not persisted to disk): When Ralph reports status, use this format: -``` +```text 🔄 Ralph — Work Monitor ━━━━━━━━━━━━━━━━━━━━━━ 📊 Board Status: @@ -1233,7 +1233,7 @@ When Ralph reports status, use this format: ✅ Done: 5 issues closed this session Next action: Triaging #42 — "Fix auth endpoint timeout" -``` +```text ### Integration with Follow-Up Work diff --git a/.squad/agents/boromir/history.md b/.squad/agents/boromir/history.md index eed6ccf..a159445 100644 --- a/.squad/agents/boromir/history.md +++ b/.squad/agents/boromir/history.md @@ -1783,3 +1783,45 @@ container crashed (exit 139/SIGSEGV). Each collection's fixture failure cascaded - When Aspire integration test collections fail at fixture init (not test body), ALL tests in the collection report as failed — making "3 failures" actually mean "3 fixture startup failures affecting N tests total" - `Assert.Skip()` (xUnit v3) skips appear in CI as Skipped not Failed — a 1-skip result on the theme toggle test is expected/normal behavior - The Mongo volume state matters across sessions: `mongo-data` (FCV-contaminated with mongo:8.2 UUID idents) must never be used with `mongo:7`; only `mongo-data-v7` is safe + +--- + +## Issue #420 — Dependabot npm_and_yarn bump (js-yaml, markdown-it) + +**Date:** 2026-07-01 +**PR created:** #423 (Node 22 workflow fix) + +### Work Done + +Reviewed Dependabot PR #420 bumping root-level npm devDependencies: + +| Package | Before | After | +|---|---|---| +| markdownlint-cli2 | 0.22.1 | 0.23.0 | +| js-yaml (transitive) | 4.1.1 | 5.2.0 | +| markdown-it (transitive) | 14.1.1 | 14.2.0 | +| markdownlint (transitive) | 0.40.0 | 0.41.0 | + +- js-yaml 5.x is a **major version** but only used internally by markdownlint-cli2; no direct usage in our code. +- All affected packages are **devDependencies** — zero production impact. +- `markdownlint` CI check passes ✅ on the PR. +- Build failures on the PR are pre-existing `MessagePack 2.5.192` vulnerability (NU1902/NU1903) — unrelated. + +### Node 22 Fix (PR #423) + +`markdownlint-cli2 0.23.0` and `markdownlint 0.41.0` raised minimum Node from 20 → 22. +Updated `.github/workflows/squad-standard-lint-markdown.yml` `node-version: "20"` → `"22"`. +Note: `lint-markdown.yml` uses `markdownlint-cli2-action@v23` (GitHub composite action) — no `setup-node` needed, unaffected. + +Approved PR #420 and enabled auto-merge. Created PR #423 as companion Node 22 workflow fix. + +### Key File Paths + +- `.github/workflows/squad-standard-lint-markdown.yml` — uses `setup-node@v5` (node-version must be kept current) +- `.github/workflows/lint-markdown.yml` — uses `markdownlint-cli2-action@v23` (node-agnostic) + +## Learnings + +**Pattern:** When reviewing Dependabot npm PRs, check both the direct dep version AND the transitive deps' engine requirements. A minor bump of `markdownlint-cli2` carried a Node >=22 requirement via its transitive `markdownlint` dep. + +**Pattern:** `squad-dependabot-auto-merge.yml` only fires for PRs targeting `dev`. Dependabot PRs that target `main` must be manually approved/merged or have auto-merge enabled by hand. From fde1a8dac0ccc328dd239596627d12ede79b38f1 Mon Sep 17 00:00:00 2001 From: mpaulosky Date: Wed, 1 Jul 2026 12:20:26 -0700 Subject: [PATCH 5/5] fix(ci): remove trailing blank lines from workflow files - squad-standard-lint-yaml.yml: Remove trailing blank line - squad-dependabot-auto-merge.yml: Remove trailing blank line - squad-main-from-dev-guard.yml: Remove trailing blank line - squad-standard-lint-markdown.yml: Remove trailing blank line Fixes empty-lines yamllint errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/squad-dependabot-auto-merge.yml | 1 - .github/workflows/squad-main-from-dev-guard.yml | 1 - .github/workflows/squad-standard-lint-markdown.yml | 1 - .github/workflows/squad-standard-lint-yaml.yml | 1 - 4 files changed, 4 deletions(-) diff --git a/.github/workflows/squad-dependabot-auto-merge.yml b/.github/workflows/squad-dependabot-auto-merge.yml index 6fa9a35..a67d235 100644 --- a/.github/workflows/squad-dependabot-auto-merge.yml +++ b/.github/workflows/squad-dependabot-auto-merge.yml @@ -42,4 +42,3 @@ jobs: } throw error; } - diff --git a/.github/workflows/squad-main-from-dev-guard.yml b/.github/workflows/squad-main-from-dev-guard.yml index c73a3e4..798af8c 100644 --- a/.github/workflows/squad-main-from-dev-guard.yml +++ b/.github/workflows/squad-main-from-dev-guard.yml @@ -20,4 +20,3 @@ jobs: if (source !== "dev") { core.setFailed(`PRs to main must come from dev. Current source: ${source}`); } - diff --git a/.github/workflows/squad-standard-lint-markdown.yml b/.github/workflows/squad-standard-lint-markdown.yml index 02f4ebd..bd14e5c 100644 --- a/.github/workflows/squad-standard-lint-markdown.yml +++ b/.github/workflows/squad-standard-lint-markdown.yml @@ -21,4 +21,3 @@ jobs: exit 0 fi npx --yes markdownlint-cli $files - diff --git a/.github/workflows/squad-standard-lint-yaml.yml b/.github/workflows/squad-standard-lint-yaml.yml index 6961ae6..b8049c0 100644 --- a/.github/workflows/squad-standard-lint-yaml.yml +++ b/.github/workflows/squad-standard-lint-yaml.yml @@ -22,4 +22,3 @@ jobs: exit 0 fi yamllint -s $files -