For agents, on top of README.md, which they read first.

Notes for agents

  • No hosting words in here. Never name Cloudflare, celld, wrangler, Durable Objects, R2/buckets or fetch/Request/Response types in this crate, in code or docs, except to say that an adapter is not this crate. The port is Whiskers' own; the adapter adapts to it, not the other way round. If a port seems to need a host's concept, the port is mis-shaped: change the port's language, not its dependencies.
  • Must build for wasm32-unknown-unknown. No std::fs, std::net, std::thread, SystemTime or Instant. Check with cargo check --target wasm32-unknown-unknown -p whiskers-ports inside nix develop ~/whiskers. Dependencies stay at serde, serde_json, log and whiskers-core.
  • serde_json has preserve_order on purpose. Stored journal lines keep a device's key order, and every JSON reply of the service is laid out the same on every backend. Do not remove the feature.
  • A capability is a trait here, its laws in whiskers-conformance and its routes in whiskers-service. Adding one touches all three (the README's "Capabilities" section lists the steps). Never write a stand-in port to satisfy a bound, never add a runtime "not configured" branch to a route for a capability the adapter may lack, and never put a new capability only in Backend's supertraits without a Has… trait of its own: an adapter must be able to hold it alone.
  • A contract is a law in whiskers-conformance. Changing a contract means changing its law in the same commit; adding a port means adding its laws and running them against the in-memory adapter and the native one. A law no adapter can fail proves nothing: keep the "caught" tests in that crate.
  • Make the wrong thing unrepresentable. Anything that arrives from outside gets a validated newtype with a private field (DeviceName, SafePictureId, SpeechLine...). Errors are enums that say why; a string goes in a Diagnostic and is never branched on. Add a variant rather than parse a message.
  • Rules live in whiskers-core or here, not in adapters. The merge rules, the window arithmetic (ThinkingLedger), the day arithmetic (VoiceDay) and the page rule (Page::of) are shared on purpose: an adapter decides where state is kept and how access is serialised, and adds nothing to the arithmetic.
  • Logging follows crates/CLAUDE.md: levelled lines, never content.
  • whiskers-core also has a ports module (Model, Guard, Log...): those are what a device's conversation pipeline runs through. These are the backend's. They are different layers.