6.8 KiB
name, description
| name | description |
|---|---|
| code-review | Review a pull request or diff against this repository's own written architecture and testing doctrine — the Cmd/Effect split, exhaustive enums over AppMode/Axis/CategoryKind, crate layering, the CellKey/layout/persistence invariants, and when a green suite is not evidence. Use when reviewing any change to this repo, and before pushing your own work. |
Code review
This repo has already written down what "correct shape" means, in more detail than most. A review here is mostly checking a change against that, rather than against generic standards.
Two passes, in order:
- Correctness — does the change do what it claims? Wrong logic, missed edge cases, error paths that swallow failures. Ordinary review judgement.
- Conformance — does it fit the design? That is what the rest of this
file is for, and it is the part a reviewer who has not read
context/will miss.
Read the sources, don't trust this file
This skill is an index. The rules live in files that change, and restating them here would let the copy drift:
| Source | What it holds |
|---|---|
context/design-principles.md |
The "why" — functional-first, polymorphism over conditionals, correctness by construction, layering, §7 testing doctrine |
context/repo-map.md |
The "where", plus numbered gotchas — read the ones your diff touches |
prolog-sessions/kb.pl |
Executable knowledge: invariant/2, doctrine/2, forbidden/2, decision/3, and prior work_done/3 |
AGENTS.md |
Working rules: bug-first tests, Option/Result/Box at the edges, merges over rebases |
kb.pl is the highest-value and least-obvious of these. Grep it before
reviewing: grep -n "invariant(\|doctrine(\|decision(" prolog-sessions/kb.pl.
A decision/3 fact often explains why the thing you are about to flag looks
the way it does.
What to check
Command/effect split. Cmd::execute takes &CmdContext and returns
Vec<Box<dyn Effect>>. Mutation happens only in Effect::apply. A command
reaching for mutable state is the single most common shape error.
New effects. kind() has no default, so the compiler asks — but check
the classification is right, not just present (doctrine(derive_then_verify)
records naive extraction mis-classifying three effects). Note there is no
changes_mode() any more: mode settling is observed after the batch, not
predicted per type (gotcha 31, improvise-toc). Don't reintroduce a static
flag for a conditional behaviour.
Exhaustive matches. AppMode, Axis, BinOp, CategoryKind and friends
should match exhaustively — a _ => arm is how adding a variant silently
skips a site. When a new variant shares one property with an existing variant
but not another, name both as predicates and route call sites through the one
they mean, rather than testing for the variant
(doctrine(two_questions_one_enum); Axis::filters_data /
is_user_assignable are the worked example).
Layering. forbidden/2 in kb.pl is the authority:
improvise-core no UI/IO, improvise-io no UI/commands, improvise-command
no ratatui/crossterm. A ratatui type in core is the usual violation — the fix
is a semantic enum in core that the render layer maps (see AxisColor).
Option/Result/Box at the boundaries. Wrap at parse/lookup/IO, unwrap
early, keep core logic on concrete values. Heap-container plumbing should be
split from logic (AGENTS.md).
Invariants the type system does not carry. Check the relevant gotchas
rather than reasoning from first principles — CellKey built through its
constructors, IndexMap order being load-bearing for persistence, display
rounding never fed back into eval, app.layout going stale mid-batch and
needing RebuildLayout in front.
Persistence. The pest grammar is the source of truth; update it first. Wire tokens are not UI labels and must not follow them.
Testing doctrine
From design-principles.md §7 and doctrine/2:
- A bug fix needs a test that failed before it. If the diff claims a fix with no such test, that is the finding. Ask whether it was run against the old code.
- Prefer unit tests over integration/e2e; test the observable contract, not internal storage.
- Suite stays under 2 seconds. Proptest stays at 256 cases — constrain the strategy instead of raising them.
doctrine(check_the_checker): a new check should be proven by injecting the fault it targets. A check nobody has seen fail is indistinguishable from one that always passes.doctrine(populate_before_roundtrip): a roundtrip over a default-constructed value proves almost nothing.doctrine(binding_dump_diff): most keybindings have no direct test, so a green suite is not evidence for a keymap refactor — diff the dumped bindings.
Verifying
Run the gate CI runs, flags and all. Clippy exits 0 on warnings, so without
-D warnings this file was green on exactly the input CI is red on — a check
that cannot fail in the direction the defect lies.
cargo test --workspace --no-fail-fast # fail-fast hides the blast radius
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all --check
python3 scripts/check-comment-runs.py # the two-line comment cap
python3 scripts/check-comment-runs.py --check-evidence # a DIFFERENT question
--check-evidence is additive, so it answers the comment cap too and both
verdicts print. It did not always: the flag used to select, so the flagged
run skipped the cap and said nothing about having skipped it, and three
separate agents hit that in one campaign — a local gate using only the flagged
form went green while CI went red on a three-line doc comment (improvise-ijql).
Run the bare form anyway when you only want the cap: it is 0.3s and needs no
toolchain, where the flagged one needs a compiled workspace.
The knowledge base is checked in CI as code:
nix shell nixpkgs#trealla --command tpl prolog-sessions/kb.pl \
-g "(kb_consistent -> halt(0) ; (kb_report, halt(1)))"
In a cloud container that needs one step first: nix shell resolves nixpkgs
as a GitHub tarball, which is not served there. Run
scripts/nix-seed-flake-inputs.sh to seed the locked inputs over git (it does
not modify flake.lock), after which the command works. Details and the two
alternatives are in context/cloud-agents.md. If you would rather not spend
the time, review kb.pl edits by reading and let CI run the check.
Closing the loop
If the review surfaces a durable fact — a decision, a new invariant, a method
that worked — add it to prolog-sessions/kb.pl as decision/3,
invariant/2 or doctrine/2. If it changes where something lives or how it
behaves, update context/repo-map.md. Those two files and
context/design-principles.md are maintained by hand and are the reason this
review is cheap next time.