Backlog intake
Skill darylmcd/Roslyn-Backed-MCP/.claude/skills/backlog-intake
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.From its SKILL.md
npx -y skills add darylmcd/Roslyn-Backed-MCP --skill backlog-intakeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
26.2 KB, as published. Nobody here has run it
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)
gitCLI on PATH. If not, refuse.pwshCLI on PATH (used by the staging script). If not, refuse.- 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." mainup to date withorigin/main(git fetch origin mainfirst). Refuse ifmaindiverged in a way that requires manual resolution.ai_docs/backlog.mdexists with the## P2 / P3 / P4 — open work+## Refsstructure. 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 artifacts from this repo and configured siblings. The canonical list of recognized shapes lives in that script's .DESCRIPTION → Recognized shapes block — read it there; do not re-list the globs here. Those shapes fall into TWO handling categories:
- Prose audit / retro / promotion reports — moved into
review-inbox/for the existing extraction flow. backlog.d/<finding-id>.mdfragments — discovered at<sibling-repo-root>/backlog.d/*.mdand at this repo'sbacklog.d/. Disambiguated from prose reports by theseverity:frontmatter key; canonical schema atai_docs/items/backlog-d-fragment-schema.md.
pwsh -File eng/stage-review-inbox.ps1 -DryRun
Review the dry-run output. If the file list looks right:
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:
- Walk
<repo>/backlog.d/*.md. For each file, parse YAML frontmatter; requireid,source_audit,source_repo,severity,area,server_version,anchors(perai_docs/items/backlog-d-fragment-schema.md). If any required key is missing ORseverity/areais outside the documented enum ORanchorsis empty OR the filename does not matchid, skip that fragment with a warning — leave it on disk for the audit operator to fix. Do NOT delete malformed fragments. - Dedupe each remaining fragment against existing
ai_docs/backlog.mdrows in this order:- Exact
(source_repo, id)match — if a backlog row already cites the samesource_repoand 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
anchorsagainst 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'sdocell — append the newsource_repoand 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).
- Exact
- Append any non-duplicate, non-shipped fragments as new rows in
ai_docs/backlog.md, classified by theirseverityfield into the matching priority band. Use the body paragraph (finding + repro + proposed fix sketch) as the seed for the row'sdocell; rewrite into the standard cell shape during Phase 4. - Delete each consumed source fragment with
git rm(when the sibling repo is git-tracked) orRemove-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 0.6 — Consume aggregated promotion scorecard proposals
If audit-reports/_aggregated-promotion-scorecard.json exists, run:
pwsh -NoProfile -File eng/propose-promotion-scorecard-backlog-rows.ps1
This script is read-only: it emits stable backlog-row proposals for aggregated
promotion-scorecard entries whose verdict is promote: blocked or
needs-more-evidence, and it lists promote: ready entries as skipped so they
continue through /promote-tier.
For each proposal:
- If
action == "add-new", append the row to the matching priority band using the emittedid,pri,deps, anddofields. - If
action == "update-existing", update the existing row'sdocell only when the proposal contains new blockers, source repos, or rationale that the row does not already mention. - Preserve idempotency: rerunning the script after a row is represented must
produce
update-existing, not a duplicate row. - If you intentionally skip a proposal, add a maintainer-readable note to the final summary explaining why it was skipped. Do not silently drop blocked or needs-more-evidence scorecard entries.
The generated do cell cites
audit-reports/_aggregated-promotion-scorecard.json; keep that anchor unless
you move the scorecard into a different durable location as part of the same
batch.
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:
CHANGELOG.md[Unreleased] + last 3 versions — grep for the tool name, service, or symbol the row cites.- Newest backlog-sweep plan under
ai_docs/plans/*_backlog-sweep/— read itsstate.jsonandplan.md. Does any shipped initiative describe the same fix? - Git log, last 100 commits —
git log --oneline -n 100 | grep -i <keyword>on tool / service / symbol names. - 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, grepSymbolSearchService.csfor 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:
- For service-class names (e.g.
DeadCodeService,CodeActionService): confirm the.csfile exists at the cited path. If the row sayssrc/RoslynMcp.Roslyn/Services/FooService.csbut onlysrc/RoslynMcp.Roslyn/Services/BarService.csexists, rewrite the row to cite the real file. Common mistakes the extraction subagent makes:CodeActionsService(plural) → actual isCodeActionService.csMoveTypeService→ actual isTypeMoveService.csSemanticSearchService→ logic lives inCodePatternAnalyzer.cs- Hallucinated
ServerInfoService/PackageReferenceService/RevertService— tools-layer-only, seesrc/RoslynMcp.Host.Stdio/Tools/or the unified service (e.g.UndoService.csfor revert).
- For tool registrations (e.g.
find_references_bulk): grepsrc/RoslynMcp.Host.Stdio/Tools/forName = "<tool_name>"and cite the real file + line. The PS1 pipeline NEVER did this; it's a distinctive value-add of this skill. - For file:line references (e.g.
CompileCheckService.cs:155): runget_source_texton 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). Note: the backlog-sweep planner/executor prompts are NOT repo Refs entries — they live globally in ~/.claude/prompts/ (backlog-sweep-plan.md, backlog-sweep-execute.md) and are loaded by the /backlog-sweep:* commands, so do NOT add repo-local rows for them:
| Path | Role (template text) |
|---|---|
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
- Update
updated_at:to current UTC. Record this timestamp as the batch id in the formYYYYMMDDTHHMMSSZ(e.g.20260424T031936Z). - Re-sort each priority band alphabetically by id.
- 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). - Archive the processed review files. Only the files that produced rows this batch — do NOT touch any existing
review-inbox/archive/<prior-batch>/subdirectories.
The flatmkdir -p review-inbox/archive/{BATCH_ID} for f in review-inbox/*.md; do git mv "$f" "review-inbox/archive/{BATCH_ID}/$(basename "$f")"; donereview-inbox/directory is now empty (save for thearchive/subdirectory) and ready for the next batch to stage into. - 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. UseEditwith exact-string replace on each affected row. - Verify the Refs table in
ai_docs/backlog.mddescribes both the flatreview-inbox/(staging) andreview-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. - Skip the commit if
--no-commit; otherwise:
Commit message template: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 "..."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) <[email protected]> - 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 (andgit log --followpreserves 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.
- Preconditions.
ghis onPATHANDgh auth statusreports 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. - Dot-source the renderer.
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.pwsh -NoProfile -Command ". '${CLAUDE_PLUGIN_ROOT}/skills/mcp-server-surface-test/lib/render-finding.ps1'; <render-and-file logic>" - 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). Thesource_auditfield 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 callgh. Print the rendered Issue body to stdout with the SECURITY/P0 escalation banner already prepended byRender-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.mdline, so the row id is the canonical fingerprint:
Apply these rules in order: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- Open hit: skip filing. Capture the existing Issue URL and use it (instead of a freshly-filed one) when annotating the backlog row's
docell. 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.
- Open hit: skip filing. Capture the existing Issue URL and use it (instead of a freshly-filed one) when annotating the backlog row's
- Otherwise call
Render-FindingIssue -Finding $fto get{title, labels, body, refusedPublic}. Writebodyto a tempfile, then: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.mdso the planner can cite the public Issue (Closes #N) when the row is sized into an initiative. UseEditwith exact-string replace; do NOT regenerate the whole file.
- Construct a finding hashtable from the row's fields (
- 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.
- Report counts in Phase 7. Add a
Publishedline: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.mdshape 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. Useclose-backlog-rowsafter a ship, this one after an audit wave.reconcile-backlog-sweep-plan: updates a backlog-sweep plan'sstate.jsonwhen 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. Runbacklog-intakefirst, 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.