Review system
Type: types/note.md
Review asks a language-model worker to judge a note against a criterion file
and keeps the judgment while both texts stay unchanged. One command,
commonplace-review, runs review as Python workflows over headless harness
launches and keeps one result file per (note, criterion) pair. Notes and
criteria stay ordinary markdown files; no database is involved.
Two properties shape the rest:
- Freshness is independent of Git. A result records the SHA-256 of the note text and the criterion text the worker was given. A pair is fresh while both hashes match the current files.
- Review is opt-in. It is not part of the default writing flow, and no check runs it automatically. You run it when you want notes judged, and each run costs paid launches.
For how it is built, see review architecture;
ADR 127 records
the decision. Exact arguments are in commonplace-review --help and
commands.
Concepts
Criterion. The markdown file a note is judged against. It may be a
catalog gate, the note's type spec, its collection's COLLECTION.md or the
critique instruction. A criterion is named by its artifact identity: a
repository path, or commonplace:<path> for a library file outside the
repository, as the gates are in an installed project.
Pair. One note and one criterion: the unit of review, result and freshness.
Gate. A catalog criterion with a closed question whose outcome is pass,
warn or fail. Gates live in the library's
instructions/review-gates/<lens>/<name>.md (kb/instructions/review-gates/
in this checkout). The gate id is <lens>/<name>, for example
prose/source-residue. A gate's requires_type and requires_trait filter
the notes it applies to; a skipped gate is not a passed gate.
Bundle. The gates of one lens directory: prose means every gate under
review-gates/prose/.
Type-conformance pair. The note judged against the type spec its type:
names (ADR 038).
Request it with the type lens, or type/<name> for one type's notes. A
fixed wrapper tells the worker how to apply a type spec as a gate; a
## Review section in the spec is the operative test. Structural checks stay
with the validator.
Collection-conformance pair. The note judged against its collection's
COLLECTION.md (ADR 041).
Request it with the collection lens, or collection/<path> with the
collection directory relative to kb/ (collection/theory). The wrapper
asks only for what the contract adds beyond the type spec, so the two
conformance pairs do not flag the same thing twice.
Critique. The critique lens applies the critique instruction as a
report criterion: the worker writes an open-ended report, and the outcome is
report. Critique is opt-in; --all-gates and the default status set leave
it out.
Outcome. A verdict review ends with exactly one ## Result: PASS,
## Result: WARN or ## Result: FAIL line as its last non-empty line; a
report ends with ## Result: REPORT. An alias, a second result line or any
other result-like line rejects the review, and the worker is asked again
with the problem.
Watches. A gate may declare watches: with any of title,
description and body. The note side of its freshness then hashes only
those parts, so an edit elsewhere, such as to tags:, leaves its pairs
fresh. A criterion without a valid declaration watches the whole note; type
specs and collection contracts declare none. The declaration therefore
decides which edits stale a gate's pairs.
Criterion-derived local paths. A worker reads the note, the criterion
and files the note links to. One exception is open to any criterion: it may
tell the worker to derive exactly one local path from a link in the note and
read it. semantic/grounding-alignment uses it for an ingest link marked
(snapshot required), which sends the worker to the matching
kb/sources/.snapshots/<slug>.md
(ADR 073).
It permits one named path from a link already in the note, not a search of
the corpus.
| Do not conflate | Distinction |
|---|---|
| fresh and approved | Fresh means the result was produced from the current texts. It does not approve the note or resolve a report's findings. |
report and a verdict |
report says the report contract completed; it is not a fifth outcome. |
| pass and absence of problems | A pass says one gate found nothing to flag. |
Result files and freshness
Each accepted review writes
kb/reports/state/review/results/<note-path>/<criterion-id>.md. The
criterion id is the gate id for a catalog gate, the repository path without
.md for another repository criterion (kb/types/note), and commonplace/
plus the library path for another library criterion. The frontmatter holds
note, note-sha256, criterion, criterion-sha256, outcome, profile
(harness, launch model, effort), model (what the harness recorded), run,
launch and completed-at. The body is the worker's review, unchanged.
A pair is in one of three states:
fresh: both hashes match the current files;missing: no result exists;stale: the note, the criterion, or both changed; status names which.
A result whose note or criterion file no longer exists is orphaned. A moved note orphans its results too. Removing an orphan is deleting its file; the moved note's pairs are reviewed again.
Result files are ignored by Git and stay on the machine that ran the review. One result exists per pair, whichever profile produced it; a later review replaces it.
Reading status
status launches nothing.
commonplace-review status kb/theory/example.md # every applicable gate pair, with missing ones
commonplace-review status kb/theory/ --criteria type collection
commonplace-review status --fresh --outcome warn # current warnings, from every stored result
commonplace-review status --stale kb/theory/ # what a sweep would ask
commonplace-review status --json --fresh --outcome warn # rows with each result file's path
With notes, status covers every applicable pair for the requested criteria
(all gates when --criteria is omitted), including pairs never reviewed.
Without notes, it covers every stored result whose note and criterion still
exist. Each row shows the note, criterion id, state, outcome and model;
orphaned results are listed apart. A directory means its direct
frontmatter-bearing notes, not nested ones, and leaves out indexes, collection
contracts and type specs. Name the notes of a validation-ignored directory one
by one.
The current warnings are the fixing queue: read each finding in the result
file --json names, and fix under the fix procedure.
commonplace-status --review adds counts of fresh, stale and orphaned
results and of current warn and fail outcomes to the project status.
Running a gate sweep
commonplace-review run gates kb/theory/example.md --criteria prose semantic/grounding-alignment
commonplace-review run gates kb/theory/ --all-gates --grouping bundle --profile sonnet
--criteria takes gate ids, bundle names and the lenses; --all-gates means
every bundle plus type and collection. The sweep skips pairs with a fresh
result unless --force is given, and asks a worker for each other pair:
--grouping pair(the default) makes one call per pair;--grouping bundlemakes one call per note and bundle for that bundle's stale gates. Each gate is judged on its own and gets its own result. A bundle with one stale gate, and every type, collection and critique pair, is still one call per pair.
A call has three attempts; a rejected review and a launch that returned no
answer each spend one. A worker that cannot judge writes a problem instead
of guessing, and no result is written for its pairs. --max-workers
(default 4) bounds the launches running at once.
Each sweep opens a run directory under kb/reports/state/review/runs/<UTC
stamp>/ and prints a JSON report: per pair its state (fresh, reviewed,
problem, out-of-attempts or not-run), the counts, what stopped the
sweep and the guard's finding. It exits 0 only when every pair has a current
result and the guard saw no change, 1 otherwise, and 2 when the command
failed.
Stops, retry and resume
- Launch failure or usage limit. The sweep stops asking new calls; results
already written stay. Repair the environment or wait for the reset, then
rerun with
--resume RUN_DIRand the same notes and criteria. Answered calls replay without a launch. - Worker problem. Read it in the report. When the cause is fixed,
commonplace-review retry RUN JOBdismisses it and a resumed run asks again. A pair's job is<note>::<criterion-id>; a bundle call's is<note>::<bundle>/*. - A wrong answer.
retry RUN JOB --key KEYdismisses one answer. Its result file still makes the pair fresh, so rerun with--resumeand--force: the dismissed call is asked again and the others replay. - An interrupted controller. If a run was killed while a launch was
running, check that the worker process and its children are gone, then
commonplace-review acknowledge RUN LAUNCH. The command inspects and kills nothing.
One process at a time may hold a run; a second exits 2.
The checkout guard
A run records git status (with untracked files), the content digest of
every listed path and HEAD, before and after, ignoring
kb/reports/state/review/. The report lists every path that differs. A full
pass or revision leaves out its note only when the note holds the final text
the run applied.
What it can claim: these paths changed while the run was open. What it cannot claim: that a worker changed them. Other sessions editing or committing in the same checkout show up as well, so a failed guard means "inspect these paths". Nothing is reverted. The guard also misses a change to an ignored file and a change undone before the run ended.
Worker profiles
Profiles live in the library's instructions/review-profiles.yaml, with the
entry shape of task profiles: harness, launch model, effort and harness
flags. --profile NAME picks one; the file's default applies otherwise. A
resumed sweep keeps the profile it opened with.
sonnet(the default) andopusrun Claude Code with read tools, edit rights only underkb/reports/state/review/runs/, and Bash denied. That this denial refuses a write has not been observed; the guard is the verified control.codexruns in aworkspace-writesandbox, which cannot keep the checkout read-only. The prompt forbids writing and the guard reports it.
Cost
A launch's fixed context dominates its cost. Single observations with the
sonnet profile:
| Run | Result |
|---|---|
| one gate on one note, one launch | $0.095, 13 s, nearly all fixed context |
the ten prose gates on one short note, one bundle launch |
nine pairs reviewed, $0.172, 31 s |
| one full pass on one short note | 27 launches, $4.47, ended in a hand-back |
Bundle grouping shares the fixed context. Whether it changes verdict quality
has not been compared, which is why pair stays the default.
Planning-free baseline
Evaluate one operation before composing it with planning or other operations:
- One review:
commonplace-review run gates NOTE --criteria GATE-ID --grouping pair. Supply one gate id, not a bundle. The result is report-only; even a failure does not select more work or edit the note. Fresh pairs are skipped. - One editing pass:
commonplace-review run revise ARTIFACT --plan FILE. The supplied YAML plan fixes purpose, protected claims, pass and scope. No assessment request is constructed. Editors receive execution instructions, not the planning procedure. See the fixed-plan example.
These operations still use judgment inside their fixed tasks. Planning means choosing work, not merely using a model. Assessment-led revision and full-pass composition need separate evaluation; reliable replay does not establish reliable selection or editing judgment.
Workflows beyond the sweep
Full improvement pass. commonplace-review run full-pass NOTE runs the
method reports, a packet, and for an accepted keep an edit, a copyedit and
a closing cycle with at most one repair. Workers write outputs only. Each
edited text stays in the run directory, and later steps work on that text.
The note is written once, only when closing is ready and the exact final
publication manifest has been approved. Until then, the candidate packet,
results and note remain unpublished. A hand-back leaves the note untouched;
no-edit dispositions may publish their report, packet and results automatically.
Gates and critique are asked over each text even
when a fresh result exists. The pass writes their result files once, at its
end, for the text the note then holds. The note's
current packet lives at kb/reports/state/review/packets/<note-path>;
commonplace-review packet checks its recorded hashes against the files.
Operate it with run a full improvement pass
and resolve a full-pass disposition;
full improvement pass closure explains
the closing cycle.
Revision. commonplace-review run revise applies the
simplification passes to
one artifact. A new run requires --plan FILE for fixed work or --assess
for assessment and automatic pass selection. Within the chosen mode, ordinary
intermediate work is preauthorized. The assessment report and generated plan
stay in the run for inspection; the validated plan runs without routine
approval. --plan FILE also supplies a validated override of a stopped plan.
Editing the retained plan alone is not an override.
Each listed pass runs in order on the preceding text; its effort selects
the profile. Every pass must end applied, no-change or skipped before
changed final text is offered for publication approval. Control-flow checks
do not establish the quality of the selected edits.
Plain-wording and abstractions still stop for explicit approval of each selection proposal. These are temporary procedure-development points: retain proposed, accepted and rejected choices with reasons, evolve the selection rules and checks, and evaluate retained and fresh examples before separately authorizing removal. This is not new automatic learning infrastructure, and fewer proposals do not establish reliability.
Runs of the two workflows. Their run directory is derived from the
inputs: kb/reports/state/review/runs/<workflow>/<note path without
.md>/<first 12 hex of the note's SHA-256>/. Repeating the same command on
the same note text continues that run, and answered calls replay. An edited
note, including one the workflow itself wrote, has another hash, so the same
command opens a new run. Revision then requires a new --plan or --assess
choice rather than silently assessing again. Older revision runs without a
recorded mode require an explicit choice too. A repeat reuses the options the run recorded,
including the full pass's --direction; an option given on the repeat
replaces the recorded value. A stop for the operator proposes a file in the
run directory, and the report's waiting-for gives what to do, the file,
the approval command and the command to repeat. Approval captures the file's
current text; repeating alone never approves it. Selection-list edits after
approval require another approval and appear under unapproved-edits.
Changed proposal inputs require fresh approval. A plan with stop set ends
the run; replace it explicitly with --plan, not an approval command. An
agent may approve only on the operator's stated word for that proposal.
For changed final text in either workflow, inspect artifact/final.md and
artifact/final.diff, then approve the exact generated
artifact/publication.json with commonplace-review approve RUN FILE --key KEY
and repeat. The manifest is not editable approval input: publication requires
an exact match. To revise, use --plan for revision, change workflow inputs,
or start a new run; do not edit the manifest or candidate. Publication also
requires the original artifact and captured brief to remain unchanged.
A conflict leaves the original untouched and names the proposed text.
Instructions remain the judgment authority; prompts name inputs only.
Interpreting results
A pass records that one gate found nothing to flag; it is not
certification. A fresh critique matches the current inputs; it does not mean
its findings were handled. When a fix produces a diff, review the diff rather
than the fixing agent's report of success. Open-ended methods stay
report-shaped instead of being forced into verdicts.
Authoring a gate
Gates are typed review-gate; that type spec owns
their frontmatter and body. Keep the judgment in the gate text: the prompt
around the inputs is outside the freshness hashes, so only an edit to a
criterion file stales its results. Declare watches: with the note parts the
gate actually inspects, because it decides which edits stale its pairs.
staleness: changed is the only accepted value.