whiskers.git / CLAUDE.md

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

Notes for agents

  • The user of this product is a young child. Every conversation is logged and visible to the parents; that is a requirement, not an option to trade away. Nothing reaches the child without passing the guard. A failure of the model, the guardrail or the network ends in a safe spoken fallback, never in silence or raw error text.
  • Never log content. Nothing the child or Whiskers said, no memory facts, no summaries, and not the child's name. Log lengths, counts, ids, durations and whether a profile is set.
  • The child is a profile, never a literal. Name and age live in HouseholdConfig::child (whiskers-core/src/profile.rs) and reach prompts and the guard only through Audience. Do not write a name, an age or a pronoun for the child into code, tests, fixtures, docs or comments; test fixtures use Ada, age 6. With no profile the guard assumes the youngest supported age (Age::YOUNGEST): a missing profile must never be the loose case.
  • Nothing identifying goes in tracked files. No family details, hostnames, IPs, device serials, user names, personal paths or secret names: they are build properties and environment variables. tools/check-private.sh enforces a deny-list kept in the gitignored .private-terms; run it before committing. It also checks commit messages (--message) and every unpushed commit (--push): the repository is served in full through the code host, so a term in an old commit or a message is as public as one in a file; the hk hooks run both. Never run a push without it, and do not pipe its output (a pipe hides the exit status). Operator-private notes belong outside this repository.
  • No non-native user interface. Rust core, Kotlin shell with Jetpack Compose. Logic belongs in the core; the shell draws and listens.
  • Jev decides whether a message is allowed; a model writes the parents' summary. Shared Jev client code lives in third-party/jevcrates (a submodule); do not write a second client here.
  • Measure on the device before designing around it. Speech recognition, the child profile and the Fire OS version differ per device. Do not assume a Google service exists on Fire OS.
  • Pushing is the owner's call, per push.
  • mise.toml is the one place for tool versions, environment variables and tasks. Nix is an optional wrapper (flake.nix provides mise and native libraries and packages the service); it holds no version. Anything that must repeat a version (a Cargo pin, the Gradle SDK levels, hk.pkl) is checked by tools/check-versions.sh. A README tells people to run mise run <task>, never cargo, gradle, adb or nix directly: add the task.
  • Nobody is required to have nix. Do not write a script, task or doc that needs it, and do not make nix the first command in a README.
  • No banner comments. A comment is read as markdown on the code host, so a ruled or dashed heading is noise there. Write // ## Title (or # ## Title) for a heading, or nothing. tools/check-banners.sh fails on the lines a commit adds (an hk pre-commit step); mise run check-banners lists every one in the tree.