backlog-intake — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited backlog-intake (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
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.
$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.
review-inbox/ + ai_docs/backlog.md. If other paths are dirty, refuse: "Working tree has unrelated changes — commit or stash before intake."git fetch origin main first). Refuse if main diverged in a way that requires manual resolution.## 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."Skip this phase if --skip-stage is set.
eng/stage-review-inbox.ps1 discovers TWO artifact shapes from this repo and configured siblings:
*_mcp-server-audit.md, *_experimental-promotion.md, *_roslyn-mcp-retro.md) — moved into review-inbox/ for the existing extraction flow.<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.pwsh -File eng/stage-review-inbox.ps1 -DryRunReview the dry-run output. If the file list looks right:
pwsh -File eng/stage-review-inbox.ps1If --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.
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:
<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.ai_docs/backlog.md rows in this order: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).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.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.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.
If audit-reports/_aggregated-promotion-scorecard.json exists, run:
pwsh -NoProfile -File eng/propose-promotion-scorecard-backlog-rows.ps1This 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:
action == "add-new", append the row to the matching priority band usingthe emitted id, pri, deps, and do fields.
action == "update-existing", update the existing row's do cell onlywhen the proposal contains new blockers, source repos, or rationale that the row does not already mention.
produce update-existing, not a duplicate row.
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.
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.
Skip this phase if --skip-verify is set.
For each candidate row, cross-check:
ai_docs/plans/*_backlog-sweep/ — read its state.json and plan.md. Does any shipped initiative describe the same fix?git log --oneline -n 100 | grep -i <keyword> on tool / service / symbol names.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.
For each remaining row, verify every service class, tool file, and file:line anchor resolves:
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.csMoveTypeService → actual is TypeMoveService.csSemanticSearchService → logic lives in CodePatternAnalyzer.csServerInfoService / PackageReferenceService / RevertService — tools-layer-only, see src/RoslynMcp.Host.Stdio/Tools/ or the unified service (e.g. UndoService.cs for revert).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.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.
Apply backlog-sweep-plan.md Rule 1 to each row. A row is heroic (and must be split) if any of:
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).
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. |
updated_at: to current UTC. Record this timestamp as the batch id in the form YYYYMMDDTHHMMSSZ (e.g. 20260424T031936Z)."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).review-inbox/archive/<prior-batch>/ subdirectories. mkdir -p review-inbox/archive/{BATCH_ID}
for f in review-inbox/*.md; do git mv "$f" "review-inbox/archive/{BATCH_ID}/$(basename "$f")"; doneThe flat review-inbox/ directory is now empty (save for the archive/ subdirectory) and ready for the next batch to stage into.
review-inbox/archive/{BATCH_ID}/<file>. This keeps anchors resolvable after the move. Use Edit with exact-string replace on each affected row.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.--no-commit; otherwise: 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) <[email protected]>review-inbox/… paths. A move + per-batch subdirectory keeps those paths resolvable (and git log --follow preserves history).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.archive/ by default (no recursion). Skipping thousands of already-processed lines per batch.--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.
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. 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.
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).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.Closes <row-id> in ai_docs/backlog.md line, so the row id is the canonical fingerprint: 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 5Apply these rules in order:
do cell. Emit → skipped: already tracked as #<N> (open) in the audit trail.→ 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.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.Render-FindingIssue -Finding $f to get {title, labels, body, refusedPublic}. Write body to a tempfile, then: gh issue create --repo darylmcd/Roslyn-Backed-MCP \
--title "<title>" \
--label "area:<area>" --label "severity:<severity>" \
--body-file <tempfile>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.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.
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)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.
backlog-intake) ADDS rows from raw audit evidence. Use close-backlog-rows after a ship, this one after an audit wave.state.json when its PRs have merged. Different file, different concern.backlog-intake first, then the sweep planner.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.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.