Eddie Runs the Board: Agent Orchestration Comes Alive
What we set out to do
The declared goal was an overhaul of Eddie’s fluid-ink avatar — the agent’s visual body, docked in the nav of every life-dashboard page. What the session actually became was a proof, end to end, that the agent layer can orchestrate real project work. The animation shipped too, but it’s the supporting cast.
Eddie as live orchestrator
The moment that reframed the session: from the Nexus page, I told Eddie “remove this page, add that to the todo.” About a second later he had filed a card on the shared Postgres kanban board. When I navigated to the board page, it had already refetched — Eddie’s tool dispatch fires a window CustomEvent (eddie:board-changed) that any open board island listens for. Minutes later, a Claude Code instance picked the card up, shipped the nav change, and closed the card.
That’s the full loop: human intent, spoken casually to an embedded agent → durable shared state → a different agent (a different vendor’s agent) executing → state closed. Later in the session Eddie auto-sorted a task onto the correct project’s board purely from context, and moved it across boards live while I watched from that board’s page. The board is genuinely multi-instance now — cards #58-61 were filed autonomously by a separate Claude Code instance working in the Voxlight project. The kanban board has quietly become a message bus between agents, with me as one publisher among several.
The builder-agent relay
The avatar overhaul itself was run as an orchestration experiment. Phase one: a 7-agent research fan-out — one code-mapper plus six dimension researchers — producing a 40-proposal “possibility wall” document. Phase two: five sequential builder agents (B0-B4) all working in one physics file. The coordination mechanism had two parts:
- Discovery hand-offs. Each builder’s final report included a “discoveries for the next bundle” section, which the orchestrator folded into the next builder’s brief. The agents never spoke to each other; the orchestrator carried forward only what mattered.
- A soak-test contract. A 12-test suite simulating 3 hours of runtime per config in ~25 seconds. Every builder had to leave it green. That suite, not code review, was the interface between agents sharing a file.
Roughly 1.1M agent tokens, 11 commits on that arc, zero merge conflicts. Sequential-with-handoffs beat parallel-with-merges for shared-file work, and the soak suite is the reason it worked: the contract was executable.
Self-discovery with a hand-built fence
Eddie gained api_docs (search the app’s own openapi.json, ~170 endpoints) and api_get (a GET-only executor with an allowlist that excludes /api/u, /api/flags, /api/agent, /api/query, and binary routes). The principle we articulated and enforced: knowledge can scale automatically; agency only by hand. Every new endpoint I ship grows Eddie’s read surface for free. Writes stay at five hand-curated tools: kanban add/move/delete, write_journal, create_audiomark.
Fabrication forensics, twice
Two incidents, both pipeline bugs masquerading as model behavior. First: the system prompt only rode step 0 of the ReAct loop, and gpt-oss forgot the protocol mid-conversation — leaked its reasoning channel and fabricated five plausible audiomarks (fake hex event IDs, marketing-voice prose) that were never in the DB. Fix: system prompt on every step, plus an answer sanitizer. Second: the regex tool-call parser handled one nesting level, missed a depth-3 api_get call, and the model invented “42 cached books” (real count: 4) for the call that never executed. Fix: a string-aware brace-walking JSON extractor replacing both the parser and the sanitizer.
An external anti-confabulation audit then found the through-line: prose reached the model lossy and unlabeled. Truncations now append an explicit … [TRUNCATED] sentinel with a prompt rule forbidding completion past it; caps went 300→700 per field and 4000→8000 per result; _safe_payload coerces JSON nulls (killing a live None[:300] TypeError class); consult_memory prefixes “About Wei:” server-side so the Honcho memory is consulted about me, never as me. Worth noting: the audit’s own smoking-gun claim (synthetic DB fixtures) was disproven against the database. Verify even sharp audits. Full writeup on the blackboard: “A Missed Parse Is a License to Fabricate.”
Watching the agent think
agent.py’s ReAct loop was refactored into a single async generator powering both the JSON path and a new SSE path (stream: true). Each tool dispatch emits an event the frontend renders as an orbiting droplet on the avatar — golden-angle placement, capped at three clusters — and the answer’s arrival gathers them back in. At sub-500ms per tool on Cerebras, you literally watch the agent think, beat by beat. It changes trust more than any latency number: a visible tool call is a claim you can count.
The reading page got the matching context upgrade: when you press enter, Eddie scrapes the transcript/epub paragraphs physically intersecting the viewport at that moment (active paragraph marked >>>), browse-mode aware. Ask about “this passage” and he sees what your eyes see, not where the audio playhead is. One snapshot per question — no standing surveillance.
Supporting work, briefly
The fluid avatar itself: a 62.8s idle loop killed via golden-ratio retuning of curl-noise wave speeds, a per-page posture system, karaoke “reading-room manners” (stillest posture while you read along — explicitly not a visualizer), and a randomized scatter-coalesce entrance. Best bug of the session: the intro animation tripped its own performance watchdog — forced 25fps plus a LUT rebuilt every frame meant the watchdog permanently downshifted to the fallback renderer at the 2-second mark, every time. The intro tripping its own circuit breaker.
Two operational lessons: Astro’s transition:persist means the persistent island never reloads on client-side navs — deploys don’t reach an open tab until hard refresh, which confounded debugging twice. And a GET-only smoke harness (scripts/test-endpoints.py) now fires all 138 GETs with auto-filled params and lists the 34 writes without firing them — the events table is the life record; a test that writes to it is itself a bug. 124/126 green.
What’s next
19 commits sit unpushed (manual pushes by policy). The obvious follow-ons: candidate write-tools graduating through the hand-curation gate as trust accrues, and more agents publishing to the board — the multi-instance pattern wants formalizing now that it’s been observed in the wild rather than designed.