Free SKILL.md scraped from GitHub. Clone the repo or copy the file directly into your Claude Code skills directory.
npx versuz@latest install darylmcd-roslyn-backed-mcp-claude-skills-backlog-intakegit clone https://github.com/darylmcd/Roslyn-Backed-MCP.gitcp Roslyn-Backed-MCP/SKILL.MD ~/.claude/skills/darylmcd-roslyn-backed-mcp-claude-skills-backlog-intake/SKILL.md---
name: backlog-intake
description: "Consolidate deep-review artifacts (mcp-server-audit, experimental-promotion, roslyn-mcp-retro) into ai_docs/backlog.md with anchor verification, dedupe, priority classification, and Rule 1/3/4/5 sizing for the backlog-sweep-plan.md planner prompt. Use when: a deep-review / audit wave has produced `*_mcp-server-audit.md` / `*_roslyn-mcp-retro.md` / `*_experimental-promotion.md` files across sibling repos (or this one) and you want them reviewed, deduped, and merged into the backlog as properly-sized initiative rows."
user-invocable: true
argument-hint: "[--stage | --skip-verify | --no-commit | --publish] — defaults: stage files first, verify against CHANGELOG, commit on a fresh branch; pass --publish to file each accepted row as a public GitHub Issue via gh issue create"
---
# Backlog intake
You are triaging a batch of deep-review artifacts into `ai_docs/backlog.md`. Your job is to turn raw audit / retro / experimental-promotion markdown files into **properly-sized, anchor-verified, de-duplicated, priority-ranked** rows that the `backlog-sweep-plan.md` planner prompt can plan against without re-processing.
This skill replaces the `eng/new-deep-review-batch.ps1 → sync-deep-review-backlog.ps1` pipeline. The PowerShell pipeline handled only `*_mcp-server-audit.md`, did literal-text dedupe, and couldn't verify anchors or size rows to Rule 1/3/4. The judgment work belongs in an LLM; the mechanical file-staging is delegated to `eng/stage-review-inbox.ps1`.
## Server discovery
This skill edits repository files, shells out to `pwsh` / `gh` / `git`, and uses Roslyn MCP **read-only** tools (`symbol_search`, `find_references`, `get_source_text`) to verify anchors. No `*_apply` / `*_preview` writers. If you find yourself calling a writer, you are in the wrong skill — stop and hand back.
## Input
`$ARGUMENTS` is optional. Supported flags:
| Flag | Effect |
|---|---|
| `--stage` | Explicit staging pass: always run `eng/stage-review-inbox.ps1` before triage, even if `review-inbox/` already has files. |
| `--skip-stage` | Skip staging entirely — triage whatever is already in `review-inbox/`. |
| `--skip-verify` | Skip the "already shipped?" cross-check against CHANGELOG + recent plans. Faster but riskier. |
| `--no-commit` | Write the updated `ai_docs/backlog.md` but do not create a branch or commit. |
| `--sibling-parent <path>` | Override the sibling-repo scan root (forwarded to the PS1). |
| `--publish` | After the batch is finalized, file each accepted row as a public GitHub Issue at `darylmcd/Roslyn-Backed-MCP` via `gh issue create`. Uses the shared renderer `skills/mcp-server-surface-test/lib/render-finding.ps1` so the public Issue body is byte-identical to what `/mcp-server-surface-test --auto-file` emits. Refuses to file rows whose `severity == P0` or `area == security` — those print to stdout with the security-advisory escalation banner instead. Requires `gh` on `PATH` and `gh auth status` reporting authenticated; falls back to stdout-print with a warning otherwise. |
Default (no flags): stage if `review-inbox/` is empty, verify against CHANGELOG, commit to a fresh branch off `main`.
## Preconditions (HARD GATES — refuse if any fail)
1. **`git` CLI on PATH.** If not, refuse.
2. **`pwsh` CLI on PATH** (used by the staging script). If not, refuse.
3. **Working tree clean** OR the only dirty paths are `review-inbox/` + `ai_docs/backlog.md`. If other paths are dirty, refuse: `"Working tree has unrelated changes — commit or stash before intake."`
4. **`main` up to date with `origin/main`** (`git fetch origin main` first). Refuse if `main` diverged in a way that requires manual resolution.
5. **`ai_docs/backlog.md` exists** with the `## P2 / P3 / P4 — open work` + `## Refs` structure. If the file is missing or shape-wrong, refuse: `"backlog.md shape is unexpected — hand edit before re-running this skill."`
## Workflow
### Phase 0 — Stage artifacts
Skip this phase if `--skip-stage` is set.
`eng/stage-review-inbox.ps1` discovers TWO artifact shapes from this repo and configured siblings:
1. **Prose audit / retro / promotion reports** (`*_mcp-server-audit.md`, `*_experimental-promotion.md`, `*_roslyn-mcp-retro.md`) — moved into `review-inbox/` for the existing extraction flow.
2. **`backlog.d/<finding-id>.md` fragments** — discovered at `<sibling-repo-root>/backlog.d/*.md` and at this repo's `backlog.d/`. Disambiguated from prose reports by the `severity:` frontmatter key; canonical schema at `ai_docs/items/backlog-d-fragment-schema.md`.
```bash
pwsh -File eng/stage-review-inbox.ps1 -DryRun
```
Review the dry-run output. If the file list looks right:
```bash
pwsh -File eng/stage-review-inbox.ps1
```
If `--stage` was passed OR `review-inbox/` is empty at the start of the run, also run the real command. If the PS1 finishes with "No new artifacts found" AND `review-inbox/` is empty AND **no `backlog.d/` fragments exist anywhere**, refuse: `"Nothing to triage. Produce audits first via /mcp-server-stress (bundled prompt at .claude/skills/mcp-server-stress/prompts/maintainer-overlay.md), then re-run."`
Record the count: `N reports + F fragments staged from M repos` for the final summary.
### Phase 0.5 — Consume fragments (`backlog.d/` pattern)
Fragments take a different path from prose reports. They are **not** moved into `review-inbox/`; instead intake reads them in place from each source repo's `backlog.d/`, dedupes, appends new rows, and **deletes the consumed fragments at the source**. The deletion IS the consumption record — there is no manifest file.
For each configured sibling repo (and this repo) with a `backlog.d/` directory:
1. **Walk** `<repo>/backlog.d/*.md`. For each file, parse YAML frontmatter; require `id`, `source_audit`, `source_repo`, `severity`, `area`, `server_version`, `anchors` (per `ai_docs/items/backlog-d-fragment-schema.md`). If any required key is missing OR `severity` / `area` is outside the documented enum OR `anchors` is empty OR the filename does not match `id`, **skip** that fragment with a warning — leave it on disk for the audit operator to fix. Do NOT delete malformed fragments.
2. **Dedupe** each remaining fragment against existing `ai_docs/backlog.md` rows in this order:
- **Exact `(source_repo, id)` match** — if a backlog row already cites the same `source_repo` and the same row id, the fragment is a duplicate. Drop it from the candidate list AND delete the source fragment (the row already represents this finding).
- **Anchor-set similarity** — compare each fragment's `anchors` against existing rows. When ≥50 % of anchor paths overlap on the same shared code (typical for cross-repo audits hitting the same Roslyn-MCP server bug from two reporters), fold the fragment into the existing row's `do` cell — append the new `source_repo` and any unique anchors. Delete the source fragment after folding.
- **Already-shipped check** — same Phase 2 logic that runs on prose-report candidates (CHANGELOG / recent plans / git log).
3. **Append** any non-duplicate, non-shipped fragments as new rows in `ai_docs/backlog.md`, classified by their `severity` field into the matching priority band. Use the body paragraph (finding + repro + proposed fix sketch) as the seed for the row's `do` cell; rewrite into the standard cell shape during Phase 4.
4. **Delete** each consumed source fragment with `git rm` (when the sibling repo is git-tracked) or `Remove-Item` (when it is not). Track deletions for the final commit message.
**Idempotency contract:** re-running intake on a clean `backlog.d/` (no fragments, or only fragments already represented in `ai_docs/backlog.md`) is a no-op. The deletion is what makes this true — once consumed, the fragment is gone, so the next pass sees nothing to consume.
**Cross-repo mutation note:** intake **mutates audited sibling repos** by deleting fragments under their `backlog.d/`. This is intentional — it is how the consume-and-track pattern works without a manifest file. The audited repos' authors are expected to be aware that intake has this side effect; if a sibling repo wants to retain a record of what was reported, the prose `*_mcp-server-audit.md` under its `ai_docs/audit-reports/` is preserved and acts as the historical record.
The existing prose-report ingestion flow (Phases 1–6) continues unchanged for `review-inbox/*.md` artifacts. Fragments and prose reports coexist; they are not mutually exclusive.
### Phase 1 — Extract actionable items (subagent)
Invoke the dedicated `backlog-intake-extractor` subagent:
```
Agent(subagent_type: "backlog-intake-extractor", prompt: <<<
reviewInboxPath: <absolute path to review-inbox/>
existingBacklogPath: <absolute path to ai_docs/backlog.md>
>>>)
```
The agent encapsulates the extraction / filter / dedupe / classify / cross-check flow with a strict Read/Glob/Grep tool policy (no writers) and returns a `<<<RESULT>>>` envelope containing the deduped row list + notes. See `.claude/agents/backlog-intake-extractor.md` for the agent's own contract.
Parse the envelope. Capture `dedupedRows` / `dedupeRatio` / `rawCandidates` for the Phase 7 summary. Capture the row list for Phase 2.
**Fallback** (environments without `.claude/agents/` support): spawn a `general-purpose` subagent and inline the agent's body as the prompt. The agent file is the canonical source of that prompt.
### Phase 2 — Verify-not-already-shipped
Skip this phase if `--skip-verify` is set.
For each candidate row, cross-check:
1. **`CHANGELOG.md` [Unreleased] + last 3 versions** — grep for the tool name, service, or symbol the row cites.
2. **Newest backlog-sweep plan** under `ai_docs/plans/*_backlog-sweep/` — read its `state.json` and `plan.md`. Does any shipped initiative describe the same fix?
3. **Git log, last 100 commits** — `git log --oneline -n 100 | grep -i <keyword>` on tool / service / symbol names.
4. **Code spot-check for rows flagged by 1-3** — open the named service / tool file and confirm the specific behavior still reproduces. For example, if the row says `symbol_search("")` overflows, grep `SymbolSearchService.cs` for an empty-query guard.
Don't spot-check every row's code — only the ones where prior evidence suggests the fix may have landed. Budget: ~1 minute per candidate flagged, ~0 for rows with no CHANGELOG / plan / log hit.
If this phase finds a row already shipped, **drop it** from the candidate list and record "dropped (already shipped per <evidence>)" in the final summary.
### Phase 3 — Fix anchors
For each remaining row, verify every service class, tool file, and file:line anchor resolves:
1. For service-class names (e.g. `DeadCodeService`, `CodeActionService`): confirm the `.cs` file exists at the cited path. If the row says `src/RoslynMcp.Roslyn/Services/FooService.cs` but only `src/RoslynMcp.Roslyn/Services/BarService.cs` exists, rewrite the row to cite the real file. **Common mistakes the extraction subagent makes**:
- `CodeActionsService` (plural) → actual is `CodeActionService.cs`
- `MoveTypeService` → actual is `TypeMoveService.cs`
- `SemanticSearchService` → logic lives in `CodePatternAnalyzer.cs`
- Hallucinated `ServerInfoService` / `PackageReferenceService` / `RevertService` — tools-layer-only, see `src/RoslynMcp.Host.Stdio/Tools/` or the unified service (e.g. `UndoService.cs` for revert).
2. For tool registrations (e.g. `find_references_bulk`): grep `src/RoslynMcp.Host.Stdio/Tools/` for `Name = "<tool_name>"` and cite the real file + line. The PS1 pipeline NEVER did this; it's a distinctive value-add of this skill.
3. For file:line references (e.g. `CompileCheckService.cs:155`): run `get_source_text` on that span to confirm the referenced code is still there.
Rewrite each row's `do` text to cite **both** the core service (under `src/RoslynMcp.Roslyn/Services/`) **and** the tool registration (under `src/RoslynMcp.Host.Stdio/Tools/`). Executors can then land on the right file immediately.
If an anchor genuinely cannot be resolved, tag the row with `[stale — cited anchor not found; executor may use synthetic examples]` per `backlog-sweep-plan.md` Step 3's anchor-verification guidance, rather than dropping the row.
### Phase 4 — Split heroic rows
Apply `backlog-sweep-plan.md` Rule 1 to each row. A row is **heroic** (and must be split) if any of:
- It describes two or more distinct bugs that live in **different code paths** (different functions, different files).
- It asks for ≥4 production-file edits to fulfill.
- Its regression tests are not trivially-additive variants of one shape.
- Its "do" field contains a numbered "(1) fix A, (2) fix B, (3) fix C" list where each item is a different code change.
Common heroic shapes to watch for:
| Shape | Split strategy |
|---|---|
| "Fix tool X has 3 issues: schema drift + perf + empty data field" | Three rows, one per concern. Often different priorities (schema=P3, perf=P3, empty=P4). |
| "Scaffolder emits invalid C# because (a) identifier bug, (b) static target, (c) ctor args" | Three rows — each touches a different code path in the service. |
| "Tool A and Tool B both need summary-mode paging" | Two rows — different tool files, different handlers, even if the shape is similar. |
| "Guard X in WorkspaceManager AND in every tool wrapper" | Tighten to the single choke point; usually WorkspaceManager alone covers all callers. |
For each split, give each child a distinct kebab-case id (prefix with the original id where readable — e.g. `scaffold-test-preview-dotted-identifier` / `-static-target-body` / `-ctor-arg-stubs`).
### Phase 5 — Ensure planner-prompt refs
Before writing, verify the Refs table in `ai_docs/backlog.md` includes these entries (add any missing):
| Path | Role (template text) |
|---|---|
| `ai_docs/prompts/backlog-sweep-plan.md` | Planner prompt; enforces per-initiative Rule 1 (bundle only on shared code path) / Rule 3 (≤4 prod files) / Rule 3b (toolPolicy) / Rule 4 (≤3 test files) / Rule 5 (≤80K context). |
| `ai_docs/prompts/backlog-sweep-execute.md` | Executor companion; consumes the planner's `state.json` and vets each initiative against Rules 3/4/5. |
| `ai_docs/bootstrap-read-tool-primer.md` | Self-edit read-only tool primer (Roslyn-MCP read-side tools preferred over Bash/Grep). |
| `ai_docs/runtime.md` | Bootstrap scope policy — main-checkout self-edit (no `*_apply`) vs worktree/parallel-subagent sessions. |
| `review-inbox/` | Source evidence for the open rows. Keep until each row is closed or superseded. |
### Phase 6 — Archive processed files, write backlog.md, commit
1. Update `updated_at:` to current UTC. Record this timestamp as the batch id in the form `YYYYMMDDTHHMMSSZ` (e.g. `20260424T031936Z`).
2. Re-sort each priority band alphabetically by id.
3. Ensure the "Standing rules" section includes the initiative-sizing rule: `"Size every row to a single backlog-sweep-plan.md initiative: one code path, ≤4 production files, ≤3 test files, one regression-test shape."` (add if missing).
4. **Archive the processed review files.** Only the files that produced rows this batch — do NOT touch any existing `review-inbox/archive/<prior-batch>/` subdirectories.
```bash
mkdir -p review-inbox/archive/{BATCH_ID}
for f in review-inbox/*.md; do git mv "$f" "review-inbox/archive/{BATCH_ID}/$(basename "$f")"; done
```
The flat `review-inbox/` directory is now empty (save for the `archive/` subdirectory) and ready for the next batch to stage into.
5. **Rewrite any `review-inbox/<file>` citations in the new backlog rows** to point at the archive path: `review-inbox/archive/{BATCH_ID}/<file>`. This keeps anchors resolvable after the move. Use `Edit` with exact-string replace on each affected row.
6. **Verify the Refs table** in `ai_docs/backlog.md` describes both the flat `review-inbox/` (staging) and `review-inbox/archive/<batch-ts>/` (processed evidence) roles. The contract: row citations point into the archive; the flat directory is always the next batch's inbox.
7. Skip the commit if `--no-commit`; otherwise:
```bash
git switch main
git pull --ff-only
git switch -c chore/backlog-audit-intake-{YYYYMMDD}
git add ai_docs/backlog.md review-inbox/
git commit -m "..."
```
Commit message template:
```
chore(backlog): intake cross-repo audit + retro batch ({YYYY-MM-DD})
Consolidates actionable items from {N} review files across {M} repos into
ai_docs/backlog.md. Archived processed files under
review-inbox/archive/{BATCH_ID}/ so the flat review-inbox/ stays empty for
the next batch.
Backlog now: {P2-count} P2 + {P3-count} P3 + {P4-count} P4 = {total} open
rows (up from {prior}). Rows anchored to both core services (src/RoslynMcp.Roslyn/Services/)
and tool registrations (src/RoslynMcp.Host.Stdio/Tools/) so first executor lands
on the right file without a scavenger hunt.
- Splits applied: {list of heroic rows that were split}
- Dropped (already shipped): {list, if any}
- Verified against CHANGELOG [Unreleased] + last 3 versions + newest backlog-sweep plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
```
8. Do NOT push unless the user asked. Leave the branch local.
### Why archive instead of a manifest or renamed-in-place
- Anchor integrity: backlog rows cite `review-inbox/…` paths. A move + per-batch subdirectory keeps those paths resolvable (and `git log --follow` preserves history).
- Next-batch cleanliness: Phase 0's staging-then-triage flow only reads the flat `review-inbox/`. By the time the next batch stages, the flat dir is empty and the extractor subagent reads only the NEW files — no need for the extractor to filter against a manifest or frontmatter stamp.
- Context savings: the extractor subagent's directory scan ignores `archive/` by default (no recursion). Skipping thousands of already-processed lines per batch.
### Phase 6.5 — Publish accepted rows to GitHub Issues (when `--publish` is set)
Skip this phase if `--publish` is not set.
For every row appended to `ai_docs/backlog.md` in this batch (Phase 4's deduped + split set, after Phase 6 wrote the file), file a public GitHub Issue against `darylmcd/Roslyn-Backed-MCP` using the shared renderer. This validates the schema + issue template by exercising it on every accepted finding — the user's load-bearing reason for hooking publish into intake rather than a separate skill.
1. **Preconditions.** `gh` is on `PATH` AND `gh auth status` reports authenticated. If either fails, emit one warning line per row (`gh unavailable — printed body to stdout instead`) and fall back to print-only for the whole phase. Do not raise.
2. **Dot-source the renderer.**
```bash
pwsh -NoProfile -Command ". '${CLAUDE_PLUGIN_ROOT}/skills/mcp-server-surface-test/lib/render-finding.ps1'; <render-and-file logic>"
```
The renderer is the source of truth. Do NOT hand-roll a body — drift between the consumer auto-file path and the maintainer publish path is the failure mode this Row 2 design exists to prevent.
3. **Per-row loop.** For each accepted row:
- Construct a finding hashtable from the row's fields (`id`, `source_repo`, `severity`, `area`, `server_version`, `anchors`, `finding`, `repro`, `proposed_fix`). The `source_audit` field is the basename of the prose audit report this row was extracted from (or the empty string when the row was created from a sweep retro instead of an audit).
- Call `Test-FindingShouldRefusePublicFile -Finding $f`. If `$true`, **do not call `gh`**. Print the rendered Issue body to stdout with the SECURITY/P0 escalation banner already prepended by `Render-FindingIssue`. Add `(refused — pre-disclosure safeguard)` to the row's audit-trail entry in the final summary.
- **Dedup pre-check (load-bearing).** Before filing, query the issue tracker for a prior filing of the same row. The shared renderer's bodies always include a `Closes <row-id> in ai_docs/backlog.md` line, so the row id is the canonical fingerprint:
```bash
gh issue list --repo darylmcd/Roslyn-Backed-MCP \
--state all \
--search "Closes ${rowId} in ai_docs/backlog.md in:body" \
--json number,state,title --limit 5
```
Apply these rules in order:
- **Open hit**: skip filing. Capture the existing Issue URL and use it (instead of a freshly-filed one) when annotating the backlog row's `do` cell. Emit `→ skipped: already tracked as #<N> (open)` in the audit trail.
- **Closed hit**: skip filing. Emit `→ skipped: previously resolved as #<N> (closed)`. Do not refile or reuse the URL — closed issues with the same row id imply a prior fix that's now regressing, and the operator should re-open manually if appropriate.
- **No hit**: proceed to `Render-FindingIssue` + `gh issue create`. As a belt-and-braces secondary check, also do a title-keyword search (`<primary tool name> in:title`) and skip with a warning if a strong title near-match is found on an open issue (covers the rare case where a sibling skill auto-filed before backlog-intake ran). The row-id check is the primary gate; this is the fallback for cross-skill collisions.
- Otherwise call `Render-FindingIssue -Finding $f` to get `{title, labels, body, refusedPublic}`. Write `body` to a tempfile, then:
```bash
gh issue create --repo darylmcd/Roslyn-Backed-MCP \
--title "<title>" \
--label "area:<area>" --label "severity:<severity>" \
--body-file <tempfile>
```
- Capture the returned Issue URL (or the deduped existing URL from the pre-check) and append it to the row's body inside `ai_docs/backlog.md` so the planner can cite the public Issue (`Closes #N`) when the row is sized into an initiative. Use `Edit` with exact-string replace; do NOT regenerate the whole file.
4. **Recommit when rows changed.** If any row gained an Issue URL, the working tree now diverges from Phase 6's commit. Amend the Phase 6 commit OR add a follow-up commit on the same branch — your call which is cleaner. Do NOT push without explicit user confirmation.
5. **Report counts in Phase 7.** Add a `Published` line: `Published: {filed} GitHub Issues + {refused} P0/security refused (printed only)`.
The two auto-file destinations (this skill and `/mcp-server-surface-test --auto-file`) MUST emit byte-identical bodies. The shared renderer is the contract; the renderer's tests at `tests/RoslynMcp.Tests/Skills/RenderFindingScriptTests.cs` enforce determinism.
### Phase 7 — Report
Emit a final summary:
```
Backlog intake complete.
Staged: {N} files from {M} repos (review-inbox/)
Extracted: {raw} candidate items
Deduped: {deduped} rows ({ratio}:1 dedupe)
Dropped (already shipped): {shipped-count}
Anchor fixes applied: {anchor-fix-count}
Heroic splits: {split-count} rows → {split-total}
Final: {P2} P2 + {P3} P3 + {P4} P4 = {total} open rows
Commit: {SHA} on branch {branch-name} (local-only; push when ready)
```
## Refusal cases (explicit)
- **`review-inbox/` empty AND staging found nothing** → refuse per Phase 0.
- **Working tree dirty beyond backlog.md + review-inbox/** → refuse per Precondition 3.
- **`backlog.md` shape unexpected** → refuse per Precondition 5.
- **Subagent returns 0 rows** → do NOT write an empty commit. Report "no actionable items extracted from {N} files" and exit.
- **Subagent returns > 60 rows after dedupe** → stop and ask the user whether to proceed with a clearly-under-deduped result, rather than silently writing a heroic backlog.
## Why a subagent for Phase 1
Each review file is 200-800 lines; a 14-file batch is 6000+ lines. Reading them all in-context would consume 30-50K tokens before any judgment work starts. The extraction subagent returns a compact structured list (typically 1-2K tokens), leaving the main agent's context free for the anchor-verify / heroic-split / commit phases, which require reading this repo's source files.
If the batch is small (≤3 files, ≤1000 total lines), skip the subagent and read directly — the overhead isn't worth it.
## Distinct from related skills
- **`close-backlog-rows`**: removes specific row ids from the open table after their PRs ship. This skill (`backlog-intake`) ADDS rows from raw audit evidence. Use `close-backlog-rows` after a ship, this one after an audit wave.
- **`reconcile-backlog-sweep-plan`**: updates a backlog-sweep plan's `state.json` when its PRs have merged. Different file, different concern.
- **`draft-changelog-entry`**: drafts one changelog fragment per PR from commit metadata. Different artifact.
- **`backlog-sweep-plan.md` (prompt, not skill)**: consumes the backlog this skill produces. Run `backlog-intake` first, then the sweep planner.
## Historical note
This skill replaces five PowerShell scripts that were deleted when it shipped: `compare-external-audit-sources.ps1`, `import-deep-review-audit.ps1`, `new-deep-review-batch.ps1`, `new-deep-review-rollup.ps1`, `sync-deep-review-backlog.ps1`. The last (the "sync" script) was the lossiest — it did literal-text dedupe via PowerShell regex and couldn't verify anchors, dedupe semantically, cross-check against CHANGELOG, or size rows to Rule 1/3/4. The only piece worth keeping from the pipeline was file staging, which is now a 150-line `eng/stage-review-inbox.ps1`.