Skip to content

Agent presence stage 1: schema, ingest route, Claude Code reporter - #22

Merged
lladawn merged 3 commits into
mainfrom
dev
Aug 13, 2026
Merged

Agent presence stage 1: schema, ingest route, Claude Code reporter#22
lladawn merged 3 commits into
mainfrom
dev

Conversation

@lladawn

@lladawn lladawn commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Stage 1 of the agent-collaborator prototype. Gets real agent state out of the tools a team already runs and into the database, deterministically.

No world, no realtime, no MCP yet. This stage exists to answer one question before anything expensive gets built: does seeing agent state beat reading it?

Why hooks before MCP

Mumbl will ship an MCP server — it's the breadth play and the line in the pitch. But an MCP tool only reports when the model chooses to call it, which costs tokens and degrades during exactly the long tasks you most want to watch.

Claude Code hooks fire deterministically. In particular Notification fires when Claude is waiting on a human — which is the blocked state, the one a terminal buries and a room can surface. Both end up as adapters onto one ingest endpoint.

What's here

Piece Path
Schema 0004_agent_presence.sql, 0005_agent_event_occurred_at.sql
Write path app/api/agents/ingest/route.js
Helpers src/server/agentPresence.js
Reporter scripts/mumbl-report.mjs
Provisioning scripts/agent-space-create.mjs
Setup docs/agent-presence-stage-1.md

agents is where a collaborator is now; agent_events is what it has been doing. Written together, so the world can never show a state with no trail.

Tables are deliberately separate from spaces — that's the anonymous Slack/room product, and entangling two products with different buyers makes both harder to change.

Ordering bug caught during testing

Hooks fire faster than the ingest round-trip. Firing five concurrently, created_at ordering put Stop first — nonsense. Events now carry a reporter-supplied occurredAt; implausible client clocks (>5min future, >24h past) fall back to server time. 0005 is separate because 0004 was already applied to staging, and editing an applied migration would leave the two databases silently divergent.

Safety properties

  • The reporter runs inside Claude Code's hook path, so it never blocks and never fails the session: bounded fetch, all errors swallowed, always exits 0.
  • current_task and event detail carry repo, branch and command detail, so both are encrypted at rest via the existing encryptContentFields path.
  • RLS on with no policies — service role is the only writer.
  • Rate limited per space (300/min).

Deliberate departure

This is the first thing that will need realtime, which docs/free-tier-compromises.md currently rules out. Stage 1 doesn't break it yet — flagged in the doc so stage 2 makes that call knowingly.

Verified

End to end against staging: real hook payloads → reporter → ingest → encrypted rows → decrypted back out ("Claude needs your permission to run: npm run deploy" as the blocked reason). Migrations applied to staging; test data removed. npm run build passes.

⚠️ Migrations 0004/0005 are on staging only — production not yet migrated.

Known limits (documented)

  • Agent status is last-write-wins by arrival, not occurred_at
  • No read path yet — nothing renders this
  • Realtime needs RLS read policies in stage 2

🤖 Generated with Claude Code

Stage 1 of the agent-collaborator prototype. Gets real agent state out of the
tools a team already runs and into the database, deterministically. No world,
no realtime, no MCP yet — this exists to answer whether seeing agent state
beats reading it before anything expensive gets built.

Hooks rather than an MCP tool first: an MCP tool only reports when the model
chooses to call it, which degrades during exactly the long tasks you most want
to watch. Hooks fire deterministically, and Claude Code's Notification event
fires when it is waiting on a human — which is the blocked state a terminal
buries. Both end up as adapters onto one ingest endpoint.

- agent_spaces / agents / agent_events, deliberately separate from `spaces`
  (different product, different buyer). RLS on with no policies, so the
  service role is the only writer.
- One write path at /api/agents/ingest, bearer token per space, rate limited.
- current_task and event detail are encrypted at rest through the existing
  encryptContentFields path — they carry repo, branch and command detail.
- scripts/mumbl-report.mjs runs inside Claude Code's hook path, so it never
  blocks and never fails the session: bounded fetch, all errors swallowed,
  always exits 0.
- scripts/agent-space-create.mjs provisions a space and prints the token once.

Events are ordered by a reporter-supplied occurred_at, not created_at. Hooks
fire faster than the round-trip: five concurrent hooks were inserted in an
order that put Stop first. Implausible client clocks fall back to server time.

Verified end to end against staging — hook payloads through the reporter to
encrypted rows and back out again. Test data removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mumbl-app Ready Ready Preview Aug 13, 2026 7:52pm

Task strings are encrypted at rest, so the Supabase table view shows
ciphertext — there was no way to see whether the signal was any good.

The watcher is also the control group for the experiment: it is the list the
pixel world has to beat. If glancing at a room does not tell you something
faster than this text view does, the spatial view is not earning its keep and
stage 2 is not worth building.

Orders by occurred_at, never created_at.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The demo was one dark room of identical sprites. It is now a walkable
office you read as a place:

- Six zones on a 1440x960 plate with a camera that follows you: meeting
  room, bullpen, lounge, cafe, rec room and reception, each with its own
  floor surface and a name label.
- Depth sorting keyed to each object's bottom edge, so you walk behind
  the tall things and in front of the low ones.
- Every agent is a different person. Build, hair, outfit and accessory
  are independent traits on the 14x20 grid, so no two collaborators are
  the same sprite recoloured, and each desk carries a personal object.
- Proximity interaction: walk up and press E. E, Escape and clicking the
  agent all drive one open/close state, so they cannot disagree.

Palette across the demo and the landing is now soft daylight pastel,
with contact shadows to keep the furniture on the floor. The front-page
theme is renamed shell-dark -> shell-soft to match what it now is.

Two new visuals carry the argument on the landing: a looping animation
of the product's loop (ask -> a collaborator walks in -> takes a desk ->
working -> done), and a list-vs-room comparison that shows the blocked
agent buried in a log and obvious in a room. Both are CSS/SVG and both
respect prefers-reduced-motion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lladawn
lladawn merged commit a45508f into main Aug 13, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant