| 1 | # One pet, attached views |
| 2 | |
| 3 | `codewhale-tui pet serve` owns the local pet independently of every window. |
| 4 | Watch starts it when needed. The native macOS package can start its bundled |
| 5 | owner as well. Closing a view releases its connection; the companion keeps the |
| 6 | identity, clock, particle field and recording. It contains no agent turn loop, |
| 7 | prompts, provider calls or second telemetry classifier. |
| 8 | |
| 9 | ```mermaid |
| 10 | flowchart LR |
| 11 | E[Existing Engine: accepted metadata] --> T[Terminal projection] |
| 12 | T --> O[Local companion: one PetNative world] |
| 13 | O --> S[Private checkpoint and immutable archives] |
| 14 | O --> W[Ratatui Watch / full habitat] |
| 15 | O --> B[Browser view] |
| 16 | O --> N[Native companion / mobile view] |
| 17 | O --> A[One optional audio device] |
| 18 | W & B & N -->|ordered interactions| O |
| 19 | ``` |
| 20 | |
| 21 | ## Try it |
| 22 | |
| 23 | From the repository root, build the existing product binary and native view: |
| 24 | |
| 25 | ```sh |
| 26 | npm --prefix pet ci --ignore-scripts |
| 27 | npm --prefix pet run sync |
| 28 | cargo build --locked -p codewhale-tui |
| 29 | PET_OWNER_BINARY="$PWD/target/debug/codewhale-tui" ./pet/macos/build.sh |
| 30 | ./target/debug/codewhale-tui |
| 31 | ``` |
| 32 | |
| 33 | In the running application: |
| 34 | |
| 35 | | Command | Result | |
| 36 | | --- | --- | |
| 37 | | `/pet` | Toggle pet mode for this terminal: the habitat takes the whole content viewport now and on every accepted turn | |
| 38 | | `/pet on` / `/pet off` | Enable or disable explicitly; `off` closes the view and stops automatic entry while the companion keeps the pet alive | |
| 39 | | `/pet appearance` | Configure the shared palette in the authenticated local browser studio | |
| 40 | | `/pet window` | Open the independently closable native macOS window | |
| 41 | | `/pet source` | Explicitly select this terminal session as the source | |
| 42 | | `/pet sound on\|off` | Request/release the companion's audio device | |
| 43 | | `/pet export` | Save a replay to this saved session's artifacts | |
| 44 | | `/pet status` | Mode, view, identity and measured terminal output counters | |
| 45 | |
| 46 | The pet has no workbar panel; the habitat is its only terminal view. Escape |
| 47 | leaves it without cancelling the turn or touching the composer draft. F6 |
| 48 | toggles sound, F8 opens the appearance studio, and F9 opens the native window; |
| 49 | reduced motion follows the shell's existing motion setting. Hints and key |
| 50 | admission use the existing shell binding table. The habitat uses the modal |
| 51 | stack; composer contents, transcript, selection and active Engine state stay |
| 52 | underneath it. Consent and approval views retain their existing priority. |
| 53 | |
| 54 | `CODEWHALE_PET_APP` can identify a locally built `.app`. `CODEWHALE_PET_HOME` |
| 55 | selects an isolated companion directory; its default is |
| 56 | `~/.codewhale/pet-shared`. `CODEWHALE_PET_PORT` chooses the initial loopback port |
| 57 | (default 4633; zero allocates an unused port for tests). A saved world retains |
| 58 | its selected port. `CODEWHALE_PET_GRAPHICS=braille` forces the text fallback. |
| 59 | None of these commands replace an installed global CLI. |
| 60 | |
| 61 | ## Appearance and work-to-result preview |
| 62 | |
| 63 | The habitat studio provides Ocean, Chalk, Graphite, Linen, Forest, Plum, Ember |
| 64 | and Cobalt presets. Background, upper light, particle color, activity colors, |
| 65 | brightness, dot size, glow and environment are independently configurable. |
| 66 | Export/import a versioned appearance JSON. Live appearance changes use the same |
| 67 | ordered, durable action contract and reach every attached view; they never enter |
| 68 | the simulation's interaction journal or change its replay digest. Native and |
| 69 | terminal hosts consume the shared background, material, dot size and glow. |
| 70 | |
| 71 | For an isolated, provider-free review, serve `pet/dist` and open |
| 72 | `shared.html?preview=1`. Compare all eight looks, toggle Still, and choose |
| 73 | **Preview work → result**. The canonical world drives the animation; the answer |
| 74 | is explicitly illustrative and no task or provider is run. The live studio's |
| 75 | preview detaches into an isolated world and offers **Return to live pet**. |
| 76 | |
| 77 | `/pet on` is opt-in for the current TUI |
| 78 | instance. The existing Engine's accepted turn-start event opens the full habitat |
| 79 | when no consent or other modal owns focus. Completion gives space to the actual |
| 80 | last assistant/error cell through the existing transcript renderer, with arrows |
| 81 | and page keys for scrolling. The terminal implementation composes the whale above |
| 82 | the answer for legibility; the browser preview demonstrates an overlaid fade. |
| 83 | Escape does not cancel the turn, replace the composer or remove the transcript. |
| 84 | Conversation text stays in the shell, never in the pet transport or recording. |
| 85 | |
| 86 | ## Legible work and spontaneous motion |
| 87 | |
| 88 | The shared frame adds a bounded, read-only `activity` projection. It reports |
| 89 | reading, searching, editing, command execution, tests, browser use, context |
| 90 | retrieval, response writing, coordination, errors and witnessed human waiting. |
| 91 | The exact printable tool identifier accompanies its label. An `exec_command` |
| 92 | receipt says **Running a command**: without inspecting its private arguments, |
| 93 | the pet cannot honestly call that a test, a build or a successful result. |
| 94 | At most four concurrent foreground cues and a fresh active-agent count are |
| 95 | projected. Tool/agent IDs, paths, arguments, prompts and outputs are omitted. |
| 96 | Cues expire after 800 ms without an appropriate heartbeat and clear on source |
| 97 | change, disconnect and restoration. A connected transport alone is not work. |
| 98 | |
| 99 | The preview selector demonstrates thirteen action/unknown states. A complete |
| 100 | 44-second work preview walks through eleven phases and then reveals the example |
| 101 | answer. Search sweeps, file marks, editing brackets, test activity rings and |
| 102 | parallel-agent marks use the canonical frame time, freeze under Still, and make |
| 103 | no claim about progress percentages, test counts or successful outcomes. These |
| 104 | extra drawn marks are currently a browser presentation treatment; terminal and |
| 105 | native prepared views share the exact action caption and canonical work forms. |
| 106 | |
| 107 | Determinism and variety live at different boundaries: |
| 108 | |
| 109 | * Physics, seven autonomous behaviors, persistent pod phases and sound use |
| 110 | fixed ticks and named seeded streams. Rendering never consumes those streams. |
| 111 | * Engine observations and human interactions arrive from outside the pet. |
| 112 | Accepted categories and ordered interactions are recorded. The same starting |
| 113 | checkpoint and input history reproduce the motion and score. |
| 114 | * Changing the input history changes the visit. Focus and Pulse therefore |
| 115 | influence a living trajectory while remaining replayable. Fresh worlds |
| 116 | currently use the same initial seed; per-pet random birth seeds are not added. |
| 117 | * Live action captions are ephemeral receipts, intentionally absent from replay |
| 118 | files to avoid persisting tool names. Replay proves the world and score, not a |
| 119 | historical transcript of exact actions. It cannot revive a stale request. |
| 120 | |
| 121 | The pet is a presentation of observed activity. It never chooses tools, invents |
| 122 | work, runs another agent loop, or blocks completion to finish an animation. |
| 123 | |
| 124 | ## Ownership and reconnects |
| 125 | |
| 126 | A process-lifetime OS lock excludes a competing owner. Private, anchored file |
| 127 | I/O rejects symbolic/hard links, replaced locks and external revisions. The |
| 128 | saved envelope holds one UUID, connection credential, selected source and |
| 129 | revision, ordered cursor, retained interaction receipts and canonical recording. |
| 130 | The first attached saved terminal selects its session only when the source is |
| 131 | `unattached`. Later terminals are views until explicitly selected. Session IDs |
| 132 | are represented by a short SHA-256 identifier. Multiple sessions are never mixed. |
| 133 | |
| 134 | Interactions require a client UUID, consecutive sequence and current source |
| 135 | revision. The world and receipt are saved before acknowledgement. Repeating the |
| 136 | same packet returns its receipt; changing a duplicate, skipping a sequence or |
| 137 | using a stale source fails. Storage failure refuses the action and preserves |
| 138 | the prior file. Receipts are retained for up to 4,096 interacting clients; they |
| 139 | are never silently evicted or re-applied after an owner restart. |
| 140 | |
| 141 | Engine metadata uses a separate ordered producer lease and the existing |
| 142 | `PetEngineTelemetry` bucketer. A fresh lease starts with sequence zero and no |
| 143 | historical events. Unknown fields, content payloads, oversized batches and |
| 144 | partial invalid batches are rejected. Gaps reset observation, and old epochs |
| 145 | cannot feed a restarted owner. A heartbeat proves transport availability; it |
| 146 | cannot invent observed work. A witnessed waiting request still requires the |
| 147 | existing typed shell's current waiting state. |
| 148 | |
| 149 | The world advances at fixed 30 Hz even with no views. Its periodic checkpoint |
| 150 | is once a second. Producer replies explicitly say `durable: false`: a crash |
| 151 | may lose the uncheckpointed observation interval. Acknowledged interactions |
| 152 | are durable. Recovery restores the last checkpoint and resumes at the first |
| 153 | unrecorded interval, clears producer/audio leases, and does not replay stale |
| 154 | sound or claim historical requests as current. Suspended-machine catch-up is |
| 155 | bounded to three ticks and marks a gap. Frame receivers independently reject |
| 156 | stale presentation after 800 ms. |
| 157 | |
| 158 | At 1,024 consumed buckets or 4,096 applied interactions, completed history is |
| 159 | published immutably before the active segment advances. Active storage is |
| 160 | bounded to 8 MiB; recovery exports stream out of QuickJS in chunks, up to |
| 161 | 64 MiB. Archives remain on disk and require explicit storage management. |
| 162 | |
| 163 | ## Graphics and audio |
| 164 | |
| 165 | The Rust view worker polls immutable snapshots, interpolates display positions, |
| 166 | rasterizes and compresses pixels outside the input thread. It overwrites one |
| 167 | latest frame slot; it does not queue movies. The maximum raster is 960×560 RGB. |
| 168 | Stillness uses a separate stable projection and cannot change the authoritative |
| 169 | simulation, score, journal or digest. Missing coverage remains hollow and |
| 170 | labeled; sleep dims it separately. |
| 171 | |
| 172 | Pixels require a successful Kitty graphics query and synchronized output. |
| 173 | Inline RGB uses zlib and base64 chunks of at most 4,096 bytes. One image ID per |
| 174 | process is replaced inside the existing Ratatui synchronized frame, with cursor |
| 175 | save/restore and deletion on layout changes, covered views and exit. The |
| 176 | transport follows the [Kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/). |
| 177 | |
| 178 | 60 fps is a presentation target, not a universal guarantee. `/pet |
| 179 | status` reports actual frames, elapsed output time, bytes/second, raster time |
| 180 | and write time for that visit. Slow encoding drops intermediate presentations. |
| 181 | The 30 Hz world does not depend on display rate. Unsupported terminals, tmux, |
| 182 | Sixel-only terminals and disabled synchronized output use braille. SSH can use |
| 183 | inline Kitty bytes if the remote terminal answers the probe, but bandwidth and |
| 184 | end-to-end SSH compatibility require separate measurement. No tmux passthrough |
| 185 | or Sixel pixel renderer is claimed. |
| 186 | |
| 187 | Sound is off by default. One view leases the companion's single `ffplay` |
| 188 | output, renewed every 500 ms and expired after two seconds. PCM comes from the |
| 189 | existing deterministic score at 48 kHz. Closing, hiding, quiet mode and stale |
| 190 | frames release or expire the lease. Late output is dropped through the bounded |
| 191 | PCM queue. FFmpeg is optional and never downloaded automatically. On mobile, |
| 192 | “Companion sound” controls the owner's speaker; it does not start a second |
| 193 | phone player. |
| 194 | |
| 195 | ## Existing habitats and mobile |
| 196 | |
| 197 | Wild, Event demo, imported replay and File study remain explicitly isolated. |
| 198 | Old browser IndexedDB recordings and per-session TUI |
| 199 | `artifacts/pet/habitat.json` files are retained. Apple File study continues to |
| 200 | use the former `live.json`; Android's File study keeps its `live` storage key. |
| 201 | Shared views never overwrite or merge those worlds. Import their recordings in |
| 202 | the standalone viewer to recover them. |
| 203 | |
| 204 | The local browser can join using its “Join shared pet” file control and the |
| 205 | private `connection.json`, or the terminal's browser command. The URL fragment |
| 206 | is removed immediately; the owner exchanges the bearer for an HttpOnly, |
| 207 | SameSite cookie. Host and Origin checks reject foreign origins. The listener is |
| 208 | only `127.0.0.1`; no CORS, LAN listener or unauthenticated remote route is added. |
| 209 | |
| 210 | Apple and Android Shared modes consume the same immutable frame contract. |
| 211 | They do not run their native simulation or native audio in this mode. Import |
| 212 | `connection.json` with “Join shared pet”; it is retained privately. On an Android |
| 213 | emulator or explicitly connected device, `adb reverse tcp:4633 tcp:4633` carries |
| 214 | its loopback traffic to the desktop owner. iOS Simulator shares host loopback. |
| 215 | Physical-device/cross-network use needs an explicitly established authenticated |
| 216 | loopback tunnel. The existing Runtime's authenticated transport and progress |
| 217 | contract are unchanged; no new remote deployment or compatibility claim is made. |
| 218 | |
| 219 | ## Verification |
| 220 | |
| 221 | `python3 pet/scripts/check-shared.py /absolute/path/to/codewhale-tui` creates a |
| 222 | unique fixture directory and checks owner exclusion, two attachments, metadata |
| 223 | validation, source changes, interaction ordering, view independence, audio |
| 224 | exclusion, crash recovery and storage conflicts. Fixtures are retained for |
| 225 | inspection. `npm --prefix pet run check` covers unchanged replay/score semantics |
| 226 | and presentation immutability. The product's `pet_watch` tests exercise real |
| 227 | transport encoding, braille, focus restoration, storage and PCM boundaries. |
| 228 | |
| 229 | Build/test receipts, actual native/browser screenshots, terminal visual |
| 230 | qualification and independent exact-SHA review are separate evidence. See |
| 231 | `QA.md` for the current measured results and outstanding limitations. |
| 232 |