Two layers. The protocol is device-independent and meant for adoption by others. Goldfish is one profile of it, plus the bindings to its hardware.
┌──────────────────────────── protocol/ (Resolution Protocol) ────────────────────────────┐
speech ──────► │ grammar: locale JSON → intents ──► session: voices · records · levels ──► retention │
(any port) │ (en, es, …) (rules §6, events §11) (schedule §5) │
│ reference: python/resolution_protocol · js/resolution.js · vectors/ (conformance) │
└───────────────▲──────────────────────────────▲─────────────────────────────▲──────────┘
│ profile (parameters + locales)│ events / applied levels │ max_level, unreadable_after
┌───────────────┴──────────────── v01/ (Goldfish profile + bindings) ─────────┴──────────┐
│ profile/goldfish.profile.json — default 3, invocation "goldfish", en+es, 3 d/30 d/1 y │
│ firmware/goldfish/device.py — loads the profile and grammars, owns the session │
│ display.py ring frames (indicator) keys.py + store.py key chains (store, §5.3) │
│ handoff.py + st25dv.py NFC note (hand-off) RTC (clock) mics/ASR (audio in) │
│ firmware/js/goldfish.js — the same bindings for the workbench site │
└──────────────────────────────────────────────────────────────────────────────────────────┘
│
site/ (workbench) — runs protocol/js + goldfish.js in the browser; Rules tab runs the vectors
| Concern | Protocol (shared) | Goldfish (profile + bindings) |
|---|---|---|
| Levels, rules, voices, records | protocol/SPEC.md §3–6, session.py / resolution.js |
— |
| Phrases → intents | protocol/locales/*.json |
invocation: ["goldfish"], locales: ["en", "es"] |
| Parameters | schema profile.schema.json |
v01/profile/goldfish.profile.json |
| Retention schedule | retention.py (readable_level, unreadable_after) |
device.key_curve() → key chains in keys.py |
| Enforcement | §5.3 (requirement) | keys.py + store.py (ATECC608 chains; per-voice keys: backlog B-26) |
| Indicator | §10 (requirement) | display.py / goldfish.js ring, who-is-speaking and the room-off glow; mic-rail eye light (hardware) |
| UI intents | §7.1 (source: "ui") |
device.press_eye / goldfish.js pressEye: the eye button lowers the room or resumes it; a hold arms the hand-off |
| Hand-off | §10.3 (binding) | handoff.py g1 notes over NFC |
| Conformance | vectors/, conformance.py, conformance.js |
v01/firmware/tests/test_device.py + test_scenario.py |
protocol/ may import from v01/ or mention Goldfish hardware.protocol/vectors/, then both reference implementations change together; v01/validate.py runs both.Decisions: ADR-0010, ADR-0011, ADR-0012, ADR-0013 (the eye button: device.press_eye, display.ring_frame(room_off=True)).
Parity: v01/validate.py runs both reference implementations against the vectors and a differential fuzz (protocol/tools/fuzz_parity.py). Light-ring parity between display.py and goldfish.js is checked the same way.
# Code architecture
Two layers. The **protocol** is device-independent and meant for adoption by others. **Goldfish** is one profile of it, plus the bindings to its hardware.
```
┌──────────────────────────── protocol/ (Resolution Protocol) ────────────────────────────┐
speech ──────► │ grammar: locale JSON → intents ──► session: voices · records · levels ──► retention │
(any port) │ (en, es, …) (rules §6, events §11) (schedule §5) │
│ reference: python/resolution_protocol · js/resolution.js · vectors/ (conformance) │
└───────────────▲──────────────────────────────▲─────────────────────────────▲──────────┘
│ profile (parameters + locales)│ events / applied levels │ max_level, unreadable_after
┌───────────────┴──────────────── v01/ (Goldfish profile + bindings) ─────────┴──────────┐
│ profile/goldfish.profile.json — default 3, invocation "goldfish", en+es, 3 d/30 d/1 y │
│ firmware/goldfish/device.py — loads the profile and grammars, owns the session │
│ display.py ring frames (indicator) keys.py + store.py key chains (store, §5.3) │
│ handoff.py + st25dv.py NFC note (hand-off) RTC (clock) mics/ASR (audio in) │
│ firmware/js/goldfish.js — the same bindings for the workbench site │
└──────────────────────────────────────────────────────────────────────────────────────────┘
│
site/ (workbench) — runs protocol/js + goldfish.js in the browser; Rules tab runs the vectors
```
## Where things live
| Concern | Protocol (shared) | Goldfish (profile + bindings) |
|---|---|---|
| Levels, rules, voices, records | `protocol/SPEC.md` §3–6, `session.py` / `resolution.js` | — |
| Phrases → intents | `protocol/locales/*.json` | `invocation: ["goldfish"]`, `locales: ["en", "es"]` |
| Parameters | schema `profile.schema.json` | `v01/profile/goldfish.profile.json` |
| Retention schedule | `retention.py` (`readable_level`, `unreadable_after`) | `device.key_curve()` → key chains in `keys.py` |
| Enforcement | §5.3 (requirement) | `keys.py` + `store.py` (ATECC608 chains; per-voice keys: backlog B-26) |
| Indicator | §10 (requirement) | `display.py` / `goldfish.js` ring, who-is-speaking and the room-off glow; mic-rail eye light (hardware) |
| UI intents | §7.1 (`source: "ui"`) | `device.press_eye` / `goldfish.js pressEye`: the eye button lowers the room or resumes it; a hold arms the hand-off |
| Hand-off | §10.3 (binding) | `handoff.py` g1 notes over NFC |
| Conformance | `vectors/`, `conformance.py`, `conformance.js` | `v01/firmware/tests/test_device.py` + `test_scenario.py` |
## Rules of the split
1. Nothing in `protocol/` may import from `v01/` or mention Goldfish hardware.
2. Goldfish never re-implements a rule. It configures one (profile) or binds a port (display, store, hand-off).
3. A behaviour change to the rules starts as a vector in `protocol/vectors/`, then both reference implementations change together; `v01/validate.py` runs both.
4. Languages are data: a new locale is a JSON grammar plus its phrase vectors.
Decisions: [ADR-0010](decisions/0010-each-voice-owns-its-record.md), [ADR-0011](decisions/0011-protocol-and-profile.md), [ADR-0012](decisions/0012-rp-0-2.md), [ADR-0013](decisions/0013-eye-button-and-kit-build.md) (the eye button: `device.press_eye`, `display.ring_frame(room_off=True)`).
**Parity:** `v01/validate.py` runs both reference implementations against the vectors and a differential fuzz (`protocol/tools/fuzz_parity.py`). Light-ring parity between `display.py` and `goldfish.js` is checked the same way.