← Ledger


title: Phase 7c-a close — durable agent.* audit + projection endpoint date: 2026-06-29 status: Accepted phase: 7c-a (rocky-side producer for external hatch consumer) predecessor: docs/decisions/2026-06-29-phase-7b-ralph-2-close.md spec: docs/specs/2026-06-29-agent-registration-v1.md plan: docs/plans/2026-06-29-phase-7c-a.md

Phase 7c-a close

Shipped

Locked decisions

1. Persistence = rolling JSON file (mirror of relay-audit)

Chosen at plan approval. The relay audit at src/lib/relay/audit.ts already carries the same shape (single-writer, atomic overwrite, 5000-entry trim), and the operator model is single-console-per-workspace. A real event journal (append-only log, offset tracking, compaction) belongs in Phase 8 when multi-consumer / multi-instance land — introducing it here is scope inflation for a producer surface.

Alternatives rejected:

2. Best-effort persistence: 202 even on write failure

Producer isolation is a hard invariant in Phase 7b — sniffer/stratt/driver close-notes all locked "emit failure is non-blocking; hatch outage never faults the producer". POST here is the receive-side of that same invariant. A disk-full at the audit sink must not cascade into a 500 that then propagates upstream via emitAgentHatch back into the producing route. Log-and-swallow keeps producer contracts intact.

The trade-off is silent-drop under sustained failure. Acceptable at this altitude — the projection endpoint's freshness monitoring is the correct place to detect drops, not per-request 5xx.

3. Read endpoint gates observer+, not admin

Mirrors relay-audit surface. Audit content is operational observability, not privileged data — read-broad, write-narrow is the console pattern.

4. Workspace filter falls back to agent_id prefix for non-registration events

Only agent.registered carries registration.owner.workspace_slug in-payload. agent.{invoked,completed,revoked} carry only agent_id. The spec's agent_id format is <workspace>-<scope>-<local>, so a prefix match on ${workspace_slug}- correctly partitions the tail events. Not spec-mandated; documented in-code and asserted via test.

Alternatives rejected:

5. Projection computed over full filtered set, not the trimmed events slice

?limit=<n> trims the returned events for wire-size control, but the projection is a summary — clamping it to the same slice would undercount agents that already have >n invocations. Tested explicitly.

6. Cap: limit clamped at 5000 (= audit ceiling)

No point returning more events than the audit ever holds. Non-numeric / negative / missing limit falls to default 500. Guarded and tested.

Deferred (external repos)

7c-b — hatch.devarno.cloud/agents consumer

Polls GET /api/relay/agent, projects to the AGENT/TYPE/OWNER/APPROVAL/CAPABILITIES/RATE table. Not in rocky-hq. This slice unblocks it by giving it a stable read surface.

7c-c — Polar entitlement + Airlock agent.approve

agent.revoked producer emission

Row retention on revocation is implemented in the projection (row kept, revoked_ts set) but no rocky-hq producer emits agent.revoked yet. Registry-teardown is an airlock/hatch-owned verb (7c-c). The projection supports it in advance so 7c-c can drive it without a rocky-hq change.

Event journal / stream API

GET /api/relay/agent polls a snapshot. A future streaming surface (SSE or WebSocket) would let 7c-b subscribe instead of poll. Not needed for the initial consumer.

Multi-workspace RBAC on the read surface

Today the read gate is role-based, not tenant-partitioned. Multi-workspace partition lands when multi-workspace itself does.

Acceptance verified

Phase 7 status after this slice

Slice Producer Consumer Notes
7a contracts agent-registration.v1 published closed 2026-06-29
7b ralph / relay / council / sniffer / stratt / driver / ralph-2 (drop) closed 2026-06-29
7c-a rocky-hq audit + GET projection endpoint closed 2026-06-29 (this)
7c-b hatch.devarno.cloud/agents polls external repo
7c-c Polar RATE + Airlock agent.approve external repos

With 7c-a landed, the rocky-hq side of Phase 7 is complete. Remaining Phase 7 work is entirely external.

Sources