Internals Walkthroughs - NormB/sipnab GitHub Wiki
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 carries (unenforced) โ that is prose, and prose is what gets forgotten, so those are the steps to be deliberate about.
Checking the enforcement claims here meant 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, the compiler turns away an MCP tool that holds a
lock across an await 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 sat beside. Those steps now carry (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 ~160-flag help output. - Wire it wherever it takes effect โ usually
plan(), so the flag becomes part ofRunPlanrather than 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). (Enforced bytests/mcp_tool_descriptions_test.rs, which fails on any of those four words in a tool description. Corrected 2026-08-05: this used to call the rule unenforced and to claimserver.rscited ascripts/check-tool-descriptions.shthat does not exist. Both halves are now wrong โserver.rsno longer carries the citation, and the rule shipped as a Rust test rather than the shell script the plan named. That test also carriesevery_cited_script_exists, which fails if the dangling citation ever comes back. It now scans every tracked.rsfile whole, not the first 40 lines of one, because the narrow version asserted nothing once the citation went away.) -
Take the store guard, project what you need into owned data, drop the guard, and only then
.await. -
Bound the response with
resolve_limit_with_cap()โ passing the caller'slimitand the server'srow_capโ and the body-bytes cap (--mcp-max-body-bytesmoves it).Not
resolve_limit(). That one takes no ceiling, so a tool wired to it silently ignores--mcp-max-rowsโ the operator sets a limit and the tool does not honor it.shape.rssays as much at the definition: it is "kept for callers with no configured ceiling", and the server is not one of them. This step used to name the wrong function, which is how a recipe written for the reader who does not yet know manufactures the very defect it exists to prevent. -
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_with_cap, body cap
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.) - Run its check from
run_detectorsinsecurity/detectors.rs, which returns the finding as anEffectfor the batch loop to file, and emit through the alert engine inalerting.rsrather than printing, so every sink (CLI, TUI,fail2ban, MCPsecurity_findings) gets it. A new rule name also belongs inSECURITY_FINDING_KINDS(mcp/server.rs) and inDetectionEngines::armed_kinds(batch.rs): the first is the vocabularysecurity_findings.kindsaccepts, so a finding filed under a name missing from it is one no caller can filter for, and the second is what tells an agent the run armed the detector at all. (enforced โsecurity_findings_kinds_match_the_names_the_detectors_file_underreads thekind:literals inbatch.rsand fails on the difference.) - 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 this page writes them 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 becomes 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.