Build log

The record of how Goldfish went from a memo to a buildable kit, a public workbench and a draft protocol, over one long working session on 2026-10-12 and 2026-10-13. Times are Berlin local time (UTC+2). Rafa's requests are quoted verbatim. Sources for this log: the session transcript (including its compaction summary), the git history and the files in this repo.

Overview

Goldfish is a voice recorder with no Wi-Fi and no radio of its own. It sits on the table during an in-person session, for up to 10 people, and lets anyone lower what it keeps by saying so in plain English, or by pressing its eye.

Start here in a new session:

  1. Read this log's last two phases and its open items.
  2. Read BACKLOG.md, and the three highest-numbered ADRs in 03-architecture/decisions/.
  3. Run ~/.venvs/goldfish-cad/bin/python v01/validate.py; every check should pass (the last line reads N/N passed).

Phase log

1. Sources and scaffold (06:58–07:08)

2. Concept and simulator rework (07:15–07:32)

3. v01 kit design: the pill (07:22–08:00)

4. First overview site (08:00–08:06)

5. Pill to pebble, validation rounds, spec English (08:23–09:56)

6. v01c: solid, drop-tolerant pebble (10:00–11:51)

7. Phone simulator and here.now deploy (10:00–14:19)

8. Per-participant resolution (10:07–10:32)

9. Workbench site and merge (16:05–16:24)

10. Meeting simulation (16:09–16:22)

11. Light redesigns: eye ring, flank windows, continuous ring (16:25–18:01)

12. Geometric review and electrical simulation (17:19–18:01)

13. Repository (18:31–18:32)

14. Sourcing and print files (18:37–18:51)

15. Light mode, market comparison, roadmap and backlog (19:05–19:10)

16. The Resolution Protocol (19:52–20:15)

17. Protocol review round 1 and the "back on the record" bug (20:10–20:18)

18. RP 0.2 and protocol review round 2 (to 20:47)

19. Builder simulations, the eye as a button, a kit with no SMD (to 23:09)

20. A bigger cell, continuous on a phone charger, Markdown notes (2026-10-13, to 09:22)

21. Five usage and build simulations (2026-10-13, after 09:22)

22. The usability and build round (2026-10-13)

23. Wiring in the 3D model (2026-10-13)

24. Flared post roots (2026-10-13)

25. Faster iteration, print practice from the community, fasteners in the model (2026-10-13)

26. Firmware hardware layer, and every board screwed down (2026-10-13 to 10-14)

27. The site's meeting runs the real firmware; a flow test replays it (2026-10-14)

28. The buildability walk (2026-10-14)

29. A chassis and a lid (2026-10-14)

30. Wiring at pad level (2026-10-14)

31. A landing page (2026-10-15)

Decision records

ADR Title Status Summary
0001 No wireless radio in v1 Accepted, amended by 0007 No Wi-Fi, Bluetooth or active radio; certification as an unintentional radiator.
0002 Retention by destroying keys Superseded in part by 0005 Encrypted storage, expiry by key destruction in a secure element, fail closed on clock doubt.
0003 Voice in plain English Accepted Lowering by ordinary phrases, instant, retroactive; raising by wake word plus objection window.
0004 Physical access only Accepted; read path per 0007 Memory read only by handling the device; no remote access.
0005 Forgetting by tiers Proposed Distil every lower level at sealing; one key per (day, level); decay is key destruction. Implemented with forward-secure chains.
0006 Light, never sound Superseded in part by 0008, 0009 Mic-rail light that software can't override, plus a firmware-driven level indicator.
0007 Passive NFC hand-off Accepted for v01 ST25DV tag, RF off unless the read button is held (10 s max); note in the URL fragment.
0008 Per-participant resolution Accepted Each voice sets its own level; room lowering covers all; nobody raises anyone else; uncertainty falls toward less.
0009 Light ring at the seam, one light in the eye Accepted, amended 41-LED ring split per participant, level by colour and fill; single mic-rail eye light; lit tap icon; power switch.
0010 Each voice owns its record Accepted Pseudonymous session identity per voice; its own record; lower, raise, forget, shorten, erase; opt-in claim.
0011 Protocol and profile Accepted Rules live in the Resolution Protocol; Goldfish is one profile plus hardware bindings.
0012 Resolution Protocol 0.2 Accepted Provisional raises with a 30 s objection window, a smaller core, adoption hooks; fixes from three reviews of RP 0.1.
0013 The eye is a button; a kit with no SMD Accepted, gestures revised by 0015 Press for off the record; room-off glow; through-hole and plug-in parts only.
0014 Bigger cell, continuous on a charger Accepted 4000 mAh, bq24074 power path, 154 × 78 × 33 mm, heat path, 50-LED ring.
0015 Usability and build round Accepted Safe presses, buttonless phone transfer, seat-facing arcs, buildable shell fixes from five simulations.
0016 Flared post roots Accepted, sizes revised by 0017 R 2 mm fillet into a 45° flare where each board post meets the roof; the packer keeps parts out of it.
0017 Print practice and fasteners Accepted Insert bores to the datasheet (Ø4.0, Ø7.2 posts), inserts and screw heads in the model, 245 °C inserts, 0.36 mm icon skin, lip lead-in, flexible glue for TPU, a test coupon.
0018 Every board screwed down Accepted Captive-nut bosses and one M2.5 × 6 screw everywhere; straps for boards with no holes; a screwed battery deck; DC jack off; case 158 × 79 × 37 mm.
0019 Firmware in the site Accepted The Meeting tab runs the real firmware in Pyodide; one meeting script shared with a flow test that checks the SPI bytes, motor pin and tag; the JS copy stays as the preview.
0020 A chassis and a lid Accepted Every board in the bottom shell, the Radxa on deck bosses; the lid joins through one latched 12-way plug; case 182 × 79 × 37 mm.
0021 Wiring at pad level Accepted, amended Every wire from a named pad to a named pad, from the vendors' board files; every wire, leg and joint inside the case; switches turned pins-in, case 182 × 85 × 37 mm; open lid routed; 30 mm working slack.
0022 A landing page Accepted The site root is a landing page for facilitators; the workbench moves to /workbench/, the reader stays at /r
0023 Mods Accepted Add-on packs (languages, domains, facilitation skills) installed over USB-C; a mod can only lower; the note names active mods; no emotion inference

How to rebuild from scratch

All paths are relative to the repo root. Each script below exists in the repo as of this log, except the here.now publisher, which lives in the local here.now skill.

  1. CAD environment (Python 3.12, outside Documents):
    uv venv --python 3.12 ~/.venvs/goldfish-cad
    uv pip install --python ~/.venvs/goldfish-cad/bin/python build123d trimesh numpy pytest
    
    The wall-thickness check also needs rtree.
  2. Fit check (pebble packer; search finds the smallest pebble that packs):
    cd v01/cad && ~/.venvs/goldfish-cad/bin/python pebble_layout.py
    
  3. Enclosure (reads v01/cad/pebble_spec.json; writes STEP, STL and layout.json to v01/cad/out_pebble/; several minutes):
    ~/.venvs/goldfish-cad/bin/python pebble_enclosure.py
    
  4. Web meshes and print data (decimated meshes in out_pebble/web/, print.json, PRINT-SPEC.md):
    ~/.venvs/goldfish-cad/bin/python make_web.py
    
  5. Wire routes (about 10 minutes; writes out_pebble/wires.json and the cut list in v01/wiring.md; rerun after any layout change):
    ~/.venvs/goldfish-cad/bin/python wire_routes.py
    

5b. Geometric review (writes out_pebble/review.json):

cd ../.. && ~/.venvs/goldfish-cad/bin/python v01/cad/review_geometry.py
  1. Electrical simulation (writes v01/sim/power_report.json):
    python3 v01/sim/power_sim.py
    
  2. Protocol conformance (optional on its own; step 8 runs it):
    (cd protocol/python && python3 -m resolution_protocol.conformance)
    node protocol/js/conformance.js
    
  3. Full validation (unit and scenario tests, protocol conformance, fit, meshes, walls, the geometric review (wiring included), electrical simulation, light-ring and eye-gesture parity between Python and JS, the firmware flow (the site's meeting replayed through the daemon), site scripts parse, CDN versions pinned, docs agree with the generated data; writes v01/validation.json, which the site shows):
    ~/.venvs/goldfish-cad/bin/python v01/validate.py
    
  4. Site (local single file with embedded assets, then the deployable folder site/web/):
    python3 site/build.py
    python3 site/build.py --web
    
  5. Deploy to npc.here.now/demo-goldfish (the slug is already linked to that path; credentials stay in ~/.herenow/ and never enter the repo):
    ~/.claude/skills/here-now/scripts/publish.sh site/web --slug astral-aura-h93b --client claude-code
    

Older artefacts: the v01a pill rebuilds with v01/cad/layout.py, v01/cad/enclosure.py and v01/cad/make_viewer.py (into v01/cad/out/ and v01/viewer.html); the earlier long-form page with python3 site/build_v1.py. The concept simulator (07-simulator/index.html) and the reader page (v01/reader/index.html) need no build.

Open items

Every open item, with priority and definition of done, is in BACKLOG.md. The main ones as of phase 22:

# Build log

The record of how Goldfish went from a memo to a buildable kit, a public workbench and a draft protocol, over one long working session on 2026-10-12 and 2026-10-13. Times are Berlin local time (UTC+2). Rafa's requests are quoted verbatim. Sources for this log: the session transcript (including its compaction summary), the git history and the files in this repo.

## Overview

Goldfish is a voice recorder with no Wi-Fi and no radio of its own. It sits on the table during an in-person session, for up to 10 people, and lets anyone lower what it keeps by saying so in plain English, or by pressing its eye.
- **The pitch:** a better session, not better notes. People listen fully and speak freely because they can see what is kept. The landing page cycles "Have / Make / Work / Ask / Find better …"; the hardware is the proof, lower down. First audience: facilitators.
- **Levels:** each voice sets the level for its own words.
- **Forgetting:** memory decays on a fixed curve: attributed for 3 days, gist to day 30, then outcomes. Forgetting is enforced by key destruction in a secure element.
- **Lights, never sound:** the device answers with light and a buzz. One eye light is wired to the microphone rail. A 50-LED ring at the seam puts each person's lights on the side facing them, and breathes red while the room is off the record.
- **Reading a note:** a phone reads the session's note by touching the tail, through a passive NFC tag. No button is needed. The note is Markdown inside the link.
- **The v01 kit:** a yellow pebble, 182 × 85 × 37 mm and about 266 g (`v01/cad/out_pebble/layout.json`), built from breakout boards and through-hole parts around a Radxa ZERO 3E plus eleven printed parts. It runs about 3 h on a 4000 mAh cell, and indefinitely on a USB-C phone charger.
- **The site:** the landing page is at [npc.here.now/demo-goldfish](https://npc.here.now/demo-goldfish/) (ADR-0022); the workbench at [/workbench/](https://npc.here.now/demo-goldfish/workbench/) carries the specification, a live 3D model, a simulated five-person roadmap session, a phone simulator, the parts list with purchase links, the print files and the build files.
- **The protocol:** the rules are a device-independent draft standard, the Resolution Protocol ([protocol/SPEC.md](protocol/SPEC.md)), with Goldfish as one profile of it.
- **Repo:** private at github.com/rafaeldavid/goldfish. Licences: code Apache-2.0, hardware CERN-OHL-P-2.0, docs CC BY 4.0.

**Start here in a new session:**
1. Read this log's last two phases and its open items.
2. Read `BACKLOG.md`, and the three highest-numbered ADRs in `03-architecture/decisions/`.
3. Run `~/.venvs/goldfish-cad/bin/python v01/validate.py`; every check should pass (the last line reads N/N passed).

## Phase log

### 1. Sources and scaffold (06:58–07:08)

- **Request:** "dig that up and help create a scaffold for a hardware product development process in the goldfish folder."
- **Done:**
  - Found the origin memo, "NPC Inc Memo: Fixing Our Meeting Memory Problem" (npcmemo.substack.com, 2025-12-24), and the companion post "Personal Memory Portability in the Digital Workplace", published 51 minutes later. Both copied verbatim into [00-sources/](00-sources/README.md) with provenance.
  - Scaffold of folders 00–10: brief, resolution ladder (R0 off the record, R1 outcomes, R2 gist, R3 attributed, R4 verbatim, R5 audio), open questions, requirements with P-, S- and N- IDs, system architecture with three options, hardware candidates, firmware states, legal notes, threat model, validation plan, roadmap.
  - First concept simulator, [07-simulator/index.html](07-simulator/index.html): Anatomy and Room tabs, checked headless at desktop and phone widths.
- **Found / fixed:**
  - Claude.ai chat history not reachable from Claude Code; no export on disk; Drive connector without search permission. Recovery still open (B-25).
  - No `/anatomy` skill on the machine, so an anatomy-style simulator was built from scratch.
  - Grid columns wider than the phone viewport; fixed.
- **Decisions:** ADR-0001 (no radio), ADR-0002 (retention by per-session key destruction), first drafts of ADR-0003 and ADR-0004.

### 2. Concept and simulator rework (07:15–07:32)

- **Request:** "people would talk to the device to express their preferences in normal english, but the device only listens, doesn't speak" … "it 'forgets' (lowers resolution of historical info, so remembers 'gist' for up to 30 days, then only outcomes".
- **Done:**
  - Docs rewritten around a continuous 72-hour stream and a forgetting curve: R3 for 3 days, R2 to day 30, then R1.
  - Asymmetry rule: lowering needs no wake word and applies at once; raising needs "Goldfish, …" plus a 30 s objection window.
  - Simulator rebuilt with Anatomy, Stream (a 72-hour conference) and Forgetting (time slider) tabs.
  - Rafa's direction added verbatim to the sources file.
  - First Python firmware core: agreement engine, forgetting curve, phrase parser. 8 tests.
- **Found / fixed:** closing-drinks event landed on Thursday 06:00 with an "undefined" label; moved and the day names extended.
- **Decisions:** ADR-0003 (voice in plain English) and ADR-0004 (physical access only) accepted; ADR-0005 (forgetting by tiers, proposed); ADR-0006 (light language).

### 3. v01 kit design: the pill (07:22–08:00)

- **Request:** "design a first version of the product, with the brand being centered on the goldfish? (so should be yellow, and maybe a soft pill type of object" … "ideally orderable pieces as a kit I can put together." Then: "the most basic wireless way to transfer to a phone while keeping the device basically offline".
- **Done:**
  - Two background research tracks: on-device speech stack ([v01/research/software-stack.md](v01/research/software-stack.md)) and radio-free orderable parts.
  - Stack chosen: Silero VAD, sherpa-onnx keyword spotter and streaming Zipformer ASR, Qwen2.5-0.5B via llama.cpp for distillation, diarisation held in RAM only.
  - Key design changed from about 90 keys to forward-secure hash chains in the ATECC608 (`keys.py`, 12 chains at most). 11 tests.
  - Board: Radxa ZERO 3E (RK3566, no radio). Measured from Radxa's own STEP by a mesh heightmap, not from quoted heights. Charger positions read from Adafruit's PCB files.
  - CAD in build123d (open source) in a venv at `~/.venvs/goldfish-cad`, kept out of Documents (619 MB).
  - Fit search: the smallest pill that packs every part is 124 × 45 × 22 mm (107 cm³). A 1200 mAh variant is no thinner.
  - Five printed parts (top and bottom shells, eye socket, smoke lens, read button), STEP and STL, watertight.
  - Interactive viewer ([v01/viewer.html](v01/viewer.html)), renders, `bom.csv` (about $170–200), `wiring.md`, `assembly.md`, `geometry.md`.
  - Phone hand-off: passive ST25DV64KC tag in the tail; note in the URL fragment as `g1.<base64url(deflate)>`; static reader page [v01/reader/index.html](v01/reader/index.html). 14 firmware tests.
- **Found / fixed:**
  - AirDrop impossible without Wi-Fi; passive NFC chosen instead.
  - Board mounting posts missing from the collision check; added, mics turned 90°.
  - Bottom shell exported as two bodies (tongue floating 0.15 mm); tongue fused below the split line.
  - `layout.py` defaults still at an old size; v01 dimensions made the default.
  - Radxa ZERO 3E sold out at Arace; preferred 2000 mAh cell out of stock. Still open (B-01, B-02).
  - heypcb.ai catalogue requires an account; design drew on open hardware files and vendor pages instead.
- **Decisions:** ADR-0007 (passive NFC hand-off, amends ADR-0001 and ADR-0004).

### 4. First overview site (08:00–08:06)

- **Request:** "make sure to include all the visuals to explore the hardware and software architecture as well as bill of materials for prototype and production considerations" and "Deploy as HTML local file".
- **Done:** `site/index.html`, built by `site/build.py`: 3D hero, memo quotes, ladder, forgetting chart, plan and section drawings from fit-check data, hardware and software diagrams, filterable BOM, production plan, open questions. Opened locally in Chrome; nothing published.
- **Follow-up question answered:** "using a small local model that does reording processing at resolution level?" Yes: a rule engine stamps each 5-minute block with its level; at sealing (about 75 minutes later) the model writes R3, R2 and R1 copies down from the stamp, each under its own key. Off-the-record blocks never reach the model.

### 5. Pill to pebble, validation rounds, spec English (08:23–09:56)

- **Request:** "do a few more rounds to validate the functionality and make sure the site is on-brand (light-goldfish) and maybe the design becomes a pebble moreso than a pill".
- **Done:**
  - Pebble geometry as a loft of superellipse sections ([v01/cad/pebble.py](v01/cad/pebble.py)) with an analytic `inside()` test and a greedy packer ([v01/cad/pebble_layout.py](v01/cad/pebble_layout.py)).
  - v01b fit: 130 × 50 × 25 mm, 125 cm³ (pill kept as v01a for comparison).
  - Business research ([v01/research/market.md](v01/research/market.md)) and real vendor 3D models for seven boards (licences in `v01/cad/vendor/models/manifest.json`).
  - New site with a nine-stop guided 3D tour, business rationale and positioning.
  - [v01/validate.py](v01/validate.py) created: 6 checks, then 7 with a site-script parse check. Python and JS parsers agree on 15 shared phrases. 20 firmware tests, including an end-to-end three-day conference.
- **Found / fixed:**
  - Packing failures: eye hung from the lowest roof point across its square (now per LED); battery and board placed greedily (now as one stack); clock board with no home (roof fallback); boost placed after the clock (order swapped).
  - Walls thinned to 0.87 mm at the tips; raised to 1.15 mm minimum, 1.82 mm median.
  - Real vendor models exposed three errors: the USB-C 4090 receptacle opens on its long edge (breakout rotated 90°, tail repacked); the SPH0645 mics are bottom-port (mounted flipped, holes moved to the real port) and 2.6 mm tall, not 1.8; the Radxa model carried a 3.66 mm offset.
  - A `//` comment swallowed an `else` branch and blanked the 3D scene; caught by headless render; the parse check now catches it automatically.
- **Range question:** "how far the recording can reach?" Estimates only: 3–4 m in a quiet room, 1.5–2 m in an office, 0.75–1 m in a café, about 0.5 m in a hall; roughly half in a pocket.
- **Copy request:** "fix all the english on the page so we don't have sentences that end in a verb … write it like a normal product management spec doc." Page rewritten as ten numbered sections. A scan of all 94 rendered sentences left one verb ending (the tagline), then fixed.

### 6. v01c: solid, drop-tolerant pebble (10:00–11:51)

- **Request:** "increase the resolution of the 3D model, allow user to hover and see the hardware components" … "more like apple hardware design vibes? Should feel 'solid' 'trustworthy' the eye being black seems a little off" … "it may fall off a table or get passed around".
- **Done:**
  - Design research ([v01/research/design.md](v01/research/design.md)): hidden-until-lit indicators, AirTag, HomePod mini, Rams, Teenage Engineering, drop practice.
  - [v01/cad/pebble_enclosure.py](v01/cad/pebble_enclosure.py) (v01c): 2.2 mm walls, TPU bumper band over the seam (0.4 mm proud), TPU foot, six snaps, TPU post washers, battery ribs, coin slot, white light guide under a translucent shell-tinted lens.
  - Size path: 134 × 52 × 26 mm, then 138 × 54 × 27 mm at about 138 g after thickening the tips (minimum wall 1.66 mm, typical 2.23 mm).
  - Hover tooltips in the viewer: name, function, part number.
- **Found / fixed:**
  - Full-resolution and medium-resolution builds failed or hung (empty or null booleans on denser lofts). The low-resolution loft is kept and exported with 4× finer tessellation (0.006 mm); web copies decimated to 40,000 faces per shell by [v01/cad/make_web.py](v01/cad/make_web.py).
  - Snap-bump clip boolean failed; clip removed.
  - Risk logged: the 12-LED ring was listed at 6.7 mm thick against an assumed 3.4 mm. Later removed by ADR-0009.

### 7. Phone simulator and here.now deploy (10:00–14:19)

- **Request:** "the software can get tested by opening a website on my phone as a simulation using phone components" and "use here.now as npc.here.now/demo_goldfish".
- **Done:**
  - `phone-sim/index.html`: eye, memory with day slider, transfer tab with QR, self-test. First published as a private claude.ai artifact, where microphone, vibration and NFC are blocked.
  - Published to here.now under the `npc` handle at `/demo-goldfish`. Reader page added at `/r`, so a note packed by the firmware decodes on a phone.
  - Phone support stated: speech recognition in Safari (iPhone) and Chrome (Android); Web NFC on Android only; iPhone reads tags natively.
- **Found / fixed:**
  - here.now rejects underscores in paths, so `demo_goldfish` became `demo-goldfish`.
  - "I tried opening in phone but it didn't work, didn't record :(" — added a status line with plain-language errors, a microphone level test, hold-to-talk, gentler iOS restarts and in-app browser detection. Root cause not confirmed; still open (B-17).

### 8. Per-participant resolution (10:07–10:32)

- **Request:** "Maybe instead of 5 dots for resoltion we have 10 dots, one per participant" … "resolution is at the participant level not at the device level?" Approved with "Yes go ahead on both decisions".
- **Done:**
  - `room.py` rule engine: first-person phrases set your own level; raising your own words needs no objection window; room-wide lowering still covers everyone; nobody raises anyone else; an uncertain speaker turns a lowering into a room lowering and ignores a raise.
  - Four first-person phrases added to the shared corpus (19 phrases). 28 firmware tests.
  - Phone simulator moved to a 12-dot eye with a "Speaking as" picker and a "Speaker uncertain" switch. JS and Python agree on 19/19 phrases and 6/6 rule scenarios.
  - Privacy constraint: voice embeddings clustered per conversation, in RAM only, never stored (GDPR Art. 9, BIPA).
- **Decisions:** ADR-0008 accepted; ADR-0006 superseded in part.

### 9. Workbench site and merge (16:05–16:24)

- **Request:** "for the product spec page, I was thinking more like this vibe" (html.moldandyeast.com, porous-forge). Then: "integrate the simulator into this screen and deploy on npc.here.now/demo-goldfish so we don't need two pages anymore".
- **Done:**
  - [site/workbench.src.html](site/workbench.src.html): Swiss monochrome, Helvetica, hairline rules, goldfish yellow as the only accent. Split screen.
  - Left: Specification (numbered PM document), Parameters (eight sliders re-forming the pebble with a live fit check; removed in phase 30), Rules (engine source with an in-browser test run), Sources.
  - Right: Model, Meeting, Print (256 mm bed, about 71 g of filament), Device (the merged phone simulator), Wiring, Graph, Parts, Checks.
  - "How do I see the full yellow not transparent?" — a Solid / See-through switch, Solid by default; all dimensions in mm and inches; scale references (12-inch ruler, hand, phone).
  - `site/build.py --web` writes a deployable folder `site/web/` with assets as separate files; the local build embeds them. Mobile layout puts the device first.
  - Previous long page kept as `site/spec-v1.html` (`site/build_v1.py`).

### 10. Meeting simulation (16:09–16:22)

- **Request:** "a lorem-ipsum continuous meeting about protocol discussions based on content from https://protocolsforbusiness.com/ and https://protocolized.summerofprotocols.com/t/sigbiz" … "a website joiner can toggle how 'private' each of the meeting attendees is".
- **Done:**
  - Five fictional attendees (Ada, Bruno, Chiara, Dmitri, Esi) speaking through the browser's speech synthesis, in a loop.
  - Script paraphrases themes from the two sources: an agent incident, drift, hardening, traces, ownership of failure, a pilot.
  - Attendees change their own levels in the script. A visitor's toggle makes that attendee say the matching phrase, so only a voice changes its own level.
  - Whole script run through the real engine; every level change happened as written. Voices untested headless.
  - Eye pinned while the transcript scrolls in its own box.

### 11. Light redesigns: eye ring, flank windows, continuous ring (16:25–18:01)

- **Requests:**
  - "more differentiation on the colors like red for off the record" … "like a signal of a mobile" … "Lights need to be visible around a round-table".
  - "lights maybe turn stronger when one person is speaking" … "two groups of three are speaking next to each other".
  - "more obvious where to place the phone, you might need an icon lit up on the tail" … "an on-switch physically".
  - "the eye doesn't need the 10 lights anymore … maybe we need only one main ON light".
  - "Maybe an easier design for the LEDs is to put a strip all around and code diff pieces of the LED strip?"
- **Done, round 1 (flank windows, deployed 17:15):**
  - Ten clear windows around the flank, four LEDs each from an Adafruit Mini Skinny strip (2969, 6.94 mm pitch).
  - Level as colour plus bar count: R0 red 4, R1 orange 1, R2 yellow 2, R3 green 3, R4/R5 blue 4.
  - Brightness 35 % at rest, full while speaking. Side conversations detected from reply links (within 6 s, at least 2 links); a room lowering said inside a group applies to that group.
  - Single eye light behind an Ø18 mm lens on the mic rail; 12-LED ring removed with its height risk.
  - Lit contactless icon on the tail (0.4 mm skin, NeoPixel Mini behind it); power slide switch on the belly flank.
  - Battery shown on all columns for 3 s at power-on and on the read button.
  - `display.py` and JS `lightColumn` share one map. Fit at 138 × 56 × 27 mm.
- **Done, round 2 (continuous ring, deployed 18:01):** one 41-LED strip (about 285 mm) round the seam, glued to a 4 mm land on the bottom shell's tongue, behind a natural (translucent) TPU bumper band. One 3.0 × 3.6 mm opening per LED. Each participant gets a segment that fills like a level meter: ¼ orange, ½ yellow, ¾ green, full red, full blue. Steps per segment: 12 for 3 people, 7 for 5, 3 for 10. The strip starts and ends beside the USB-C pocket, with no cuts.
- **Found / fixed:**
  - Strip zone blocked the NFC tag and clock board; packer now keeps a lateral clearance band. The clock "failed at every size" because of a stacking bug (it used the highest top of all overlapping parts) and a double removal from the failure list.
  - Power switch had one fixed height near the curved floor; now searches a range.
  - Bottom shell collapsed to 0.02 cm³: the loft-minus-loft tongue boolean failed silently. Replaced by an extruded analytic outline (`prism()`).
  - 3D windows rendered white (Solid mode made them opaque), then pastel (tone mapping); fixed with unlit saturated materials. Glow blown to white by additive blending; switched to normal blending.
  - "The NFC icon on the shell light looks like it's floating" — drawn flat on a sloping tail; now on the surface.
- **Decisions:** ADR-0009 (light ring at the seam, one light in the eye), with an amendment from ten windows to one continuous ring. Supersedes the 12-dot eye of ADR-0008 and the brightness-only language of ADR-0006.

### 12. Geometric review and electrical simulation (17:19–18:01)

- **Request:** "do a geometric review of the model, simulate it using online tools or code to make sure the circuits work and it's 'buildable'" … "Are there microphones all around?"
- **Done:**
  - [v01/cad/review_geometry.py](v01/cad/review_geometry.py): measures openings, ring path and pitch, controls, USB-C, mic seals, thermal and drop. 28/28 at the time; 27/27 in the latest run.
  - [v01/sim/power_sim.py](v01/sim/power_sim.py): time-stepped LiPo model through the bq25185 power path, power switch, boost, Radxa and ring, on datasheet values sourced by a research agent. Inputs in [v01/sim/design.json](v01/sim/design.json).
  - Validation grew to 10 checks: geometric review, electrical simulation and light-ring parity added.
  - Answer: two microphones only, each behind a Ø1 mm hole on top.
- **Found / fixed:**
  - Ten windows removed 79 % of the shell at window height and cut 32° into the dome (the notch Rafa saw). Led to the continuous ring.
  - USB-C socket 6 mm inside the tail; plug pocket added. Mic switch and read button had holes but no part; both placed. Printed slider caps added. Foam seals per mic.
  - Electrical: the power switch couldn't turn the device off (boost passes battery through when disabled) → high-side P-MOSFET. Radxa got only 4.55 V through a Schottky → LM66100 ideal diode (4.92 V). Vibration FET marginal at 3.3 V → AO3400A. Ring over-current risk → 0.5 A polyfuse and 35 % brightness cap. RTC board must run at 3.3 V.
  - Battery: 1.5–2.8 h of listening per charge against the 72-hour goal. Logged as the one failing requirement (P-11 / N-01) and moved to a v02 low-power capture architecture (B-21).
- **Follow-ups:** "How come an iphone / phone gives so much battery?" — about 100× lower power draw (dedicated always-on audio core, sleeping main processor), only about 2× more battery. "What's the black thing sticking out the side?" — a viewer bug: any part named "mic…" got the microphone model, so the mic switch showed a board through the shell. Fixed and redeployed.

### 13. Repository (18:31–18:32)

- **Request:** "save all this to a repo called goldfish at https://github.com/rafaeldavid/".
- **Done:** private repo `rafaeldavid/goldfish`, one commit, 200 files, about 80 MB.
- **Left out on purpose:** here.now publish state, Python caches, pebble STEP exports (25–82 MB each, regenerable), the unzipped Radxa drawings (see [.gitignore](.gitignore)).

### 14. Sourcing and print files (18:37–18:51)

- **Request:** "link to the exact SKU purchase link online, and if it's a 3D printed link out to exact spec file plus link to where to order online or 3D print in Berlin?"
- **Done:**
  - Every BOM row linked to the maker's page (Adafruit, Pololu, SparkFun, Pimoroni, TI), plus an EU link where one exists (DigiKey.de, LCSC, 3DJake). 36 links opened; DigiKey confirmed in a real browser; RobotShop marked unverified; one dead link removed. Data in [v01/sourcing.json](v01/sourcing.json) and [v01/bom.csv](v01/bom.csv).
  - Nine printed parts, each with its own row, full-resolution STL and print settings ([v01/cad/print_spec.json](v01/cad/print_spec.json), `PRINT-SPEC.md`), plus one zip in the web build.
  - Print options: online (Protolabs Network, Craftcloud, Lux3D, 3D Partner, Shapeways, PCBWay, JLC3DP) and in Berlin (FabLab Neukölln, MotionLab.Berlin, YOUin3D). Filament from 3DJake.
- **Found:** battery, Radxa board, RV3028 clock board and the SparkFun NFC tag all blocked or US-only; read-button switch height unconfirmed; foam without an EU source (B-01–B-07).

### 15. Light mode, market comparison, roadmap and backlog (19:05–19:10)

- **Request:** "default to light mode and add to the repo to the product overview current market product comparisons and backog and recommended product roadmap".
- **Done:**
  - Site opens in light mode; a visitor's dark choice is remembered.
  - [01-concept/market-comparison.md](01-concept/market-comparison.md): Plaud, Bee, Limitless, Omi, Friend, Sandbar, Genspark, Meta Ray-Ban glasses, Otter; each claim tagged by sourcing. Finding: every product sends speech to a phone or the cloud; none states a bystander-consent feature; none deletes automatically.
  - [ROADMAP.md](ROADMAP.md): now / next / later with a gate per horizon.
  - [BACKLOG.md](BACKLOG.md): 25 items (7 P0, 13 P1, 5 P2) with a definition of done and a source each.
  - `site/build.py` gained a small Markdown-table reader, so Specification sections 9–11 render from these files.
- **Found / fixed:** kit cost recomputed from the BOM: $189 for USD-priced rows, about $220 with EUR/GBP rows, before printing. README still said "pill"; corrected. Italic tags rendered as raw asterisks; renderer fixed.

### 16. The Resolution Protocol (19:52–20:15)

- **Request:** "develop a protocol framework for recording where we have the resolution specification at the individual level. The goldfish parameters should be one expression of the protocl in terms of localization and specification" … "make sure you do a multiround review to look for simplification, universal-ishness". Then: "each voice gains an identity in the recording process, so each voice controls its own record".
- **Done:**
  - Prior-art survey ([protocol/research/prior-art.md](protocol/research/prior-art.md)): SIPREC, meeting platforms, consent-record specs, ODRL.
  - [protocol/SPEC.md](protocol/SPEC.md) (RP 0.1, draft): RFC 2119 language, five principles, conformance classes.
  - JSON Schemas for profile, locale and event; grammars as data ([protocol/locales/en.json](protocol/locales/en.json), [es.json](protocol/locales/es.json)); conformance vectors.
  - Two dependency-free reference implementations, Python (`protocol/python/resolution_protocol`) and JavaScript (`protocol/js/resolution.js`), held to the same vectors.
  - New voice-owned intents: `claim` ("remember my voice") and `erase` ("erase everything I said").
  - Goldfish as a profile: [v01/profile/goldfish.profile.json](v01/profile/goldfish.profile.json), with bindings in `v01/firmware/goldfish/device.py` and `v01/firmware/js/goldfish.js`. Old `engine.py`, `intents.py`, `room.py`, `curve.py` and `phone-sim/` removed. Layering in [03-architecture/code.md](03-architecture/code.md).
  - Site Rules tab runs the conformance vectors in the browser; Device tab logs voice identities.
  - Spec Appendix A records four behaviour fixes found by the split: a room lowering caps a self-raised voice; a self-lowering never raises; "back on the record" waits through the objection window; shortening applies to outcomes.
- **Decisions:** ADR-0010 (each voice owns its record), ADR-0011 (protocol and profile; supersedes `06-protocol/`). Backlog B-26 (per-voice erasure keys) and B-27 (external review of RP 0.1).

### 17. Protocol review round 1 and the "back on the record" bug (20:10–20:18)

- **Reviews:** three parallel reviewers on simplification, universality and correctness.
  - Simplification: a side-conversation "off the record" is silently dropped when groups merge. Proposals: 8 intents to 6, one term per concept ("requested" and "applied", never "rung"), first-match grammar with `{count}` and `{unit}` placeholders, a shorter spec.
  - Universality: any default above level 0 contradicts "no class treated as consenting by default"; meeting software needs account identities and button commands; glasses need room raises switchable off; SIPREC mapping partly inverted; missing rules for processed-but-not-kept speech, recorder-to-recorder exchange, versioning. Idea: lowerings merge by minimum, so one small preference object can carry settings and exchange.
  - Correctness: 6,000 random sequences through both engines. Agreement everywhere except phrase matching. Bugs: stray "I" or "my" turned room lowerings personal; provisional raise survived session end, didn't restart on a second raise, failed open on a crash; speech after a "forget" could escape it; a segment spanning a level change took the higher level; Python and JS differed on accented and non-Latin text; erase didn't withdraw a claim; "keep this for 0 days" ignored.
- **Bug report:** "when someone says 'back on the record' it doesn't return to pre off-the-record state". Cause: the RP 0.1 rule made "back on the record" wait 30 s, and the Meeting tab never showed the waiting state. Fix: it applies at once as *provisional*; an objection or new lowering within 30 s undoes it and restamps that window. New vector for the exact meeting case. Both engines pass 79/79 vectors (26 English phrases, 16 Spanish, 22 sessions, 15 retention). Validation 10/10, 97 unit and scenario tests.
- **BOM question:** "Does the bill of materials also include all the screws / connectors etc.?" Assembly guide rewritten for v01c (ring on the tongue with resistor and capacitor, single eye light, slider caps, carrier board for small SMD parts, bench checks for the new power wiring). Exact links for the missing consumables (adapter boards, battery lead, 26 AWG wire, strip resistor and capacitor, tape, glue, heat-shrink, solder) under research at the time of writing.

### 18. RP 0.2 and protocol review round 2 (to 20:47)

- **Done:**
  - Each voice owns its record. The protocol and the Goldfish profile are split.
  - Raises for others are provisional for a 30 s objection window.
  - Review round 2: lowerings and forgets can no longer fail silently.
  - Python and JS conformance vectors with a differential fuzz; the kit BOM completed; a favicon.
- **Commits:** 019e73e, 1ad9ab9, d0bf32d.

### 19. Builder simulations, the eye as a button, a kit with no SMD (to 23:09)

- **Request:** "the eye / or entire strip needs to go red when it's off the record mode, and maybe the eye needs to be able to be pressed … make the overall build more kit-friendly".
- **Done (ADR-0013):**
  - The eye is a button over a tactile switch, and the whole ring glows red while the room is off.
  - The kit has no surface-mount soldering: plug-in STEMMA QT boards and through-hole switches.
  - Electrical fixes from five simulated builders: fuses, a thermistor, 5 V for the ring.
- **Also done:**
  - Licences (Apache-2.0 / CERN-OHL-P / CC BY 4.0).
  - A Files page with a Markdown viewer and a source zip.
  - A brief Goldfish entry and post on rafael.fyi under "Robots and hardware".
- **Found / fixed:**
  - A silent CAD bug: spline faces extruded downward, so some LED openings never cut.
  - A crash in the 3D viewer.
- **Commit:** b0cc054.

### 20. A bigger cell, continuous on a phone charger, Markdown notes (2026-10-13, to 09:22)

- **Requests:**
  - "Can we double the battery size … ideally single battery tho, and make sure it works continuously if plugged into normal USBC phone charger?"
  - "we need basically 3 hours of continuous play"
  - "worst case should be 1.5 hours or 2 hours is fine for the first iteration"
  - "the output files of transcripts should be markdown LLM friendly"
  - "optimizing model energy efficiency will be a serious research project"
  - "If it's heat, maybe we need to add a microfan or whatever or heat sinks"
- **Done (ADR-0014):**
  - **Cell:** EREMIT 4000 mAh, the only ~4000 mAh JST-PH cell in German stock. Rafa chose to widen the pebble for it.
  - **Charger:** the bq24074 board (Adafruit 4755): USB runs the device first, up to 1.5 A.
  - **Shape:** 154 × 78 × 33 mm, with a 50-LED ring.
  - **Heat:** heat now modelled. A heatsink, pads and an aluminium spreader; no fan, because the mics would hear it.
  - **Simulation:** 3.1 h typical, 2.7 h worst case, and 72 h on a charger.
  - **Notes:** in Markdown with front matter (`v01/note-format.md`), identical from Python and JS.
  - **Backlog:** B-41, a research track on model energy efficiency.
- **Found / fixed:**
  - The wider shape pinched the wall to 1.39 mm near the tips. The cavity became a slope-aware offset (min 2.04 mm).
- **Commit:** d394b50.

### 21. Five usage and build simulations (2026-10-13, after 09:22)

- **Request:** "can the user reach the switchs with their finger? … run multiple simulations for the build and the usage step by step to check usability and build opportunity".
- **Simulated:** a first-time owner, a five-person meeting, an accessibility user, a novice kit builder, and a mechanical engineer measuring the meshes.
- **Found (blockers):**
  - A board post through the eye opening (shipped in d394b50).
  - The tongue overlapping the top shell's tips (a side effect of phase 20's offset).
  - An eye cradle that couldn't take the switch's legs.
  - A heat stack fitted in an impossible order, and 3 mm over-squeezed.
  - Slide switches that needed a fingernail.
  - A coin slot no coin fits.
  - A press that toggled, so pressing "to stop" could turn the room back on.
- **Rafa's rulings:**
  - Reaching the eye from across the table is fine.
  - Showing the undo window is an advanced feature, fine for now.
  - Half the ring per seat is fine "as long as person can see their own lights".
  - Passing the pebble is fine.
  - The phone transfer should need no button.

### 22. The usability and build round (2026-10-13)

- **Done (ADR-0015):**
  - **Eye presses:** a press only ever goes off the record; a double press while off comes back on; holding it shows the battery.
  - **Phone transfer:** no button. The note goes into the always-readable tag when the meeting ends, and the tail icon lights.
  - **Seat-facing ring:** each person gets two mirrored arcs on the side facing them (`display.seat_frame`), because two mics give only the angle from the head. The meeting view seats five people.
  - **Feedback:** room off breathes red; the objection window counts down in amber; a dim idle pixel; a haptic vocabulary (`display.HAPTIC`).
  - **Shell:**
    - three board posts;
    - the tongue clipped to the cavity;
    - the eye cradle as its own print;
    - printed switch caps 1.8 mm proud (smooth with one dot for power, ridged with two dots for the mics), 31 mm apart;
    - a 12 × 2.6 mm coin slot;
    - softer snaps;
    - a 1 mm eye dish with a uniform 1 mm lens;
    - a 0.05 mm switch preload;
    - a roof boss that sets the heat-stack squeeze to 0.5 mm with soft pads.
  - **Docs:** the assembly order rewritten (heat stack before the board, power modules before the charger, the cell after soldering, 100 mm seam leads, epoxy on switch backs). Build time restated as 12–15 h. Print settings corrected for supports.
  - **New review checks:** posts clear of the eye, shells don't overlap, switches reachable and told apart, coin slot, heat-stack squeeze.
  - **Firmware checks:** 32 tests. Python and JS agree on 18 ring frames and 6 eye-press sequences.
- **Found / fixed along the way:**
  - OCC booleans on the slope-aware *outward* offset failed silently and turned the band inside-out. Outward offsets are now straight; lofts that come out inside-out are reversed.
  - The 1.5 J drop-energy line was retired as arbitrary. The 1.2 m drop test decides (B-43).
- **Validation:** 10/10.

### 23. Wiring in the 3D model (2026-10-13)

- **Request:** "Can you add the wiring to the 3D model as an option to view? … make sure the case also fits them".
- **Done:**
  - **Router:** `v01/cad/wire_routes.py` routes all 27 wires of `wiring.md` (power, ground, I²S, ring data, controls, STEMMA QT cables) through the free space of the closed case.
    - It uses A* on a 1 mm voxel grid around every part and clear of the light strip's zone.
    - Paths stay mid-space and spread away from bundles already laid.
    - Output: `out_pebble/wires.json`.
  - **Site:** the 3D model has a **Wires** toggle, with tubes at true thickness coloured by kind (power red, ground black, data yellow, I²C blue, mic purple, control green). Hovering names each wire with its gauge and cut length.
  - **Cut list:** wiring.md now has a generated cut list: routed length × 1.1, + 60 mm for wires that join the two shells, so they can lie open side by side. It replaces the flat "100 mm" rule, which was too short for 5 of the 12 seam wires (the longest route is 174 mm).
  - **New review checks:** every wire routes; every bundle fits (the worst squeeze, 0.42 mm, is within the grid's 0.5 mm tolerance); the seam wires are cut long. Review 36/36; validation 10/10.
- **Notes:**
  - Pads are approximate (a part's face toward the other end, or the Radxa header pin), so lengths are ±10 mm.
  - The router takes about 10 minutes, so `validate.py` reads its last output. **Rerun `wire_routes.py` after any layout change.**

### 24. Flared post roots (2026-10-13)

- **Request:** "Some additional advice: add flared/filleted bases to those cylindrical standoffs or screw holes 🙂 3D prints especially will be super prone to snapping at the base otherwise".
- **Done (ADR-0016):**
  - **Flares:** each of the three board posts meets the roof through an R 2 mm fillet that turns into a 45° flare, 2 mm wider at the roof (r 3.2 → 5.2 mm, 2.8 mm tall). The 45° part prints dome up without new supports.
  - **Sloping roof:** the roof climbs up to 2.1 mm across a post's root. Each flare is anchored at the lowest point of its root, so the fillet is whole downhill and ends in a short collar uphill.
  - **Packer:** a `root flare of post …` keep-out per post. The first build put a flare 1.7 mm into the tail mic's board; with the keep-out the tail mic moves 2.0 mm toward the head.
  - **Tail icon at 3/4 size:** the icon's outer arc passed 0.15 mm from post 3.5, 26.5, and the flare covered about 2 mm of it from inside. Rafa chose "Shrink the tap icon ~25%". The icon's shape lives in one place (`NFC_ICON`), which the CAD and both site views read. The packer reserves the icon's light path.
  - **Router:** treats each flare as an obstacle at its full width up to the roof. All 27 wires still route; the worst squeeze is unchanged at 0.42 mm.
  - **New review checks:** post roots flared (probed in the printed mesh, 108/108 points) and post flares clear of parts. Review 38/38; validation 11/11.
  - **Docs:** the print note for the top shell; B-43 asks for a look at the post roots and the unflared cradle pins after each drop.
- **Found / fixed along the way:**
  - The new clearance check caught the mic conflict. The icon conflict was found by looking at a section through the post: a closed void where an icon pocket should open into the cavity.
  - The router took 43 s, not the ≈ 10 min in AGENTS.md; the stated times are updated.
- **Not changed:** the eye cradle's three pins. Only about 1 mm of each shows above the boss that sleeves it.

### 25. Faster iteration, print practice from the community, fasteners in the model (2026-10-13)

- **Requests:**
  - "There may be more efficient ways to save the model / code the model so we can do progressive iterations easier. Give that some thought"
  - "go ahead and review the recommendations then implement them"
  - "point yourself also to 3D printer feedback forums online as well to integrate best-practice from ongoing discussions as well into our design"
  - "And the bom and model have the screws / fastenings as well?"
- **Review of the recommendations:**
  - **Profile first.** The enclosure took 285 s. Two pure-Python loops took ≈ 50 s of it; that was not the CAD kernel. The `shell overlap` check took 55 s for one exact boolean, and the icon 38 s. Export was only 10 s.
  - **Done:** stage checkpoints, one build command with stamps, keep-outs as layout boxes, and a section tool.
  - **Not done:** recommendation 5, git LFS or untracking the meshes. That is the owner's call (B-53).
- **Done, tooling:**
  - **Speed:** the profile is memoised (`Pebble.profile`), the land search is vectorised, and the overlap check runs on the meshes (manifold, 0.2 s). Outputs are identical to the baseline: byte for byte for the bottom shell and `layout.json`, and to 0.01 mm³ in volume for the top shell.
  - **`stages.py`:** checkpoints the enclosure after each `tick()` section, keyed by a hash of the section's code, every earlier section and every input. Shapes pickle exactly once their boolean history is stripped. A rebuild with no change takes 14 s; a change in the icon section, 63 s; from scratch, ≈ 3 min.
  - **`stamps.py`, `v01/build.py` and the new "Generated files current" check:** each step stamps the files it read. A stale step fails validation by name, and `build.py` reruns only the stale steps. `validate.py` now exits non-zero on a failure.
  - **Keep-outs and fasteners are layout boxes** (`role: keep-out | fastener`, named `<what> of <owner>`). The router's flare-specific code is gone, and one check covers them all.
  - **`section.py`:** sections of the printed meshes with the layout boxes they cross.
- **Done, design (ADR-0017), from community and maker sources:**
  - **Insert bores to the datasheet:** Ø4.0 × 6.7 mm in Ø7.2 mm posts (were Ø3.6 × 6.0 in Ø6.4). The flare is 1.6 mm wide, so the root stays 10.4 mm across.
  - **Fasteners modelled:** inserts and M2.5 screw heads. The heads clear the battery bay by 2.1 mm, and each screw engages 3.4 mm of thread.
  - **Inserts at 245 °C** (it said 220), melted to 90 % and then pressed flush.
  - **Icon skin:** 0.36 mm (it was 0.2 mm in fact; the docs said 0.4).
  - **Lead-in** on the top shell's lip.
  - **Foot glue:** a flexible adhesive for the TPU foot, not CA.
  - **A test coupon** (P11).
  - **Print notes:** nozzle temperature and support gaps.
  - **Re-pack:** the NFC tag moved 0.5 mm and the icon LED 1.5 mm toward the tail.
- **Found / fixed along the way:**
  - **First lip chamfer:** it was lofted from the analytic outline and bit up to 56 mm³ per 6 mm into the tip walls, where the outline runs outside the loft. Measured section by section from a checkpoint; now grown from the lofted cavity.
  - **Measuring small cuts:** the top shell's volume carries ≈ ±15 mm³ of integration error, so a small cut is measured as an intersection, not a difference.
  - **Flare probe:** the "Post roots flared" probe sat on the surface of the narrower flare; it now probes halfway into it.
  - **Section tool:** it dropped long segments; fixed.
  - **Docs check:** it caught "ten printed parts" once the coupon joined print.json; the coupon isn't a device part.
- **Validation:** 12/12. Review 41/41.
- **Not changed:** the eye cradle's pins, the 0.15 mm fit (until the coupon says otherwise), and PETG (ASA needs an enclosed printer).

### 26. Firmware hardware layer, and every board screwed down (2026-10-13 to 10-14)

- **Requests:**
  - "Are all the pieces inside fastened correctly? we obsviously don't want it to break if we shake it?"
  - "Go ahead and implement, maybe in parallel begun programming the firmware as well?"
  - "In terms of fastening, it's also ok to make the case slightly larger and make it all "screwed in" if that makes sense"
  - "The reason I chose screw every board is that for the prototype we will likely need to build and unbuild it multiple times as the build evolves after physical testing"
  - "I assume in your analysis you are considering also that the case is malleable as its 3D printed right?"
  - "For the nuts and screws you can change the purchase to be a kit of them"
  - Plus a peer builder's note on hole shrinkage and heat-set inserts (both already in ADR-0017).
- **Done, firmware** (merged `ec3791f`; a parallel agent in its own worktree):
  - A hardware boundary with simulated and Linux backends.
  - The ring encoder (exact SPI bytes; the icon LED first).
  - Eye debounce into gestures, haptic patterns, and the B-44 clean-shutdown state machine.
  - Audio sources and model seams, and a main loop with `--sim`.
  - `tools/ring_test.py` (B-31).
  - 83 firmware tests (234 with the protocol's).
  - Nothing has run on hardware: every pin, register and timing is marked `VERIFY` (B-59). Its findings are B-56 to B-59.
- **Done, fastening (ADR-0018):**
  - **Mounting:** captive-nut bosses with flared roots and one M2.5 × 6 screw everywhere; 30 nuts in all.
  - **Boards:** seven on their own holes, five under printed straps.
  - **Battery deck:** screwed to four floor posts with heat-set inserts. It clamps the cell, which sits on foam tape.
  - **Charger:** its DC jack comes off.
  - **Mics:** port tubes.
  - **Heat stack:** a 3 mm top pad.
  - **Docs:** the assembly guide rewritten around screws, plug clamps (Kapton for now), tacked wires and a shake test; the BOM as a screw and nut kit.
- **The case:**
  - **Size:** 158 × 79 × 37 mm and about 247 g, inside Rafa's budget, with a fuller tail.
  - **Packer:** a mounted-placement search over floor, deck and roof, with mixed supports and stacking. Fixed-position parts go first.
  - **Trace hook:** `TRACE` counts why a part's candidates fail, which found every blocker in this round.
- **Found / fixed along the way:**
  - **OCC volume integration on spline faces** was off by hundreds of mm³. It made a good coin-slot cut look failed, and the band look three quarters gone. The meshes were right. Cuts are now judged by intersection, and masses come from the meshes.
  - **Mass:** it had counted four board washers for three posts.
  - **The lip check's 60 % ratio** was arbitrary; it now probes the printed mesh.
  - **The heat-stack check** caught the taller case's 2.2 mm PETG boss over the SoC.
  - **The ring check** caught 53 LED positions against the firmware's 50.
- **Validation:** 12/12; geometric review 44/44.
- **Not done:** plug clamps (B-54), a less fragile packer (B-55), and the kit's own link (B-60).

### 27. The site's meeting runs the real firmware; a flow test replays it (2026-10-14)

- **Requests:**
  - "the simulation should run the firmware no? So that you're actually testing the flow?"
  - "Start the next round as well on the real firmware"
- **Done (ADR-0019):**
  - **One script:** `v01/firmware/scenarios/meeting.json` holds the five attendees, their seats, the 20 lines and the site's captions-only timing. The site inlines it; the flow test reads it.
  - **One runner:** `goldfish/meeting.py` (`SimRun`) drives `daemon.py` on the sim board. The audio pipeline and the distiller are its only stand-ins.
  - **Flow test:** `tools/flow_check.py`, `tests/test_flow.py` (24 tests) and the new validate check "Firmware flow". It replays the meeting with six scripted presses, then the 10-minute gap, and checks 12 things from the outputs alone:
    - the power-on gauge and the idle pixel;
    - every voice's arcs facing its seat;
    - the whole-ring red breath;
    - three amber countdowns;
    - the battery while held;
    - eight press outcomes;
    - the spoken requests;
    - 15 motor patterns;
    - one tag write;
    - the note against each line's filed level;
    - the tail icon for 12 h;
    - the next meeting.
  - **Firmware in the page:** the Meeting tab loads Pyodide 314.0.7 (jsDelivr, exact version, released 2026-09-14) on first open. It runs the firmware's own Python files, draws the ring from the SPI bytes, plays the motor patterns, and shows the note read back from the simulated tag. The JS copy draws the page at once, and stays as the fallback, labelled "Engine: preview (JS)".
  - **Measured:**
    - **Download:** 6.3 MB over the wire for Pyodide's core.
    - **Load:** 1.2–1.3 s cached, 5.9 s cold.
    - **Running:** about 0.3 ms per loop pass.
    - **Same device in both runtimes:** CPython 3.12 and Pyodide's Python 3.14.2 give the same hash of every SPI transfer, motor edge and tag byte (`tools/pyodide_flow.mjs`).
    - **`store.py`:** its AES-GCM works in Pyodide (`cryptography` 47.0.0, +2.4 MB, not loaded by the page).
- **Found and fixed in `daemon.py`** (`display.py` and `device.py` unchanged):
  - **No buzz:** a voice whose first words lowered itself got no "own-lowered" buzz. The level before was taken as unknown, not the room's.
  - **Speech never filed:** the daemon never filed speech in any record, so the distiller seam would have got nothing, and "forget what I just said" reached nothing. `hear(span=)` now files each line at the lowest level that applied over it.
  - **No seat-facing arcs:** the main loop ignored seats and drew only the segment ring, against ADR-0015. With bearings it now draws `seat_frame`.
- **Found, not fixed (backlog):**
  - **B-62:** round-robin turns can read as two side conversations.
  - **B-63:** a lowering inside an objection window erases everything said since the resume.
  - **B-64:** motor patterns are quantised to the 30 Hz loop, so still-off and battery can feel the same.
  - **B-65:** the tag write freezes the loop for 1.8 s.
  - **B-66:** the Device tab still runs the preview.
- **Also:** the spec's "Firmware" row said "Not written yet"; it now says what exists.
- **Validation:** 13/13, with the new "Firmware flow" at 12/12; 258 unit tests (was 234). "Light ring parity" is kept, since the preview still exists. Looked at in Chrome: the Meeting tab switched to "Engine: firmware (Python)" 1.2–1.3 s after opening (5.9 s cold); presses took the room off and resumed it; after the last line the gap wrote a 1520-character note, read back from the simulated tag, and lit the tail icon.

### 28. The buildability walk (2026-10-14)

- **Request:** "If you were me and considering ordering all the parts (assume they are available) and building the kit, what checks would you do to make sure the kit was actually buildable? Run those in iterations".
- **Done:** `v01/cad/buildability.py`, run by `validate.py` as "Buildable kit". It has 12 checks:
  - BOM against the model;
  - print rows and settings;
  - fasteners used against bought;
  - docs counts;
  - shells closing straight;
  - driver access to every screw at its assembly step;
  - nut slots opening into free space;
  - board components under bosses and heads, from the vendor models;
  - STEMMA QT cable lengths with seam slack;
  - print bed fit;
  - supports named;
  - every board wired.
- **Iteration 1:** 8/12. Found:
  - the router's seam test by height (the deck's boards are bottom-shell);
  - two STEMMA QT cables too short;
  - four mic nut slots pointing at their port tubes;
  - the top shell's hanging bosses missing from its print notes;
  - my own 0.2 mm head margin, and pads counted as parts. The check now uses the physical radius and ignores copper.
- **Iteration 2:** 11/12. One mic slot was still blocked, by the heat-stack boss, which the slot chooser didn't know.
- **Iteration 3:** 12/12; validation 13/13.
- **Not checked:** the clock (no 3D model of its board), soldering access at each step, and anything only a real print shows (B-51's coupon).

### 29. A chassis and a lid (2026-10-14)

- **Request:** "Is the case openable with the wiring? Maybe we need to rethink how the pieces are screwed on so one of the cases (top or bottom) opens easily?". Then "yes go ahead", and "Grow the case" when asked about the charger.
- **Done (ADR-0020):**
  - **The chassis:** every board is in the bottom shell. The Radxa stands on four slim deck bosses (r 3.8), sized because its underside parts are 4.0 mm from the holes.
  - **The lid:** only the eye, the mics, the NFC tag and its icon LED.
  - **One plug:** a latched 12-way Harwin Gecko G125 under the roof. 7 wire runs cross the seam, all ending at it (10 before shared lines were merged); a new buildability check holds that.
  - **The case:** 182 × 79 × 37 mm (spec L 184) and about 259 g, from 158 mm long.
  - **The heat stack** closes as the lid goes on, on three 1.5 mm top pads.
  - **The board washers** are gone.
  - **Docs:** the assembly guide rewritten (§5 chassis, §6 lid), and the BOM's connector row and counts: 36 nuts, 40 screws, 4 inserts, 6 straps.
- **Found / fixed along the way:**
  - **Charger placement:** the 41 × 33 mm charger has no room in the bottom shell short of a longer body. The packer found 184 mm; a deck past the cell's nose would hit the light strip's zone.
  - **The connector** didn't fit in the chassis, so it lives in the lid: either half works for an inline plug.
  - **Radxa bosses:** they overlapped the deck boards; they're now placed first.
  - **The union guard** failed a good boss on the shell's volume error; it now judges the piece.
  - **The band:** a 6-edge tessellation crack, repaired on export.
  - **Paused once mid-round** at Rafa's request (WIP commit f772e56).
- **Not done:** B-67 (the connector costs ≈ €77 with a 15-week lead time), B-68 (a smaller charger would bring back ≈ 22 mm).

### 30. Wiring at pad level (2026-10-14)

- **Request:** "Can we increase the fidelity of the model so that we can see where the cables are connected / soldered? and have the pins more clearly visible?". Then "deploy when you're ready".
- **Done (ADR-0021):**
  - **`cad/board_pads.py`:** every part's pads, from the vendors' own files: Adafruit and SparkFun Eagle boards, Pololu's STEP files and drawings, Radxa's DXF, and the switch and connector datasheets. Two boards are from photos only and are marked unverified.
  - **Board frames:** each board's frame is computed once; the site draws its model and its pads in it. A new check holds the frame to the mounting holes.
  - **The router** lays runs as bundles and takes each wire to its own pad: 66 wires in 31 runs, on 108 pads (was 34 wires between box faces).
  - **The site:** pads (gold rings or squares), solder fillets, plugs in their sockets, and sleeves over inline parts (fuses, D4, resistors). **Pins** names the pads in use; hovering over a wire names its two pads. The lid connector is drawn (it wasn't).
  - **Cut list:** per wire, pad to pad. Plus the lid connector's pin map.
- **Found by the pad-level model, and fixed:**
  - **Missing wires:** the lid's ground (the mics, the eye light and switch, the icon LED), I²S DOUT, the motor's transistor emitter and + feed.
  - **One hole, one pin:** two wires shared Radxa pins 1 and 6. The QT cable's ground moves to pin 9, the mic switch to pin 17 (Radxa's table: "+3.3V 17").
  - **The 5 V boost has one VOUT pin,** which can't take three wires: the icon LED feeds from the strip's +V.
  - **Charger output:** the "Load Out" socket faced away from the 2810 (210 mm). The header's OUT pad is used instead.
  - **Cable too short:** secure element → clock needs ≈ 115 mm, so the 100 mm cable goes; the 200 mm one takes its place.
  - **No splices at the connector:** a contact has one pigtail, so the lid's ground chains pad to pad, and the tag's I²C runs on pigtails with no cable cut.
  - **Straps on joints:** the ideal diode's strap sat on its header (moved; the packer now records each board's turn), and the icon LED's strap pressed its back pads (now 1.8 mm off, on two foam pads).
  - **Blocked plug:** a lid strap screw stood in front of the clock's STEMMA QT socket.
  - **Radxa header:** the formula was 0.9 mm off, corrected from the DXF.
  - **Lid pigtails:** all 24 are white (Harwin), so the builder flags each.
- **Then:** "Since the model is getting pretty detailed, maybe left-side panel with spec is collapsible?": a "Hide spec" button gives the model the full width. And, asked whether the lid connector could be wired half by half and snapped together, the answer was that it already is (two latched halves, each with its own pigtails); the model now shows it: "lid half (B)" under the roof, "chassis half (A)" plugged in, and A drops with the chassis when the view explodes.
- **Then:** "In the parameters it says 24 pieces outside the cavity, maybe we don't need this parameters section anymore since it's not really useful to the user". The Parameters tab is gone. Its fit check tested layout boxes (bosses, straps, keep-out space) against an analytic shell, so it reported parts outside that aren't; the real fit checks are the packer's, the geometric review's and the buildability walk's. And "I don't see button to collapse the spec side-layout": the control moved from the header onto the panels: "‹ Hide" in the spec panel's tab bar, "› Spec" in the model's to bring it back.
- **Then:** "Can you add a way to zoom in and out easier?": + / − / fit buttons on the model, the wheel zooms toward the pointer, a double-click on a part flies in to it (on empty space it fits), and the + and − keys zoom.
- **Then (ADR-0021, amended):** "For the cables that branch over in the "open" state, can you make sure they don't go "through" any of the components?" and "for the wires that go from component to component can you make them longer / looser". The open lid is now routed through free space (both shells, every part, the lid's turned over). That showed two chassis runs need 160 mm, past the 150 mm pigtails, so the chassis half is now the 450 mm socket. Every soldered wire gets 30 mm to work with, folded into a loop closed (29 loops, all stowed); total cut 7,860 mm.
- **Then (ADR-0021, amended):** "Do the wires that go to the switches on the side fit alongside the battery?" They didn't: the switch pins and four wires were in the belly wall. A new check, "Every wire, leg and joint fits inside the case", then found wires through three straps and the Radxa as well; the router now keeps whole bundles clear. Rafa chose "Pins inward, case +6 mm": the switches are now the vertical C&K OS102011MS2QN1 on their backs, pins trimmed to 1.5 mm and pointing in, and the case is 182 × 85 × 37 mm, about 266 g. The clock turned 180° ("spin the real-time clock 180 degrees"); the NFC tag stays as it is, for its antenna. Spare with no room for folds lies as a hairpin; both halves of the lid connector now have 450 mm pigtails. Validation 14/14; the buildability walk 19/19.
- **Not done:** B-69 (pads from photos and the 5830's spare pins, checked at bring-up), B-70 (the 2810's real height and its actuator), B-71 (a 3421 model from its own board file).

### 31. A landing page (2026-10-15)

- **Request:** "add the following prompt to the backlog to create a landing page for the goldfish (use the same site)". Then, after two rounds: "landing page now should be at npc.here.now/demo-goldfish deploy and move workbench to /demo-goldfish/workbench across the board", and "Update everything / commit / clean up please."
- **Done (ADR-0022):**
  - **Research** (`01-concept/landing/research/`):
    - facilitator and participant voices (82 items);
    - academic sources and the rules facilitators work under;
    - competitors;
    - an audit of Goldfish's own pages;
    - a voice corpus of 361 verbatim facilitator quotes;
    - how competitors and neighbouring products position themselves.
  - **Writing:** a voice guide and a copy deck, approved by Rafa before any layout. A test kit and a privacy reviewer's sheet.
  - **The page** (`site/landing.src.html`, `site/landing/`):
    - **Brand:** a goldfish logomark that flips into a thought bubble; Funnel type; clear-water blue with goldfish yellow.
    - **Content:** photos of real sessions; three demos on the firmware's JS mirror; the real 3D model with its insides; a product header and footer; an interactive 404.
  - **The site:** the landing page at `/`, the workbench at `/workbench/`, the reader still at `/r`.
- **Found, and fixed or written down:**
  - **No machine writes a note yet.** The note sentences in `meeting.json` are hand-written stand-ins. The page says so next to every note.
  - **Overstated claims elsewhere:**
    - key destruction has run in software only;
    - "pocket" is wrong (182 × 85 × 37 mm);
    - the ring's arcs are mirrored pairs, not one per seat.
  - **The note on the tag never ages,** and any phone that touches the tail can read the last note (B-75).
- **Lessons Rafa gave, kept in the voice guide:**
  - sell the session, not the device or "no minutes";
  - write in normal English, inviting, short;
  - render and look at desktop and phone width before showing a page.
- **Not done:**
  - B-76 (the pre-sale Worker route);
  - B-77 (a photo shoot with real facilitators);
  - B-78 (public repository for the GitHub link).
- **Commit:** 74d7145. Deployed to npc.here.now/demo-goldfish; validation 14/14.
- **Then:** voices, and the 3D model as a short film.
  - **Voices:** Fish Audio, made in Rafa's logged-in browser ("you can probably just use the browser as well"). Five Fish Official voices: Alice (Laura), Bob (Adrian), Charlie (Ethan), Dana (Hannah), Eve (Sarah). Nine clips in `site/landing/voices/`, with `manifest.json`. They were made on the Free plan, so they are for an internal demo only. Rafa, verbatim: "It's not commercial use sir, this is for an internal demo on how Claude can help with ideation and prototyping in personal research". The page says the voices are AI-generated.
  - **The film** (Rafa chose "Animation that plays like a video"): Act 1 plays the conversation with the real audio, and the ring lights for whoever speaks. In Act 2 the case opens and six parts light up in turn (the microphones, the eye, the light ring, the battery, the computer, the phone tag), each with a caption. Then it closes and loops. A two-part progress bar ("Listen in", "Inside") replaces the "Open it up" slider.
  - **Rafa on the first cut, verbatim:** "In the inside part of the listen in, all the pieces are transparent so hard for user to see, and during the Listen In, I don't see the lights yet", then "The lights also don't stay aligned in a line, they look a bit jagged / inconsistent and not enough like light, more like blurrs".
  - **Fixed:**
    - Parts in the tour stay solid. Parts outside the current scene turn pale, and the highlighted ones turn amber; boxes also pulse.
    - Each LED is now a flat, unlit tile just outside the seam, in one even line. Before, they hid behind the clear band, and soft glows blurred.
    - The device's dim levels are lifted on screen only (square root), since they are set for a dark room.
    - Act 2 keeps wall-clock time: Chrome drops frames in a background window, and a 0.1 s clamp per frame had slowed the tour to a crawl.
    - The camera sits farther back, so the open case fits.
  - **Copy:** "Serious technology." became "Technology that just works." (Rafa: "Instead of serious technology, say "Technology that just works"").
  - **Also:** a phone-tap animation beside "What happens to what you say", thought-bubble notes on the timeline, and the light states shown first.
  - **Testing:** in real Chrome, watched both acts end to end at desktop width, and Act 1 at narrow width; the ring's colors were also read from the page. Validation 14/14.
- **Then:** Rafa, verbatim: "Delete the artifact and archive the version, change workbench to the roadmap session, push everything to github we'll do rafael.fyi later", then "ok just leave the artifact but move to archive and don't update anymore etc."
  - **The claude.ai preview:** kept, not deleted, and no longer updated. Its last version is saved as `01-concept/landing/archive-claude-preview-2026-10-15.html`. The Artifact tool has no archive action, so moving it to an archive on claude.ai is left to Rafa.
  - **Workbench:** the scripted session in `v01/firmware/scenarios/meeting.json` is now the landing page's product roadmap session, with Alice, Bob, Charlie, Dana and Eve in the landing page's seats. It keeps the same 20 turns, cue phrases, presses and expects. The Meeting tab's intro now reads "An example product roadmap session." The flow test's labels follow the new names.
  - **Found:** the first draft of the new lines was shorter, so the 30 s objection window was still open at the hold after turn 9 ("Haptics" failed: one buzz instead of window-closed). Two lines got longer; the check is unchanged. Flow test 12/12, validation 14/14. In Chrome, the Meeting tab played the session on the firmware in Pyodide, and Dana's "keep my words to the gist" set her to R2.
  - **Pushed:** the branch `round-24-build-feedback` to GitHub (the repo stays private, B-78). The rafael.fyi post link is for later.
- **Then:** four requests, verbatim:
  - "Change "Some conversations only happen in person." to "Some conversations can only happen in person." (as in this tool is for in-person discussions)". Done.
  - "Can you update the build log, and the product spec on the workbench to align with all the feedback and the landing page?"
  - "In the header can we make "Have better conversations. Make better decisions." to be like an animation flip, where it cycles between "Have better conversations" "Make better decisions" "Work better together" not sure if there's two more same pattern"
  - "The flip is too quick, and better should always be the same, no change, so verb, "better", and noun on second line if that makes sense, maybe with a goldfish peaking up / swimming around "better""
- **Workbench spec, aligned with the landing page:**
  - The lead and overview: a recorder for in-person sessions around one table, up to 10 people, not for online sessions. A new paragraph says the product is the better session, not the notes.
  - Problem: the NPC memo quote is replaced by the facilitator research quote the landing page uses (Diana Mastel Gurwicz, INIFAC). The memo stays in the Sources tab as provenance.
  - Business rationale: the device's estimated price ($199–$499, Rafa's range) next to the kit's parts cost, and the first audience (facilitators; sell the session, never note-taking).
  - "Session", not "meeting", in everything a reader sees: the Session tab, "Listen to a session", "End the session", the battery line. Code names (`meeting.json`, `R-meeting`) stay.
  - The note's heading on the phone is now "# Session note: …" (`goldfish/daemon.py`, the workbench's Device tab, `v01/note-format.md`, two tests).
- **Hero line:** "better" stays put. The verb before it and the noun below it flip every 5 s: Have / conversations, Make / decisions, Work / together, Ask / questions, Find / answers. The last two pairs are suggestions in the same pattern. The verb's slot eases to each verb's width, so "better" glides rather than jumps. Every 10 s a small goldfish rises from behind "better", looks round, swims along it and dives back. Screen readers get all five lines as text; with reduced motion, the first pair stands still and the fish stays hidden.
- **Checked:** in Chrome at desktop and narrow width, a full cycle of all five pairs and the fish. The workbench spec, the Session tab and the new price cell were checked in Chrome. Unit tests 258 passed; validation 14/14.
- **Then:** Rafa, verbatim: "ok I opened in mobile and clicked listen on the animation and then it didn't hold still but jumped up and down".
  - **Cause:** the note timeline above the model loops, and a forgotten bubble swapped its list for a one-line stamp, so the section changed height (1102 to 1222 px at 500 px wide) and everything below it moved. Desktop Chrome hides this by scroll anchoring; mobile Safari has none.
  - **Fix:** each bubble's list and stamp share one grid cell and swap visibility, so the bubble keeps its height. Measured over 20 s of playback at 500 px: every section and the page height (9186 px) stay constant.
- **Then:** Rafa, verbatim: "I think we're missing a section under the Hero that showcases the product. And in "Experience Goldfish" we should automatically type examples and move the "Say" input above and have it create 3-5 different ones and cycle through. The product showcase should be like how Apple does it with a high quality "photo-realistic" render and the specs, maybe have the goldfish sitting on a table, I'm sure there's some way you can generate a photo of a Goldfish elegantly placed on a table in a workshop room? Or find a photo and then add the Goldfish on it?" Then, on the credits: "we should add that somewhere else not in the footer, maybe just small italics light font briefly under each image or better yet in the image metadata instead of on the site".
  - **Meet Goldfish:** a new section under the hero: one large render, then eight specs as big numbers (10 voices, 50 lights, 2 microphones, 0 radios, 3 · 30 · 365 days, 1 tap, all day on USB-C, 266 g). Every number is from `01-concept/landing/goldfish-facts.md`. The caption first said it is a render and that no unit has been built; Rafa asked for "something more user friendly", so it now reads "Shown as designed. Real photos come once the first units are built." Then Rafa: "Remove "Shown as designed. Real photos come once the first units are built." it's not necessary, since the site is already saying notify me on launch". The caption is gone; the image's EXIF still says it is a render.
  - **The render:** Blender 5.2 (installed with Homebrew) and Cycles on the GPU, from `site/landing_render.py`. It reads the printed meshes and the 50 LED positions from the CAD, so the shape and the lights are the design's own. The ring shows two seats' arcs (green, orange) through the frosted band, and the eye is lit. The table is procedural oak; the room behind is the page's sticky-wall photo, out of focus. 2400 × 1500 px, 384 samples; page copies at 2400 and 1200 px.
  - **Say it your way:** the input moved above the result. Five examples type themselves in turn, are said, and show what Goldfish keeps; each lands on its intended level (checked with `GF.parse` in the page). A click on a chip or the input stops it; it starts again after 15 s.
  - **Credits:** the photo credits left the footer. Each photo carries its credit in EXIF (Artist, Copyright, ImageDescription); `PHOTOS.md` keeps the list. The machine-generated label for the page copy moved to the small print under the research quotes, as AGENTS.md requires it on the product.
  - **Checked:** in Chrome at desktop width, and in headless Chrome at 500 px. Validation 14/14.
- **Then:** Rafa, verbatim: "For the stats, we should focus on the job, so "For up to 10 participants" or whatever, not sure how Apple does it, but talk about the outcomes then the tech underneath so like how the microphones work in a room, then the 2 microphones and spec?" and "This "It sits in the middle of the table, listens to everyone, and shows each person what it keeps." also has verb at the end, probably needs a better subtitle more like Apple".
  - **Specs:** each of the eight now reads outcome, then how, then the spec: e.g. "Hears everyone at the table" / "Two microphones listen across the table and work out which way each voice comes from." / "2 microphones · up to 10 voices". The microphone line says direction, not identity: the two microphones measure the angle (ADR-0015); telling voices apart is voice clustering.
  - **Subtitle:** "A quiet listener for the middle of the table. Everyone stays in charge of their own words.", one sentence per line.
  - **Then:** Rafa, verbatim: "Maybe make the render like a full side by side and put "Meet Goldfish" subtitle and a paragraph in the image itself?" The render now runs edge to edge; on wide screens the heading and subtitle sit in its top, over a fade from the page white into the blurred room. On phones the heading sits above the image.
  - **Then:** "For What facilitators already know we should use the speech / thinking bubbles like the brand". The three research quotes are now thought bubbles: rounded, with the two trailing circles of the note timeline and the logo, and the source below each.
  - **Then:** "for the features like "Hears everyone at the table" can we make sure they are all either 1-line or two lines and align their contents so they look consistent?" Every title is now two lines with a set break ("Hears everyone / at the table"), and each feature's title, text and spec share rows across the grid (CSS subgrid), so they line up at desktop and phone widths.
- **Then:** Rafa, verbatim:
  - "For the Q&A, the answers should be assuming the product exists so it feels real in the demo, so "How good are the notes?" should answer with what the notes keep and maybe an example excerpt output from an transcribed meeting using the software / firmware"
  - on "Will people really speak more freely?": "should be more like about how Goldfish helps people have their recording preferences noted live during the discussion, helping people talk in a more natural manner while being able to continuously / live remove sensitive topics or their own names from comments."
  - "For "Does it work for online sessions?" it should talk about how Goldfish is designed for in person conversations etc. Instead of a "No""
  - **Done:** "How good are the notes?" shows part of the note the real firmware wrote to the simulated tag after the roadmap session (`goldfish.meeting.run_script`): decisions, gist and who said what, each with its forget date, and the line that Bob's off-the-record aside is not in it. The paraphrases in that note are the hand-written stand-ins in `meeting.json` (p, g, o); the filing, levels and dates are the firmware's. The time is left out, as the simulator's clock starts at midnight.
  - Then: "Remove "That's what it's for. Nobody has to decide anything up front:"". Done; the answer starts "Goldfish notes each person's preferences live".
  - The other two answers are rewritten as asked. Two lines that said the product isn't built (the timeline's small print, and "No built unit has been checked for radio yet") are trimmed, for the same reason. The workbench and BUILD-LOG keep the real status.
  - Also: the workbench battery line now says "an all-day workshop" instead of "a 72-hour conference", and ROADMAP.md's trials are facilitated sessions instead of conferences. The workbench market comparison is being redone (Rafa: "comparator products should be about discussion facilitation and recording, not like the glasses etc.").
  - **Market research, redone** (background agent; Rafa: "Is the workbench text updated based on all the latest product research? Feels like the main market research is wrong since comparator products should be about discussion facilitation and recording, not like the glasses etc."):
    - `01-concept/market-comparison.md` now compares what a facilitator or team uses to capture an in-person discussion, 14 options checked on vendor pages on 2026-10-16: Otter, Granola, Fireflies, Plaud, Teams Rooms + Copilot, Zoom Rooms + AI Companion, Owl Labs, Jabra, Cortico Fora (MIT CCC), Mentimeter/Slido, Miro, a Sony voice recorder, and a human note-taker or the Chatham House Rule. The wearables and glasses are gone, with a note on why. `v01/research/market.md` gains a dated section; the old one is marked as the earlier framing.
    - Workbench §2 stats: 33.4% of US workers have an AI notetaker in their meetings, 25.1% of those were never asked (Kolmogorov Law and Pollfish, n = 500, July 2026; a law-firm survey, ±7.6% on the 167), 74% of remote-capable US employees work on-site or hybrid (Gallup, May 2026), and 0 of the other capture tools compared forget by default. Sources are now printed under the stats. A proposed 84% "change what they say when recorded" (a vendor blog, no sample size, conflicting dates) was left out.
    - Workbench §3: the table compares Goldfish with Otter, Teams Rooms + Copilot, Plaud and Cortico Fora on where speech goes, per-person control and forgetting by default. Option A now licenses to makers of recorders and meeting-room systems, not wearables.
    - Found: the Stacker link behind the 25.1% returns 404; the comparison cites the law firm's own page.
- **Then:** Rafa, verbatim: "For the render banner of Goldfish can you add a pencil and marker around it so people get a sense of size intuitively". The render now has a pencil (about 17 cm: hexagonal yellow body, sharpened cedar tip, metal ferrule, pink eraser) in front of Goldfish and a whiteboard marker (about 14 cm, blue cap) beside its tail, both at real size, with the camera pulled back to fit them. The image links carry `?v=2` so cached copies refresh.
- **Then:** "Instead of "Notify me on launch" scroll to section can you just make it a pop up / drawer on mobile so user doesn't feel "jerked" down to the section? Same for the footer button". Every "Notify me on launch" (hero, top bar, footer) and the FAQ's "Sign up" link now open a sign-up sheet in place, a `<dialog>`: a drawer from the bottom on phones, a small pop-up on wider screens. It closes with ×, Escape or a tap outside, and the page doesn't move. Checked in Chrome (open, empty submit, valid submit, Escape; scroll position unchanged) and in headless Chrome at 500 px.
- **Then:** questions on the business, answered in the conversation and not stored as numbers in the repo: the realistic market, viability, prior attempts (Cortico's Hearth device, Microsoft's conversation-transcription kit, on-device notetaker apps, Mycroft), how the product changes as models improve, and upgrades as revenue. Then Rafa, verbatim: "So you could extend the business model with mod sales, like a language pack, specific industry skill listening, etc." and "Yeah, let's add this to the product roadmap and planning, no financials tho, just product and reasoning, and suggestions for customer outreach and so on".
  - **ADR-0023 (Mods):** add-on packs installed over USB-C with an offline licence. A mod can only lower; the note names the active mods; no emotion or sentiment inference; the firmware and the checks stay open. First packs: German, Research, Facilitator.
  - **ROADMAP.md:** a mods track and a customer-outreach plan in the recommended roadmap. Outreach covers segments in order (works councils, researchers, facilitators, then sensitive internal sessions), where to meet them, a design-partner program, paperwork for IT and works councils, waitlist segmentation, and what each pilot must answer. No financial figures, as asked.
  - **BACKLOG.md:** B-81–B-86 (mod framework, the hostile-pack check, mods in the note, the three packs) and B-87–B-90 (design partners, paperwork, waitlist segments, Cortico).
  - **Workbench:** its small Markdown renderer flattened nested lists into run-on text. It now renders nested and numbered lists, and a lead line before a list, so the roadmap's new tracks read as written. Checked in Chrome.
- **Then:** Rafa, verbatim: "The 3D render animation is a bit janky, lights are not on the strip or consistent can you fix?"
  - **Cause:** `landing_model.py` read each LED's third value as its height, but layout.json stores the strip's tangent angle there (radians, -3.1 to 3.1). So the lights wandered up to 3 mm above and below the band. They were also pushed out a fixed 2.2 mm from the facing point, while the band's outer surface is 4.3 to 8.1 mm from the LEDs, so some floated and some sank.
  - **Fix:** every LED now sits at the strip's height (`strip.z`). The model data carries where each LED's ray leaves the band's outer surface and the surface normal there (cast on the band mesh with trimesh), and the page draws each light as a thin tile flush on that point. On screen, each lit LED shows its color at full strength, with a small lift for the speaker's arc only when one stands out; before, the device's dim levels made arcs uneven and the red pink.
  - **Checked:** in Chrome, idle and through Listen in (Bob's green arc, Dana's yellow gist, the room off in red).
- **Then:** Rafa, verbatim: "In the "Say it your way" can you keep the typing but simplify the card to just be a notepad type of transcript with maybe a small pill box on the type of comment on the side? So like there's the transcript, and then in one of the lines you get to say it your way, then the notes change accordingly? I'm not sure, just feels overly complex right now, it's not simple enough to understand what's going on at a glance". The card is now a short session note: Alice's and Charlie's lines with a "Name me" pill, and Bob's line, where his request types itself ("Bob says …") and his note line and pill change to match, through `GF.parse`. The level chips and the separate result box are gone; one sentence below says what happened.
- **Then:** "Change from "You can always see what it's doing" to "You can always see how Goldfish is listening" I think that's better? Not sure, maybe you have a diff recormmendation". Kept as suggested: it covers both the eye (whether it listens) and the ring (how it keeps each person's words).
- **Then:** Rafa, verbatim: "For this project then, start another directory where you research and prioritize five grant submission possibilities like the OShea Foundation that would help bring the product to reality. This is educational hardware / AI hardware / New forms of memory so there may be programs available we can apply for", then "Like this one: https://www.osv.llc/fellowships".
  - **Done:** `11-grants/` (researched by a background agent): a ranked README and one file per pick. 1 Prototype Fund (closes 30 Nov 2026), 2 NLnet Restack (closes 3 Nov 2026), 3 O'Shaughnessy Fellowships (2027 window not yet announced), 4 Foresight Institute AI Nodes (closes 31 Oct 2026, weak fit), 5 Emergent Ventures (rolling). B-80: apply to the Prototype Fund.
  - **Found:** all five have rules on AI-written applications, so the drafts are outlines marked machine-generated and Rafa writes the submitted text. The Prototype Fund, NLnet and Foresight expect public code, which needs B-78 (the private repo) decided first.
- **Then:** "you need to review the site again, 3D model is janky and … is bad copy compared to the best practice we laid out … Basically across the board we also should "Show first, then tell" like how apple presents its products". The page was rewritten section by section:
  - **Hero:** Rafa's own line, "Just put a Goldfish on the table and focus on the discussion. It remembers what matters and forgets what's sensitive."
  - **Buttons:** "Notify me at launch" and "Experience Goldfish".
  - **Research label:** the quote cards now sit under "What facilitators already know", marked as research sources, not reviews.
  - **Speak freely:** "Anyone can say…" replaces "Every chair sets its own terms".
  - **Off the record:** "Press the eye, or just say so", in a goldfish-yellow panel that looks like the device.
  - **Section names:** "Listen fully. Goldfish remembers what matters." and "Remembers the decisions. Forgets the rest."
  - **Example:** a product roadmap session with Alice, Bob, Charlie and Dana, in place of the AI-agent meeting.
  - **Technology:** "Serious technology. Zero setup.", with one-line bullets.
  - **Sign-up:** "Be the first to know."
  - **Order:** each section shows its picture, demo or model before its words.
  - **The 3D model:**
    - the inner parts sat half a box off: `layout.json` gives each box by its low corner, as the workbench draws it;
    - the shells now get creased normals and studio lighting.
- **Then:** "is there a way to show the lights maybe simulating a discussion? Maybe have a button that says "Listen in" and use goog natural voice recording that loops", and "instead of user dragging the scroll, can't you make a better animation that loops?":
  - **Listen in:** an example roadmap session (`site/landing/voices/script.json`) plays on a loop. Goldfish's ring lights for whoever speaks, through the firmware's JS mirror: `GF.seatFrame`, and `GF.parse` for "keep my words to the gist", "off the record, please" and "Goldfish, back on the record". Captions show who is speaking and what Goldfish does.
  - **The voices:** the browser's for now. `site/landing_voices.py` makes AI voices once there is a key (B-79).
  - **The note timeline:** loops through today, 3 days, 30 days and a year, and can be paused.
  - **Copy:** "Notify me on launch" (Kickstarter's wording), and "Want one?" heads the sign-up.
  - **Testing:** headless Chrome shows a stale frame of the 3D canvas, so the ring was checked by reading the LED colors from the page (16 lit for two speakers).
- **Then:** placeholders off the page:
  - **Price:** Rafa, verbatim: "change the answer to be something normal like "The cost is estimated between $199-$499 per device. etc. etc. like acknowledge the ambiguity and suggest the sign-up as the CTA,,, try to remove / not use placeholder text where possible". The FAQ now gives that range as an estimate, says a ship date follows testing, and points to the sign-up. The price range is Rafa's, not derived from the BOM.
  - **Sign-up:** the four-row privacy table and the request preview are replaced by one line, "We only use your email to tell you about Goldfish. No tracking. Unsubscribe any time.", with a privacy-policy link.

## Decision records

| ADR | Title | Status | Summary |
|---|---|---|---|
| [0001](03-architecture/decisions/0001-no-radio.md) | No wireless radio in v1 | Accepted, amended by 0007 | No Wi-Fi, Bluetooth or active radio; certification as an unintentional radiator. |
| [0002](03-architecture/decisions/0002-retention-by-crypto-shredding.md) | Retention by destroying keys | Superseded in part by 0005 | Encrypted storage, expiry by key destruction in a secure element, fail closed on clock doubt. |
| [0003](03-architecture/decisions/0003-voice-in-plain-english.md) | Voice in plain English | Accepted | Lowering by ordinary phrases, instant, retroactive; raising by wake word plus objection window. |
| [0004](03-architecture/decisions/0004-physical-access-only.md) | Physical access only | Accepted; read path per 0007 | Memory read only by handling the device; no remote access. |
| [0005](03-architecture/decisions/0005-forgetting-by-tiers.md) | Forgetting by tiers | Proposed | Distil every lower level at sealing; one key per (day, level); decay is key destruction. Implemented with forward-secure chains. |
| [0006](03-architecture/decisions/0006-light-language.md) | Light, never sound | Superseded in part by 0008, 0009 | Mic-rail light that software can't override, plus a firmware-driven level indicator. |
| [0007](03-architecture/decisions/0007-passive-nfc-handoff.md) | Passive NFC hand-off | Accepted for v01 | ST25DV tag, RF off unless the read button is held (10 s max); note in the URL fragment. |
| [0008](03-architecture/decisions/0008-per-participant-resolution.md) | Per-participant resolution | Accepted | Each voice sets its own level; room lowering covers all; nobody raises anyone else; uncertainty falls toward less. |
| [0009](03-architecture/decisions/0009-flank-lights.md) | Light ring at the seam, one light in the eye | Accepted, amended | 41-LED ring split per participant, level by colour and fill; single mic-rail eye light; lit tap icon; power switch. |
| [0010](03-architecture/decisions/0010-each-voice-owns-its-record.md) | Each voice owns its record | Accepted | Pseudonymous session identity per voice; its own record; lower, raise, forget, shorten, erase; opt-in claim. |
| [0011](03-architecture/decisions/0011-protocol-and-profile.md) | Protocol and profile | Accepted | Rules live in the Resolution Protocol; Goldfish is one profile plus hardware bindings. |
| [0012](03-architecture/decisions/0012-rp-0-2.md) | Resolution Protocol 0.2 | Accepted | Provisional raises with a 30 s objection window, a smaller core, adoption hooks; fixes from three reviews of RP 0.1. |
| [0013](03-architecture/decisions/0013-eye-button-and-kit-build.md) | The eye is a button; a kit with no SMD | Accepted, gestures revised by 0015 | Press for off the record; room-off glow; through-hole and plug-in parts only. |
| [0014](03-architecture/decisions/0014-bigger-cell-and-continuous-power.md) | Bigger cell, continuous on a charger | Accepted | 4000 mAh, bq24074 power path, 154 × 78 × 33 mm, heat path, 50-LED ring. |
| [0015](03-architecture/decisions/0015-usability-and-build-round.md) | Usability and build round | Accepted | Safe presses, buttonless phone transfer, seat-facing arcs, buildable shell fixes from five simulations. |
| [0016](03-architecture/decisions/0016-flared-post-roots.md) | Flared post roots | Accepted, sizes revised by 0017 | R 2 mm fillet into a 45° flare where each board post meets the roof; the packer keeps parts out of it. |
| [0017](03-architecture/decisions/0017-print-practice-and-fasteners.md) | Print practice and fasteners | Accepted | Insert bores to the datasheet (Ø4.0, Ø7.2 posts), inserts and screw heads in the model, 245 °C inserts, 0.36 mm icon skin, lip lead-in, flexible glue for TPU, a test coupon. |
| [0018](03-architecture/decisions/0018-every-board-screwed-down.md) | Every board screwed down | Accepted | Captive-nut bosses and one M2.5 × 6 screw everywhere; straps for boards with no holes; a screwed battery deck; DC jack off; case 158 × 79 × 37 mm. |
| [0019](03-architecture/decisions/0019-firmware-in-the-site.md) | Firmware in the site | Accepted | The Meeting tab runs the real firmware in Pyodide; one meeting script shared with a flow test that checks the SPI bytes, motor pin and tag; the JS copy stays as the preview. |
| [0020](03-architecture/decisions/0020-chassis-and-lid.md) | A chassis and a lid | Accepted | Every board in the bottom shell, the Radxa on deck bosses; the lid joins through one latched 12-way plug; case 182 × 79 × 37 mm. |
| [0021](03-architecture/decisions/0021-pad-level-wiring.md) | Wiring at pad level | Accepted, amended | Every wire from a named pad to a named pad, from the vendors' board files; every wire, leg and joint inside the case; switches turned pins-in, case 182 × 85 × 37 mm; open lid routed; 30 mm working slack. |
| [0022](03-architecture/decisions/0022-landing-page.md) | A landing page | Accepted | The site root is a landing page for facilitators; the workbench moves to /workbench/, the reader stays at /r |
| [0023](03-architecture/decisions/0023-mods.md) | Mods | Accepted | Add-on packs (languages, domains, facilitation skills) installed over USB-C; a mod can only lower; the note names active mods; no emotion inference |

## How to rebuild from scratch

All paths are relative to the repo root. Each script below exists in the repo as of this log, except the here.now publisher, which lives in the local here.now skill.

1. **CAD environment** (Python 3.12, outside Documents):
   ```sh
   uv venv --python 3.12 ~/.venvs/goldfish-cad
   uv pip install --python ~/.venvs/goldfish-cad/bin/python build123d trimesh numpy pytest
   ```
   The wall-thickness check also needs `rtree`.
2. **Fit check** (pebble packer; `search` finds the smallest pebble that packs):
   ```sh
   cd v01/cad && ~/.venvs/goldfish-cad/bin/python pebble_layout.py
   ```
3. **Enclosure** (reads `v01/cad/pebble_spec.json`; writes STEP, STL and `layout.json` to `v01/cad/out_pebble/`; several minutes):
   ```sh
   ~/.venvs/goldfish-cad/bin/python pebble_enclosure.py
   ```
4. **Web meshes and print data** (decimated meshes in `out_pebble/web/`, `print.json`, `PRINT-SPEC.md`):
   ```sh
   ~/.venvs/goldfish-cad/bin/python make_web.py
   ```
5. **Wire routes** (about 10 minutes; writes `out_pebble/wires.json` and the cut list in `v01/wiring.md`; rerun after any layout change):
   ```sh
   ~/.venvs/goldfish-cad/bin/python wire_routes.py
   ```
5b. **Geometric review** (writes `out_pebble/review.json`):
   ```sh
   cd ../.. && ~/.venvs/goldfish-cad/bin/python v01/cad/review_geometry.py
   ```
6. **Electrical simulation** (writes `v01/sim/power_report.json`):
   ```sh
   python3 v01/sim/power_sim.py
   ```
7. **Protocol conformance** (optional on its own; step 8 runs it):
   ```sh
   (cd protocol/python && python3 -m resolution_protocol.conformance)
   node protocol/js/conformance.js
   ```
8. **Full validation** (unit and scenario tests, protocol conformance, fit, meshes, walls, the geometric review (wiring included), electrical simulation, light-ring and eye-gesture parity between Python and JS, the firmware flow (the site's meeting replayed through the daemon), site scripts parse, CDN versions pinned, docs agree with the generated data; writes `v01/validation.json`, which the site shows):
   ```sh
   ~/.venvs/goldfish-cad/bin/python v01/validate.py
   ```
9. **Site** (local single file with embedded assets, then the deployable folder `site/web/`):
   ```sh
   python3 site/build.py
   python3 site/build.py --web
   ```
10. **Deploy** to npc.here.now/demo-goldfish (the slug is already linked to that path; credentials stay in `~/.herenow/` and never enter the repo):
    ```sh
    ~/.claude/skills/here-now/scripts/publish.sh site/web --slug astral-aura-h93b --client claude-code
    ```

Older artefacts: the v01a pill rebuilds with `v01/cad/layout.py`, `v01/cad/enclosure.py` and `v01/cad/make_viewer.py` (into `v01/cad/out/` and `v01/viewer.html`); the earlier long-form page with `python3 site/build_v1.py`. The concept simulator ([07-simulator/index.html](07-simulator/index.html)) and the reader page ([v01/reader/index.html](v01/reader/index.html)) need no build.

## Open items

Every open item, with priority and definition of done, is in [BACKLOG.md](BACKLOG.md). The main ones as of phase 22:

- **Ordering blockers (P0):**
  - Radxa ZERO 3E stock (B-02);
  - the NFC tag in the EU (B-04);
  - foam (B-07).
  - The bq24074 charger has EU stock only on BerryBase's eBay shop until Kiwi's restock.
- **Firmware (P1):** not written yet:
  - ring encoder;
  - audio pipeline;
  - haptic patterns on the real motor (B-50);
  - power sense and keep-alive for a clean shutdown (B-44).
- **Tests on a real build (P1):**
  - heat (B-40);
  - a 1.2 m drop (B-43);
  - snaps over 50 openings (B-46);
  - eye-button feel (B-33);
  - standby drain (B-32);
  - the first-pixel level shift (B-39).
- **Design next (P2):**
  - a third mic so mirrored seats get their own arcs (B-45);
  - a tray so the cell goes in last (B-47);
  - one inter-shell connector (B-48);
  - reader localisation (B-49).
- **Research:** model energy efficiency (B-41).
- **Battery:** the 72 h stream on battery alone needs the v02 low-power capture architecture (B-21). On a charger it runs today.
- **Mobile microphone on Rafa's phone (B-17):** root cause unknown, waiting for the phone, browser and status message.
- **rafael.fyi:** its local repo has unpushed commits; the Goldfish post describes the eye as in ADR-0013.