@geonosis/review
v3.0.0
Published
The decision-only reviewer contract: a review's sole output is one JSON decision, validated and applied by a step that comments nowhere, approves nothing and cannot reach a forge.
Maintainers
Readme
@geonosis/review
Through the front door: geonosis review — the metapackage pins this and every other kit tool at
ONE version, and passes the exit code through unchanged.
The decision-only reviewer contract. A review's sole output is one JSON decision; a deterministic step validates it and records it. The step comments nowhere, labels nothing, approves nothing, and has no forge client and no network client in it — by construction, and there is a test that reads the shipped bundle to keep it that way.
Ported from Medusa's reviewing-prs skill, which runs this in production: read-only tools, one
review-decision.json, and the mutation scripts deliberately unavailable in this job.
npm i -D @geonosis/review zod
geonosis-review apply decision.json [--to ledger|stdout] [--slug <name>]
geonosis-review check decision.json --criteria plans/022-1.0.0-everything.md| exit | what it means |
|---|---|
| 0 | applied |
| 1 | the review was READ and REFUSED — prose, a bad shape, a contradiction, a criterion never checked |
| 2 | the command could not run — no file, no verb, an unknown flag, an option with no value, a plan with no criteria |
Only 1 is an answer about a review. Everything the command line itself gets wrong is 2, on one
line and with no stack trace, so a caller that branches on the code never reads a typo as a refusal.
The decision
{
"decision": "request-changes",
"summary": "The ops-leakage probe reads its patterns twice and defaults them in one of the two.",
"findings": [
{
"file": "packages/walk/src/probes/ops-leakage.ts",
"line": 21,
"severity": "blocking",
"criterion": "When a probe is enabled without its list, the run shall refuse",
"text": "needs() requires patterns but run() falls back to an empty list, so a walk that skipped needs() reports a clean page."
}
],
"criteriaChecked": [
"When a probe is enabled without its list, the run shall refuse"
]
}decision is approve, request-changes or comment — the three outcomes a review has anywhere.
Medusa's two closing templates are not here: closing is a forge action, and this applier takes none.
severity is blocking, major or minor. file and text are required on every finding,
because "being vague about required changes" is on Medusa's own list of mistakes and a finding that
names neither is exactly that. criterion is the field Medusa has no analogue for: it binds a
finding to the plan's EARS criterion.
What is refused
Beyond the schema — which is strict, so an unknown key is refused rather than dropped:
- a prose review, in the first clause of the message rather than after a schema dump;
- an approval that lists a blocking finding — Medusa's "Common Mistakes", made mechanical;
- a
request-changeswith nothing blocking or major in it — the same failure from the other side: a decision the author cannot act on, and how a review loop reaches round three having said nothing; - a decision that never looked at a criterion the plan states (
check), naming which.
check --criteria
Every EARS criterion under the plan's acceptance heading must appear in criteriaChecked, matched
on the whole criterion with whitespace normalised — a criterion an editor reflowed is the same
criterion; a criterion half-quoted is not, or criteriaChecked: ["When"] would cover a plan.
The heading and the criterion pattern are configuration, because a heading is a repo's vocabulary:
{
"review": {
"criteriaHeading": "^#{2,3} +Acceptance criteria\\b",
"criteria": "^- When .+ shall .+",
"proofs": "proofs",
"runner": "peer-session"
}
}runner
peer-session or agent:<model> (C13). D-009 said the reviewer runs on a different model; D-016
said the cross-context check is the peer session that owns the other repo. Both are not the
author, which is the property that matters — so the kit ships the contract and leaves the runner to
configuration. Nothing in this package acts on the field; the agent text reads it, because a
library that picked a runner would have picked for every consumer.
Why it does not use @geonosis/ledger
apply --to ledger writes <proofs>/<slug>.md itself, and this package imports nothing of the kit.
Three measured reasons:
- the ledger's only proof API is
captureProof, which runs a command and captures its output — a decision that already exists is not a command, and an applier that shells out is not deterministic; captureProofwritesNNN-slug.md, one past the highest number on disk, so the same decision lands at a different path on a different tree — the opposite of a deterministic apply;- a reviewer that can reach the ledger is one call away from writing the tick it just judged. The scored agent never writes the scoreboard.
Running apply twice on the same decision rewrites the same bytes at the same path.
