diff --git a/.copilot/skills/auth0-token-forwarding/SKILL.md b/.copilot/skills/auth0-token-forwarding/SKILL.md index 00d9bb81..03b0165b 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 60b8f529..66a888ba 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 dd07609a..f2779989 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 4d59ea3a..a79690f8 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 2d5a27a1..579b693b 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 223d9087..a0afdeec 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 8b4c6e2b..3492ecd0 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 3058d32b..830ed2ea 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/.github/dependabot.yml b/.github/dependabot.yml index 17a35b1c..3c85c7da 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 45860039..f6f505d0 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 97a82765..08e6bb75 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] diff --git a/.github/workflows/squad-dependabot-auto-merge.yml b/.github/workflows/squad-dependabot-auto-merge.yml index 6fa9a354..a67d2354 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 c73a3e48..798af8c6 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 30c494b7..bd14e5cf 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') @@ -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 6961ae63..b8049c03 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 - diff --git a/.squad/agents/boromir/history.md b/.squad/agents/boromir/history.md index eed6ccfc..a1594453 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. diff --git a/tests/AppHost.Tests/MongoClearDataIntegrationTests.cs b/tests/AppHost.Tests/MongoClearDataIntegrationTests.cs index 5038f254..baca15ab 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 87987572..c6aa4b78 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 483a93a5..c3b29c22 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 = [], }; }