For agents, on top of README.md, which they read first.
Notes for agents
- The wire is a contract with the app and the tablet. Do not change a path, a JSON field, a status or a
status text while moving logic.
crates/whiskersd/testsandtools/preflight.shrun it end to end; the tests here pin it byte for byte (tests/service.rs). Key order matters:serde_jsonhaspreserve_orderon purpose. - Policy lives here; state and calls live in adapters. If you are about to write a rule (a cap, an order of
checks, what a refusal says) in
whiskersd, it belongs here. If you are about to name a host, a file or a socket in this crate, it belongs in an adapter. - Everything must build for wasm: no
std::fs,std::net,std::thread,Instant,SystemTime. Time comes fromHasClock::clock(). Check withcargo check --target wasm32-unknown-unknown -p whiskers-service. - The order of checks in
/speakis part of the wire (bad body, empty, not configured, too long, the day's cap), for both speak routes. The comment there says why; keep it. - Never log content: no message text, facts, summaries, keys or picture bytes. Lengths, counts, ids of
pictures, statuses and durations (computed from the clock port) are fine. See
crates/CLAUDE.md. - A new route gets a test in
tests/service.rswith the exact bytes it replies, and its path goes in that file'sEVERY_ROUTElist (a test fails ifService::completeforgets a route). - A route's capabilities are its
Route<C>impl's bounds, and nothing else.impl<C: HasJudge + HasClock> Route<C> for Checkis the entire statement of what/checkneeds. Never add a runtime "not configured" branch for a capability the adapter might not have, and never write a stand-in port to satisfy a bound: the route is simply not given to that adapter, and the compiler refuses to give it. A route's handler asks the adapter for exactly the capabilities in its bounds and no more (the compiler enforces that too). - Adding a route: its capability trait (if new) is in
whiskers-ports, its laws inwhiskers-conformance; here you add the route type (a unit struct, sealed), itsRouteimpl, a line inService::completeand in theCompletealias.