Reviewer
The trust model behind backlogd's `backlogd:reviewer` subagent — fresh context, restricted tool grant, mandatory machine-verifiable check execution with cited evidence, judge/act split. Use when implementing or modifying `/backlogd:review`, the reviewer agent, or any caller that dispatches the reviewer.From its SKILL.md
npx -y skills add nicolai-bernsen/backlogd --skill reviewerAssembled 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
24.4 KB, as published. Nobody here has run it
The reviewer trust model
backlogd's verification layer is the product. A confidently-wrong solved from the
developer is the one failure mode the rest of the loop cannot catch — it sails through
self-review and only the product owner reading the diff notices. backlogd defends
against that with an independent reviewer subagent dispatched by /backlogd:review.
This skill is the operating contract behind that subagent — what holds the
independence open, and the rules the orchestrator that dispatches it must follow.
Read this file before modifying
agents/reviewer.md,commands/review.md, or any other caller that dispatches the reviewer. The three trust properties below are load-bearing — break any one and the verification layer is theatre.
Three trust properties — why each one matters
1. Fresh context
The reviewer is dispatched as a Claude Code subagent via the Agent tool. That gives it a fresh context by construction — it does not inherit the developer's conversation buffer, the orchestrator's prior turns, or any reasoning chain that produced the change it is reviewing.
This is the property that makes the verdict worth trusting. Without it, the reviewer is the same model reading its own chain of thought — by then the wrong answer has already been written down once, in the developer's voice, and confirmation bias is fully baited. With it, the reviewer sees only the artifacts on Linear and the PR diff — exactly what the product owner would see if they audited it themselves.
Implications for the orchestrator that dispatches:
- The envelope is the reviewer's entire world. Pass the spec, the AC, every per-unit progress comment, the solution brief, the PR url, the CI signal, and the worktree path. Anything you don't put in the envelope is invisible to it.
- No inline "by the way" context. If the developer flagged a tricky edge case in its progress comment, that belongs in the envelope verbatim. The reviewer does not see your conversation; it cannot ask you to clarify.
- Mirror the developer envelope's discipline.
commands/solve.mddispatches the developer with a tight inline envelope;commands/review.mddoes the same for the reviewer. The two dispatches are structurally identical — fresh agent, fresh context, complete envelope.
2. Restricted tool grant
The reviewer's tool grant is deliberately narrow. From agents/reviewer.md's
frontmatter:
tools: Read, Grep, Glob, Bash, mcp__linear__get_issue, mcp__linear__list_comments, mcp__linear__save_comment
It can:
- read any file in the worktree (
Read,Grep,Glob), - run commands (
Bash) for machine-verifiable checks — tests, builds,ghreads,gitreads — strictly read-only: nogit add/commit/push/gh pr merge/gh pr close, - read the issue (
get_issue/list_comments), - post one comment on the issue (
save_comment, edited in place).
It cannot:
- edit code (
Edit,Write,NotebookEdit), - change Linear state or restructure issues (
save_issue— not in the grant), - merge the PR or transition the issue — those are the orchestrator's job.
This is the judge / act split. The reviewer's verdict is its only output; the scrum-master reads the verdict and acts on it (merge, transition, send back). If the reviewer's verdict says "send it back", that does not happen by the reviewer's hand — it happens by the scrum-master's. That separation keeps the reviewer honest: it has no power to "fix it" mid-review and tell itself the AC is now met.
3. Machine-verifiable check execution — with cited evidence
The reviewer's contract — written into agents/reviewer.md — is to run the check,
not trust the developer's word for it. For every - [ ] AC bullet that can be checked
by a command, the reviewer:
- decides what command would prove or disprove it (file existence,
Grepfor promised string,gh pr checksrollup, re-run a test command, etc.), - runs it with
Bash/Read/Grep/Glob, - cites the evidence in the verdict: the command, the output it saw, what it proved.
Example verdict line (the verdict body is a Linear comment, so state is a
- [x]/- [ ]checkbox + bold state label, never a status emoji — seeoutput-styles/linear-comment.md):- [x] **MET** `agents/reviewer.md` exists with restricted tool grant — verified with `Grep -n 'tools:' agents/reviewer.md` showing `Read, Grep, Glob, Bash, mcp__linear__get_issue, mcp__linear__list_comments, mcp__linear__save_comment` and no `Edit, Write`.
This is the regression guard. A deliberately-incomplete solution the developer
reported solved cannot pass — the reviewer's verdict has to point at a real command
that proved each AC met, and a missing piece is a missing piece.
When NB-324 lands (typed AC kinds — [test] / [manual] / [review]), this
becomes much sharper: [test] items will name the command to run, [manual] items
will be explicit PO asks, [review] items will stay judgement calls. Until then the
reviewer heuristically classifies each - [ ] bullet and runs what it can.
Pure-judgement AC ("is this prose clearer?", "is this good enough?") is not machine-verifiable — the reviewer marks it NEEDS-PO rather than guess. The distinction matters: hiding a judgement call as a MET reintroduces the self-marking failure the whole role exists to prevent.
Standards corpus — index-first load order (NB-380)
The reviewer judges every change not only against the AC and the DoD but against the
standards corpus — the Accepted ADRs under docs/standards/adrs/ (an Accepted ADR is
a hard rule; violating one is UNMET, same weight as a failed DoD line). The risk this
introduces is the one NB-380 exists to defuse: if "consult the standards" meant load all
the prose ADRs into context every review, the verification layer would get slow, burn the
token/context budget (the NB-379 quota pressure), and — worst — a reviewer swimming in N
prose standards would miss the one that applies. With a corpus that grows over time, that
failure gets worse, not better.
The fix is the authoring format, not a storage backend. Each ADR carries
machine-readable front-matter — a crisp checkable assertion, an applies-to scope
(domains / file-patterns / decision-types), and a lifecycle status — and
scripts/standards_index.py generates a single small committed artifact,
docs/standards/index.json, from it. The reviewer's load order (wired into
agents/reviewer.md):
- Read the compact index first (
docs/standards/index.json) — cheap, the only standards file always read. - Filter to applicable standards by
applies-to— match the diff's changed files (globfile-patterns), area (domains), and decision kind (decision-types); enforce only the currentAcceptedset and skip every non-Acceptedstatus—Proposed(not yet binding),Superseded, andDeprecated(history). ADRs stay agile: Accepted today is a hard rule but reconsiderable — reopen-and-supersede per the template lifecycle, so the reviewer enforces only what is currently Accepted. Most ADRs are irrelevant to any given diff. - Judge against each applicable
assertion— usually enough to call met/unmet. - Open a full ADR only when the rationale is needed — never the whole set.
This keeps the reviewer's context bounded regardless of corpus size — the property that lets the standards gate scale. It is the same regression-guard discipline as property 3: the reviewer cites which indexed standards were applicable and how the diff met them.
Boundary — v1 is index/files only, no graph DB, no server. This honours the
keyless/serverless principle (ADR-002): the index is a committed JSON file generated by a
zero-dependency stdlib script (mirroring scripts/graph.py), and the
scripts/test_standards_index.py drift checker fails CI if the committed index diverges
from the corpus. The named v2 follow-up (NB-320) — not in scope here — unifies this
corpus with the execution graph in Neo4j to query inspection patterns (standards correlated
with rework, domains with a high blocker-rate and no governing standard, which specialist
most hits missing-standard blocks). That cross-run join is where a graph DB finally earns
its place; v1 deliberately ships only the files + index that solve the lookup pain now.
Read this section before adding a standard to
docs/standards/adrs/or changing how the reviewer consults it. Front-matter is the single source of truth: edit it, then regenerate the index (python scripts/standards_index.py) in the same change — CI's drift test enforces the two stay in sync.
The fourth verdict outcome — block (missing load-bearing standard)
The index walk above closes most reviews cleanly: an applicable standard is honoured or violated. It has one soft ending — no indexed standard applies — and that soft ending is right only for a non-consequential change. The hole it leaves is the dangerous case: a change that decides something consequential (a one-way-door choice — auth model, data format, state ownership, a public contract) for which no Accepted standard exists. "No applicable standard" then doesn't mean fine; it means the governing standard is missing, and silently accepting bakes an ungoverned decision into the corpus by default.
So the reviewer's verdict has a fourth outcome — block — alongside accepted / sent
back / needs you (the gate's binary ok / needs-changes rolls a block up as
needs-changes, since the gate can't wait on an ADR or the PO):
-
Detect & block, don't invent. When a consequential decision has no governing Accepted standard, the verdict is a
block(not an accept). The reviewer names the missing standard ("this change decides X; no Accepted standard governs X") and does NOT invent one to unblock — inventing the rule that clears your own block is the self-marking failure the whole role exists to prevent. State the gap; never fill it. -
Classify the gap, write it into the verdict. Every block is tagged:
- missing standard — a durable, cross-issue governance gap → graduates into an ADR, escalates to the PO. Only this kind enters the corpus.
- missing fact — a one-time lookup (a value, a path, a version) → answered once, no ADR, no PO.
The classification is written into the verdict body (a "Missing standard / fact" section, one NO-STANDARD line per gap) so the scrum-master can route it — see the boundaries below.
Boundary — mechanism here, threshold and routing elsewhere. This section (and the
matching agents/reviewer.md outcome) introduces the mechanism: the block outcome plus
the standard/fact classification. Two adjacent decisions live elsewhere, not in this trust
model:
- The blocking threshold — when a missing standard rises to a block vs a
flagged-assumption-and-proceed — is a reversibility × blast-radius calibration: a
blockfires only on a one-way door (irreversible AND wide blast-radius — data model, auth/onboarding model, public API shape, the keyless principle); a two-way door (reversible or narrow) gets a flagged assumption recorded in the verdict and proceeds. This is what keeps a blank repo usable — the empty-corpus bootstrap degrades gracefully rather than firing fifty blocking questions on issue #1. The calibration itself (the threshold + worked examples) is documented operationally inagents/reviewer.md(Calibrating the block — reversibility × blast-radius, NB-386); this trust model states that it exists and why (a wrongful block stalls a usable repo), and the agent doc owns how it is applied. - The scrum-master routing of a block — open a "Define standard for X" sub-issue, mark
the parent blocked-by it, ask the PO — lives in
commands/review.md/commands/solve.md/skills/scrum/(a separate unit, NB-385). The reviewer's job ends at writing the named, classified gap into the verdict; the scrum-master reads it and acts (judge / act split — property 2). The reviewer never opens the sub-issue or changes state itself.
NB-340: tool-grant hazard the orchestrator must work around
A finding from a parallel run (NB-340, Backlog, High) documents that subagent runtime
tool grants may be frontmatter ∩ parent's currently-loaded deferred tools. In
plain English: even if the reviewer's frontmatter lists
mcp__linear__save_comment, the subagent may not actually have it at runtime
unless the parent has loaded it first (via a prior call from the orchestrator's
own context).
This is a Claude Code platform behaviour — not a backlogd bug — but /backlogd:review
has to work around it. The mechanism:
/backlogd:reviewpre-loads the deferred Linear MCP tools before anyAgent({subagent_type: "reviewer", ...})dispatch via a single batchedToolSearchcall across the canonical Linear MCP tool list (see step 0 ofcommands/review.mdandskills/linear/SKILL.md→ Deferred tools — pre-load before dispatch for the canonical list). This is the NB-346 mitigation — defense in depth at the orchestrator layer so the reviewer's restricted-granttools:list receives every Linear tool it names at runtime.- If the reviewer reports it could not post its
**[backlogd reviewer]**comment, treat that as a tool-grant skew, not a reviewer failure. Re-dispatch from a fresh session where the pre-load has happened, or fall back to the orchestrator copying the reviewer's structured-return verdict into a comment itself (audit-trail loss; only as a last resort).
The same hazard applies to any subagent in backlogd that needs an MCP tool — the
developer agent (agents/developer.md) is the other case. /backlogd:solve's
dispatch is the symmetric pre-load point.
Specialist-grant contract (NB-413)
NB-340/NB-368 above are about Linear MCP tools a subagent may not have at runtime.
NB-413 is the sibling case for the runnable checks an AC requires — markdownlint, an
index regen + --check, a drift test, a label/issue create. A specialist on a narrow grant
(e.g. a docs specialist with no Bash) could be dispatched against such an AC, and the old
behaviour let the un-runnable check fall silently to the orchestrator or the pre-commit
gate — relocating the trust boundary upward with nobody deciding it should. The instances:
NB-379 (a docs dispatch with only Read/Grep/Glob/Edit/Write + Linear deferred 12 markdownlint
errors, the index regen, and the drift test to the orchestrator) and NB-381 (a developer on a
save_comment-only grant could not create the kind:improvement label its AC implied).
The contract — two complementary halves, neither widening the restricted grants:
-
Explicit, enumerated hand-off (the general rule). A specialist runs every AC-required check its grant can run, and enumerates each one it cannot in a machine-readable
Deferred-checks:line in its STATUS report (agents/developer.md→ TheDeferred-checks:line).Deferred-checks: noneis the honest default when it ran everything. The gate is then obligated to run each enumerated check and folds a failure intoneeds-changes(skills/solve/gate.md→ Gate-mandated checks the reviewer runs (b)). This replaces silent deferral: the boundary still moves when a grant is too narrow, but on purpose and recorded, not by accident. -
markdownlint folded into the gate (the common docs check). The single most common un-runnable check — markdownlint on changed
.md— is run unconditionally by the reviewer in every pre-commit gate, because the reviewer already holdsBashand already runs every machine-verifiable check itself. It is invoked the way CI does (markdownlint-cli2pinned tov0.22.1, reading.markdownlint-cli2.jsonc), trusting the printed error count over the process exit code — an ad-hoc invocation can print a false exit-0 while CI still reds (NB-417, where a green tester + reviewer let an MD038 through to CI). So a docs specialist need not even enumerate markdownlint: the gate always runs it. Seeskills/solve/gate.md→ Gate-mandated checks the reviewer runs (a).
Why not just widen every grant? Rejected. The restricted specialists
(reviewer/tester/refiner) keep narrow grants by design (the reviewer's missing
Edit/Write is what makes its verdict trustworthy — Restricted tool grant above), and a
broader tools: list is more for the NB-340 intersection to silently drop. The generic
developer already inherits a broad grant (no tools: line, NB-345) and runs its own
checks. Widening the restricted roles to self-check would trade away the property that
earns their trust.
Why not the NB-353 disallowedTools: inverted grant? Evaluated, deferred. Inheriting
the parent surface and subtracting (e.g. disallowedTools: Write, Edit, Bash) is a smaller
frontmatter with less for the intersection to drop, and is attractive prior art
(Yeachan-Heo/oh-my-claudecode). But its runtime interaction with the NB-340
frontmatter ∩ loaded-deferred-tools behaviour is unverified here (NB-368 verified the
positive tools: list path, not the inverted one), and adopting it now would couple this
trust-contract fix to an unproven harness behaviour. It stays a candidate for a focused
follow-up; the enumerated hand-off + gate-runs-markdownlint contract above does not depend on
it.
The decision lives here (the trust model) and is mirrored in
skills/linear/SKILL.md → Specialist-grant contract, alongside the NB-340 tool-grant
lineage it extends.
How the reviewer fits into the loop
/backlogd:solve → developer (one per unit) → [pre-commit gate] → solution brief → In Review
↓
ship-on-green auto-chain (happy path, on by default) ──┐ ┌── /backlogd:review (manual re-entry)
↓ ↓
reviewer (fresh, restricted) → verdict draft
↓
orchestrator acts (merge → Done on fully-green / send back / surface needs-PO / route no-standard block)
The verdict pass has two triggers, one engine: /backlogd:solve's ship-on-green final
phase auto-chains it on the happy path (no human gate), and /backlogd:review remains the
manual re-entry point the PO can invoke any time. Both dispatch the identical
verdict-mode reviewer and act on the identical merge decision.
The same reviewer subagent is dispatched in two distinct passes, both honouring the three trust properties above:
- The pre-commit gate — dispatched inside
/backlogd:solveper unit, before commit, inpre-commit-gatemode (binaryok/needs-changes; seeskills/solve/gate.md). This is the in-session gate. - The independent verdict — dispatched after In Review against the whole problem, in
verdictmode (accepted/sent back/needs PO/block; seecommands/review.md). This is the fresh-context pass whoseacceptedrollup gates the merge — ablock(missing load-bearing standard) holds the merge until the scrum-master routes the gap (NB-385).
The verdict pass is never triggered by the reviewer itself — it is initiated either by
the PO running /backlogd:review, or automatically as /backlogd:solve's ship-on-green
final phase (skills/solve/ship.md), which auto-chains the same verdict-mode dispatch and
merge decision on the happy path. Ship-on-green removes the human trigger, never the
independent verification: the merge is gated on this independent reviewer's accepted
rollup, not on the pre-commit gate alone — the two passes stay separate, and both run.
Boundaries — what is not the reviewer's job
- Style review. The reviewer judges AC satisfaction, not formatting, naming, or "is the code beautiful". Style is the developer's concern (and code-review tooling's); over-extending the reviewer here turns the verdict into noise.
- Definition of Done. If the team adopts a DoD (see NB-329), it extends what the reviewer judges, not how (still: run checks, cite evidence). The trust properties above do not change. NB-329's reviewer adds a DoD walk on top of the AC walk — same engine.
- Re-dispatching the developer. That's the orchestrator's call from the rollup verdict. The reviewer judges and stops.
- Posting the PO-facing
**[backlogd review]**rollup. That comment belongs to/backlogd:review(the scrum-master). The reviewer posts the**[backlogd reviewer]**draft; the orchestrator may lift it verbatim into the rollup or annotate it. Two distinct badges, two distinct authors, two distinct audiences (audit trail vs PO). - Restating the block threshold in this trust model. The reviewer detects a missing
load-bearing standard, emits the
blockoutcome (above), and applies the reversibility × blast-radius calibration (one-way doors block; two-way doors proceed with a flagged assumption) — but that operational threshold + its worked examples live inagents/reviewer.md(Calibrating the block, NB-386), not here. This skill is the trust model (why the outcome exists, why a wrongful block hurts a blank repo); the agent doc owns how the reviewer calibrates each call. - Routing a block. Acting on a
block— opening a "Define standard for X" sub-issue, marking the parent blocked-by it, asking the PO, deciding ADR-vs-answer-once — is the scrum-master's job (commands/review.md/commands/solve.md/skills/scrum/, NB-385). The reviewer writes the named, classified gap into the verdict and stops — judge / act split. It does not open the sub-issue or change state.
Pitfalls checklist
Each pitfall pairs the Wrong move with the Right one (no status emoji — these are prose do/don't pairs, kept consistent with the no-emoji rule the verdict surfaces follow):
- Wrong: dispatching the reviewer with implicit "you know what we're working on" context (it doesn't). Right: inline envelope with every artifact it needs.
- Wrong: treating the developer's
solvedclaim as evidence the AC is met (that's exactly the self-marking failure mode). Right: the reviewer runs the check; the developer's claim is a hypothesis, not a fact. - Wrong: a MET AC line in the verdict with no cited command or file path
(unverifiable by the PO, indistinguishable from theatre). Right: every MET
carries cited evidence (
command → output,file:line, etc.). - Wrong: the reviewer editing a file "to make the AC pass" (blows the judge/act split;
its tool grant excludes
Edit/Writeprecisely so this is not possible). Right: send it back via the orchestrator. - Wrong: the orchestrator dispatching the reviewer before any
mcp__linear__*call has loaded the deferred tool (reviewer can't post its comment, NB-340). Right:commands/review.mdstep 0 pre-loads each deferred tool from the orchestrator first. - Wrong: posting both
**[backlogd reviewer]**and**[backlogd review]**from the reviewer (double-posting, breaks the in-place edit contract, blurs authorship). Right: reviewer posts only**[backlogd reviewer]**; orchestrator posts only**[backlogd review]**. - Wrong: a consequential, ungoverned decision passed as "no applicable standard for
this diff" (an ungoverned one-way-door silently baked into the corpus). Right: a
consequential decision with no governing Accepted standard is a
block— name the missing standard. - Wrong: the reviewer inventing the missing standard to clear its own block (self-marking; it just authored the rule it then judged itself against). Right: state the gap, classify it (standard / fact), and hand it to the scrum-master to route (NB-385), never fill it.