-
Notifications
You must be signed in to change notification settings - Fork 0
Internals Walkthroughs
Six ordered checklists for the changes people actually make. Each step names the test that fails if you skip it. Where no test enforces a step it is marked (unenforced) — that is prose, and prose is what gets forgotten, so those are the steps to be deliberate about.
The enforcement claims here were checked by making the change and watching the
gate fail — or, more often than expected, watching it pass. Where the first
draft of this page and reality disagreed, reality won: adding a CLI flag does
not trip docs_drift_test; an MCP tool that holds a lock across an await is
rejected by the compiler before clippy gets a word in; and three of the six
checklists turned out to name gates that do not fire at all for the change
they were attached to. Those steps are now marked (unenforced).
The pattern behind all three: a gate that hardcodes its subjects — three flood scenarios, two output-flag behaviors, and until it was rewritten to enumerate the directory, a list of schema filenames — cannot see a new subject. It protects what exists from regressing; it does not notice an addition. Assume nothing enforces a new thing until you have watched it fail.
- Add the
#[arg]field toCliincli.rs, with ahelp_headingmatching its group. →cli_help_testchecks the grouping of the ~140-flag help output. - Wire it wherever it takes effect — usually
plan(), so the flag becomes part ofRunPlanrather than being read at the point of use. - Give it a config fallback in
config.rsif its peers have one. →config_wiring_testcatches a config key that is never read. - Add a test that references the flag by name — any test, anywhere under
tests/, including a.trycmdgolden. →flag_coverage_testfails immediately without one:zzz-gate-probein its message is the flag you just added. - Document it in at least two examples across the docs.
→
doc_example_coverage_testfails with--your-flag: 0 example(s)until you do. - Add a default-value case to
cli_defaults_test. (unenforced — that test does not enumerate the CLI, so a missing case passes silently.)
Verified: adding a throwaway #[arg(long = "zzz-gate-probe")] failed
flag_coverage_test and doc_example_coverage_test, while docs_drift_test,
cli_defaults_test and config_wiring_test all stayed green. docs_drift_test
guards the other direction — a flag named in the docs that the CLI does not
have.
The two gates that do fire, in the order you meet them:
sequenceDiagram
autonumber
participant Dev as you
participant Cli as src/cli.rs
participant FC as flag_coverage_test
participant DE as doc_example_coverage_test
participant CI
Dev->>Cli: add #[arg] field
Dev->>FC: cargo test
FC-->>Dev: FAIL — flag referenced by no test
Dev->>Dev: add a test or a .trycmd golden
Dev->>DE: cargo test
DE-->>Dev: FAIL — 0 example(s) in the docs
Dev->>Dev: document it twice
Dev->>CI: push
Note over CI: both gates are ratchets — the exemption lists may only shrink
Model it on the newest one, StreamLossMap, whose commit touched exactly these
places.
- Add the variant to
Viewinstate.rs. - Add the renderer — a module beside
loss_map.rs— and register it inrender/mod.rs. - Keep the computation out of the renderer.
StreamLossMap's binning core lives inrtp/loss_map.rswith its own unit tests, so the view logic is testable without a terminal. - Add the controller in
controllers/and route it incontrollers/mod.rs. - Add the key that opens it, in whatever view it opens from.
- Document that key in
HELP_TEXTinhelp.rsand indocs/keybindings.md. →keybinding_drift_testfails withthese handled KeyCode::Char keys are neither in the F1 help nor allow-listedand names both the view and the character. - Add snapshot coverage in
tui_snapshot_test— at minimum the populated case and the empty/degenerate case. - Mirror the keybinding into
website/content/docs/keybindings.md. (unenforced for a new key — the mirroring obligation is a convention; see CONTRIBUTING.)
Verified: binding KeyCode::Char('Y') in the call-list controller without
touching the help failed two assertions in keybinding_drift_test.
- Add the handler to
server.rsunder the#[tool_router]impl, with a#[tool(name = …, description = …)]attribute. - Write the description as a statement of what the tool returns. Never
instruct the model to "trust", "verify", "act on" or "ensure" anything about
the content — that is the prompt-injection rule (D22 in
../design/implementation-plan-phases-8-10.md). (unenforced — the module comment inserver.rscitesscripts/check-tool-descriptions.sh, which does not exist; the lint is still an open backlog item.) - Take the store guard, project what you need into owned data, drop the
guard, and only then
.await. - Bound the response with
resolve_limit()and theMAX_BODY_BYTEScap. - Add an end-to-end case to
mcp_stdio_testand, for HTTP-visible tools,mcp_http_test.
Step 3 is not a style preference. The rmcp router requires each handler's
future to be Send, and a parking_lot guard held across an await makes it
non-Send — so the build fails on the #[tool] attribute with "future cannot
be sent between threads safely" before clippy::await_holding_lock (denied at
the workspace level) is even consulted. Two independent mechanisms, and the
stricter one is the compiler.
sequenceDiagram
autonumber
participant Client as MCP client
participant Tool as tool handler
participant DS as DialogStore
participant Shape as shape.rs
Client->>Tool: call_tool
Tool->>DS: read()
DS-->>Tool: guard
Tool->>Tool: project into owned summaries
Tool->>DS: drop(guard)
Note over Tool: the guard is gone — the future is Send again
Tool->>Shape: resolve_limit, MAX_BODY_BYTES
Shape-->>Tool: bounded payload
Tool-->>Client: CallToolResult
- Add the module under
src/security/besidescanner_detect.rsorreg_flood.rsand export it fromsecurity/mod.rs. - Keep detector state bounded and attacker-keyed maps capped — a detector is
by definition fed hostile input.
(unenforced —
resource_bounds_testis three hand-written scenarios overDialogStoreandRtpStream. It has no way to discover your module.) - Emit through the alert engine in
alerting.rsrather than printing, so every sink (CLI, TUI,fail2ban, MCPsecurity_findings) gets it. - Add regression coverage to
security_test, including the false-positive case — a detector that fires on normal traffic is worse than no detector. (unenforced — nothing requires a new detector to have a test.)
Verified: a new src/security/ module holding an uncapped HashMap<IpAddr, u64>,
exported from mod.rs, left resource_bounds_test at 3/3 and security_test
at 38/38. Both steps above are real obligations with no gate behind them —
which is exactly why they are written down.
5. If it can transmit anything, it must be opt-in and must respect the
HEP-origin restriction (SN-01): HEP-sourced packets are ineligible for
active response without --hep-allow-kill.
- Project through
output/model.rs. Do not build a bespoke JSON object for a dialog or a stream. (unenforced for a new surface —summary_consistency_testpins the three surfaces that already exist, CLI/API/MCP, againstDialogSummary/StreamSummary. A fourth writer is invisible to it.) - Add the writer beside
json.rs/cli_print.rsand emit throughsink.rs. - If it is machine-readable, add or extend a schema in
tests/schemas/. No registration step: theall_schemas_compilecase injson_schema_testenumerates the directory, so a new file is compiled into a validator the moment it lands and a malformed one fails there. What is still on you is live-output validation — proving the surface actually emits what the schema describes. - Add behavior coverage to
output_behavior_test. (unenforced — it is two tests about--json-prettyand--call-reportexit codes.)
History, and the reason step 3 now reads that way: a deliberately malformed
zzz_gate_probe.schema.json dropped into tests/schemas/ once left
json_schema_test at 6 passed, 0 failed — the test walked a hardcoded list of
four filenames and never opened the file. That experiment is what motivated
the switch to directory enumeration, and the test's own doc comment records
it. Steps 1, 2 and 4 are still convention with no gate behind them.
5. Document it in docs/output-formats.md, and mirror
into the website page. The flag that selects it then owes the CLI-flag
checklist above.
- Add
fuzz/fuzz_targets/<name>.rscalling one parser entry point on&[u8]. - Register the
[[bin]]infuzz/Cargo.toml. (unenforced, and this is the trap: an unregistered file underfuzz_targets/is not a broken build, it is an invisible one. Cargo never compiles it, sofuzz-checkand the pre-pushcd fuzz && cargo checkboth pass and your target silently never runs.) - Seed
fuzz/corpus/with at least one real input. (unenforced — despite the name,fuzz_corpus_replaynever opensfuzz/corpus/. It has no filesystem access at all: it drives the parsers with an adversarial seed set and a mutation sweep defined in the file itself.) - Add the same entry point to
smoke_fuzz_test. Coverage-guided fuzzing needs nightly and runs weekly; the smoke floor runs in everycargo test, and it is what actually catches the regression. - When a target finds a crash, commit the reproducer into the corpus in the
same change as the fix — and, because step 3's replay does not read the
corpus, add the input to
smoke_fuzz_testtoo if you want it replayed on everycargo test.
Verified: an unregistered fuzz/fuzz_targets/zzz_gate_probe.rs containing a
hard compile error left cd fuzz && cargo check at exit 0. Registering the
same file made the same error fail the build with exit 101. The gate only ever
sees registered targets.
Website · Repository · Issues · Generated from docs/ — edit there, not here.
Getting started
Using the TUI
CLI & automation
Configuration
Integrations (API & MCP)
Development & internals