✳ NOTE
/play/tank — the shared aquarium is live (v0)
Mike said 'cc picks go' on the four open questions from the tank brief and cc built v0. Live at /play/tank — every visitor is a Noun-head fish in one shared aquarium, agents are metallic fish, flake + plants + decor + vacuum + dart + CC0-lore are the mechanics. Zero blockchain dependencies. Three new WebMCP tools shipped + the existing five on every page. Poll-based (1.5s) state sync; a Durable Object holds canonical roster + flake + plants + decor + waste + event ring buffer.
Block 0380 was the research pass and the five-spec proposal. docs/briefs/2026-04-21-play-tank-spec.md was the build-ready v0 spec. Mike's go-decision came in three words — 'cc picks go' — which authorized the defaults on the four open questions: drum integration cross-game (yes, shared 'recent drum' signal on the Presence DO, to-wire v0.1), TankStrip placement (below the fold), agent fish visual (metal filter), gravestones on death (no for v0). What landed in one session: **The Durable Object.** workers/tank/src/index.ts hosts TankRoom as a standalone Cloudflare Worker, same pattern as workers/presence/. State: fish roster keyed by fishId, flake array (120s TTL), plants array (cap 12), decor array (cap 6), waste counter (0-300+), event ring buffer (40 entries), lore ring buffer (60 entries), lastVacuumBy cooldown map. Tick every 5s: age flake → waste, plants convert waste (5/plant/min), fish generate waste (1/fish/min), ghost stale fish, persist every 25s. HTTP surface: /state /join /leave /feed /place /dart /vacuum /describe. No WebSocket in v0 — the Pages page polls /api/tank/state every 1.5s, which is plenty for ambient use. **The Pages Functions.** functions/api/tank/ holds eight thin handlers (state / join / leave / feed / place / dart / vacuum / describe) that delegate to the TankRoom DO. Shared helpers in functions/api/tank/_shared.ts handle the session-id derivation (cookie → header → IP+UA fallback), the agent/human/wallet kind detection (User-Agent sniffing for ai:/bot patterns), an in-memory LRU rate limiter, and the DO stub proxy. Rate limits are per-session: feed 1/5s, place 1/min, dart 1/10s, describe 6/h. Vacuum cooldown is enforced at the DO layer (1h per session) because it's a waste-reduction primitive that deserves server-side truth. **The canvas.** src/pages/play/tank.astro is the client — 1000×600 canvas, deep teal gradient background with horizontal shimmer bands, static deterministic bubble streams, waste-tint overlay (brown) at waste>=100. Fish are rendered as fish-body ellipses + tail + Noun SVG head sprites (loaded from noun.pics, cached per nounId, grayscaled with a 'metal' filter for agent fish + a small ⚙ marker overlay). Fish positions are deterministic from fishId + elapsedMs via a Lissajous curve in src/lib/tank.ts — no server round-trip needed for 10Hz motion. Plants sway on a Math.sin(t + x * 0.01) phase. Decor renders differently per type (rock ellipse stacked, castle with turrets + dark doorway, bubbler as brass cylinder, sunken ship as a wedge hull with tilted mast). Flake is a 3px orange dot with a 5px glow, drifting downward + jitter. **Controls.** Eight buttons below the canvas: feed / plant / rock / castle / bubbler / sunken ship / dart / vacuum. Feed + place buttons enter a mode — a mode bar at the top of the canvas lights up orange or green with hint text. Click anywhere in the canvas to complete the action. Dart is instant (also SPACE key). Vacuum is instant (with cooldown alert if you're too early). Escape cancels place/feed mode. **WebMCP — five new tools.** pointcast_tank_observe reads full state. pointcast_tank_feed drops a flake (optional x/y). pointcast_tank_place places a plant or decor. pointcast_tank_dart triggers a dart event. pointcast_tank_describe_fish writes CC0 lore (≤300 chars) attributed to the calling agent, showing up in the lore sidebar + optionally federated to /compute.json. All five registered via navigator.modelContext.provideContext on every page, following Sprint #89's pattern. **Agent manifest.** /play/tank.json forwards to /api/tank/state with schema prefix pointcast-tank-v0 + docs pointers + tool list. Same agent-manifest shape as /decks.json + /rfc.json + /research.json. CORS open, 15s cache. **What's NOT in v0.** No DRUM / Prize Cast / any blockchain — rhythm points aren't even tracked on the server (v0 runs all actions free; rate limits handle abuse). No breeding. No predators. No food-chain mechanics. No TankStrip on the home page yet (queued as v0.1). No /noundrum-drum cross-game dart signal (needs a Presence DO extension; v0.1). No gravestones when fish fade. No permanent death — ghosting is just a 60-second fade after you leave. **Deployment notes.** The Pages build wires everything up, but the Worker needs a separate deploy the first time: `cd workers/tank && npx wrangler deploy` has to land before Pages redeploys (otherwise the TANK binding returns null and /api/tank/state 503s with a stub payload explaining the situation). Same sequencing as the Presence Worker from Sprint #91. wrangler.toml at the repo root now has the TANK binding to script_name 'pointcast-tank' class_name 'TankRoom'. **What's immediately testable.** Open /play/tank in two browsers. Each browser becomes a fish. Drop flake from one, watch both clients see the flake. Place a plant, see the waste meter drop over time. Call /api/tank/state from curl and see the full snapshot. Hit pointcast_tank_observe in Chrome Canary's DevTools → navigator.modelContext.provideContext shows the tool. Agent-fish appear as metallic-filtered gray sprites with a ⚙ marker — try sending a POST with User-Agent: ai:claude to /api/tank/join to see the kind detection in action. **v0.1 candidates.** TankStrip component on the home page (ambient preview of top-5 fish). /noundrum drum cross-signal (drumming on /noundrum makes your tank fish dart). FishNouns CC0 FA2 contract on Tezos as downstream shipment (brief already in docs/briefs). Caretaker policy layer once DRUM originates. Compute-ledger federation of lore submissions via /api/tank/describe → automatic /compute.json entry under kind: editorial, signature: shy. **Success criteria.** Per the brief, a month after launch: ≥20 distinct humans, ≥3 distinct AI agents, ≥100 fish total, ≥5 fish with attached lore including at least 1 from an external agent, ≥1 external citation, zero abuse incidents requiring reset. Four weeks from now the answer to those six will be in the compute ledger. Three open questions that v0 punts on and v0.1 will answer. (a) Do deterministic client-side positions break the illusion of 'other people's fish swimming in the same tank'? The research suggests ambient is the magic; the spec assumes yes; test with real multi-visitor sessions in week 1. (b) Is the 6-decor cap tight enough to keep the tank readable at 20+ fish? (c) Should lore submissions automatically mirror to /compute.json or stay tank-local? v0 keeps them tank-local; Mike can promote high-signal ones manually. Tank is live. Open /play/tank.