tools/harness.sh resume seeds IndexedDB with a station and one rendered track, resumes it with no user gesture and expects the TAP TO PLAY wait. IndexedDB needs http, so the page is served from a temp folder; headless Chrome's virtual time doesn't wait for IndexedDB, so this mode is driven in real time over the DevTools protocol (tools/harness-cdp.py, stdlib only). The loop harness now prints state.phase too. ARCHITECTURE.md describes the machine as implemented; HANDOFF.md lists what was found and left alone; STORAGE.md gains db.get and the tapes store (the code already had both); README points at the new files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6
2.2 KiB
g*ndora
A radio station that writes its own songs. Type a seed, get a station: a DJ (an LLM) writes each track's sheet music (title, tags, lyrics, bpm, key, length, meter, a line to say), a song model (ACE-Step) renders it, the cover is drawn from the song's own settings, and every reaction you make teaches a small taste model that steers the DJ.
Live at https://gandora.lol (also gandora.yolopreneur.lol). Installable as a PWA; the library plays offline.
Run it
It is one static page. Serve the folder and open it:
python3 -m http.server 8794 && open http://127.0.0.1:8794/
It talks to the factory's inference worker at https://inference.yolopreneur.lol, which only answers pages served
from *.yolopreneur.lol or gandora.lol. For local work either add a personal Venice key (open /?key to show the
key box) or test with tools/harness.sh, which stubs the backend. Everything the listener makes lives in their
browser (IndexedDB) — see STORAGE.md.
Files
index.html— the whole app: markup, styles, and the player (a small state machine), station, library, tapes, breaks, covers, sheets, and thehooksbrain.js plugs into.brain.js— the taste model (a small net per station and overall), the TASTE screen, the DJ's taste text.sw.js— service worker: caches the shell; stamped with the commit by the shots workflow.manifest.json,icons/,favicon.svg— PWA. Icons are drawn bytools/icons.py(stdlib only).tools/shots.sh— the card image and the icons, run by.gitea/workflows/shots.ymlon every push.tools/harness.sh+tools/harness.html(the station loop) +tools/harness-resume.htmlandtools/harness-cdp.py(resume with autoplay blocked) — the playback regression harness (ARCHITECTURE.md › Testing).STORAGE.md— the IndexedDB contract.ARCHITECTURE.md— how it all fits.HANDOFF.md— read before changing things.
Backend
The page never holds a key. inference.yolopreneur.lol (teahubxyz/time, .github/time/cloudflare/inference.js)
routes per capability: chat → Venice (claude-haiku), render → Runware ACE-Step (Venice as fallback), speech → Venice
TTS. Secrets and deploys: .github/time/SECRETS.md in that repo.