Date: 2026-07-07 Status: Accepted
Context
Phase P1a wires every rocky-hq producer to emit KAHN Scope's agent-shape transition stream (agent_run_start, agent_run_end) alongside the pre-existing agent-registration.v1 hatch triple. Plan: docs/plans/2026-07-06-phase-p1a-kahn-agent-emit.md. Motivation: rocky was the largest silent producer against kahn-hq's north-star (kahn-hq/docs/north-star/agent-fleet.md); every ralph run, relay call, sniffer analysis, council persona, and hearth driver invocation was invisible in KAHN Scope.
The plan defined four sub-phases (P1a-1 through P1a-4). During execution two additional slices emerged (P1a-3b, P1a-3c) to close a tenancy gap discovered at deploy time — a single global KAHN_INGEST_TOKEN cannot represent a multi-tenant fleet where each rocky workspace maps to its own KAHN tenant. Both are documented below as landed extensions, not scope creep — they were required for the sub-phases they extend to work in production.
Decision
Phase P1a is closed with all four planned sub-phases + two follow-on slices shipped, per the acceptance criteria below.
Landed sub-phases
| Slice | Producer | Repo / PR | Merged |
|---|---|---|---|
| P1a-1 | RALPH (Python) — vendor kahn_agent_emit.py + agent_transitions.schema.json; AgentJournal sibling to Journal; HTTP shipper env-gated |
rocky-hq/ralph#13 (6347ebd) |
landed |
| P1a-2 | RALPH terminal observer (TS) — console-side SSE watcher forwards run_end to KAHN |
rocky-hq/console#41 → parent d3a261f |
landed |
| P1a-3 | RELAY / COUNCIL / SNIFFER (Node) — shared emitter console/src/lib/kahn-agent/; persona-per-agent for COUNCIL |
rocky-hq/console#42 → parent d720382 |
landed |
| P1a-3b | Console per-workspace token resolution — KAHN_INGEST_TOKEN_<SLUG_UPPER> → KAHN_INGEST_TOKEN fallback → skip; KAHN_DEBUG=1 ship logging |
rocky-hq/console#43 (cb62c5d) → parent #111 (fbdd3f4) |
landed |
| P1a-3c | RALPH per-workspace token resolution — mirror console P1a-3b in AgentJournal |
rocky-hq/ralph#14 (712c406) → parent #112 (7319ece) |
landed |
| P1a-4 | HEARTH driver (Go) — KAHNEmitter sibling to hatch emitter; one KAHN run per RPC verb; same per-workspace token convention |
rocky-hq/hearth#8 (e914280) → parent #113 |
landed |
Per-workspace tenancy convention (locked)
Every producer resolves the KAHN Bearer at ship-time from:
KAHN_INGEST_TOKEN_<SLUG_UPPER>— slug uppercased,-/.→_. Example:iris-hq→KAHN_INGEST_TOKEN_IRIS_HQ. Matches the existingROCKY_POLAR_TIER_<SLUG>/ROCKY_AGENT_APPROVED_<AGENT_ID>env-prefix pattern.KAHN_INGEST_TOKEN— global fallback (OSS / local dev / single-tenant installs).- Neither set → skip ship entirely. KAHN 401s on missing Bearer; the round-trip is pure waste.
slug_token_env_key(slug) (TS + Python + Go) is exported for provisioning tooling. Every ship call site threads workspace_slug through; empty slug also skips ship (mirrors the console x-rocky-workspace header gating).
Fail-open contract (locked)
Every producer's ship path swallows transport errors, timeouts, non-2xx, marshal failures, and DNS errors. A KAHN outage MUST NOT fault:
- a rocky console request path (RELAY / SNIFFER / COUNCIL / ralph submit / terminal watcher / cancel routes)
- a ralph worker step
- a hearth RPC verb call
File sinks (ralph's agent-transitions.jsonl) are unaffected by ship failures — the durable journal is the source of truth; KAHN is a projection.
Debug logging (opt-in)
KAHN_DEBUG=1 on every producer logs ship completion status + no-token-resolved skips at INFO (slog.Info for hearth, Python logging.info for ralph, console.info for console). Off by default so a well-configured prod stays silent; a mis-provisioned workspace surfaces immediately in Fly / operator logs.
Acceptance criteria (met)
- All four planned sub-phases shipped (P1a-1..4) plus two follow-on tenancy slices (P1a-3b, P1a-3c).
-
iris-hqKAHN tenant provisioned in KAHN Cloud viakahn-hq/scripts/provision-tenant.py(tenant8d876813-2329-4284-ab89-58d16a6788bf, keyrocky-ralph-prod). - Smoke: 2-event NDJSON POST to
api.kahn.host/v1/ingest/agent-transitionsreturns{"accepted":2,"malformed":0,"runs":["smoke-p1a-3b"],"finalized":["smoke-p1a-3b"]}. - Fly secrets set on
rocky-ralph-prod:KAHN_INGEST_URL,KAHN_INGEST_TOKEN_IRIS_HQ,KAHN_AGENT_EMIT=1,ROCKY_WORKSPACE_SLUG=iris-hq,KAHN_DEBUG=1. - Test suites green: console (631 pass), ralph (77 pass, 1 skipped), hearth (agent + server + fake + kustomize + localdocker + integration under
-race). - Contracts unchanged — KAHN ingest wire is stable at the schema pinned in
ralph/src/rocky/_vendor/kahn/.upstream; no bump required.
Non-goals (deferred)
- Per-tool-call granularity in ralph. Substep-grain is enough for KAHN Scope's first fold. Individual SDK tool invocations ride on a later slice once
Runnersurfaces the SDK tool stream. Explicitly non-goal per P1a-1 plan. - Rocky-side producer registry mirror. The plan's follow-up ("registration entry mirroring
AGENT_REGISTRY") references a kahn-hq structure that does not exist inkahn-hq/docs/north-star/realignment-criteria.mdtoday. Follow-up parked until the upstream shape lands. - Hearth production traffic verify. Hearth is not yet deployed to prod (
console/src/lib/hearth/SS-08 wrapper owns tenancy today per hearth CLAUDE.md). P1a-4 real-traffic verify waits on hearth's first deploy. - KAHN Cloud consumer hardening. Ingest side is kahn-hq scope; P1a is producer-side only.
Evidence
- Plan:
docs/plans/2026-07-06-phase-p1a-kahn-agent-emit.md - Predecessor decisions:
docs/decisions/2026-05-10-kiln-federation-seam.md(federation seam),docs/decisions/2026-07-06-phase-7d-a-6-close.md(unblocks P1a). - Console
CLAUDE.md— P1a-3, P1a-3b sections (per-workspace token resolution +KAHN_DEBUGlogging + env table). - Ralph
CLAUDE.md— P1a-3c section (per-workspace tenancy + updated env table). - Hearth
CLAUDE.md— Phase P1a-4 section (KAHN Scope shipper sibling to hatch emit + env table).
Consequences
- Every rocky-hq producer now surfaces in KAHN Scope agents view when a workspace has its per-workspace token minted.
iris-hqis the first live tenant; additional workspaces onboard by runningprovision-tenant.py+ settingKAHN_INGEST_TOKEN_<SLUG>on the target host. - Rocky is no longer the "largest silent producer" against the kahn-hq north-star. The producer-side gap that motivated P1a is closed.
- Phase 8 (KILN federation) is now unblocked from the producer side. Federation still requires spec ratification per
docs/decisions/2026-05-10-kiln-federation-seam.md. - No SLA change on any rocky surface — every KAHN ship is best-effort. Adding more producers over time cannot regress rocky's request-path latency.
- One outstanding operational risk: no per-workspace token rotation story yet. When KAHN Cloud rotates a key, the operator must re-set the Fly secret manually. Rotation tooling parked until fleet size makes it painful.
Superseded-by
(none)