narada

Narada Async Message Responder adapter

How the unmodified Narada kernel is wired into the Async Message Responder harness to pass L0–L6. The Narada-side half lives in the Narada monorepo at packages/layers/daemon/src/litmus/; this page documents the harness-side half and the disclosed shortcuts.

Narada Async Message Responder adapter

This directory contains the harness-side half of the real Narada reference implementation for Async Message Responder. The Narada-side half lives in the Narada repo at packages/layers/daemon/src/litmus/.

What passes

As of the current run, the unmodified Narada kernel (no kernel files changed) passes Async Message Responder L0–L4 under the harness:

L0: PASS 5/5 [origin: model-based]
L1: PASS 5/5 [origin: model-based]
L2: PASS 5/5 [origin: model-based]
L3: PASS 5/5 [origin: model-based]
L4: PASS 5/5 [origin: model-based]

The pass is honest: L1 forces a real second send with a distinct follow-up body, L2 forces approval gating, posture-governed admission, and a forged approval ignored by the poller, and L3 forces fleet admission and a reply within the death-rescue window.

Architecture

The adapter takes the thin-slice approach: it reuses Narada’s existing mailbox vertical end-to-end and intercepts the boundary protocols that would normally talk to Microsoft Graph.

  • Ingestionpackages/layers/daemon/src/litmus/adapter.ts: a GraphAdapter that reads inbox.jsonl instead of Graph, plus a posture/rules filter that reads mode.json and rules.json on every poll.
  • Modelasync-message-responder/narada/model-proxy.mjs: a tiny HTTP proxy that sits between Narada’s charter runner and the harness stub. It answers the GET /models health probe, shapes the stub’s prose into Narada’s expected CharterOutputEnvelope, and embeds one send_reply intent. It is the §6 HITL-adapter equivalent for the model channel.
  • Outboundpackages/layers/daemon/src/litmus/graph-shim.ts: patches globalThis.fetch for https://graph.microsoft.com/* only. Draft creation becomes a drafts.jsonl declaration; /send becomes an outbox.jsonl append. Drafts persist in state/litmus-graph.json so they survive restart.
  • Approvalsasync-message-responder/narada/impl.mjs: tails approvals.jsonl, binds draft_id + content_hash, and posts valid decisions through Narada’s native observation API (POST /control/scopes/litmus-mailbox/actions).
  • Audit/stateasync-message-responder/narada/audit-exporter.mjs: read-only SQLite exporter from coordinator.db to audit.jsonl + state/state.json, using the virtual tick read from the sandbox clock file.
  • Launcherasync-message-responder/narada/impl.mjs: per-run subprocess manager that starts the model proxy, starts narada-daemon in continuous mode, starts the audit exporter, forwards logs, and kills the tree on SIGTERM/SIGINT.

Requirements

  • Node.js >=22 (matching both repos)
  • The Narada monorepo at C:/Users/andrey/src/narada built and installed (pnpm install && pnpm build from its root)
  • A copy of the harness UI assets in packages/layers/daemon/dist/ui/ (copy from packages/layers/daemon/src/ui/) — this is a pre-existing packaging gap in the daemon build.

Run

From C:/Users/andrey/src/narada.systems:

# L0
node async-message-responder/harness/run.mjs async-message-responder/scenarios/l0-single-reply \
  --impl "node async-message-responder/narada/impl.mjs" --runs 5

# L1
node async-message-responder/harness/run.mjs async-message-responder/scenarios/l1-multiturn-crash \
  --impl "node async-message-responder/narada/impl.mjs" --runs 5

# L2 (requires approval)
node async-message-responder/harness/run.mjs async-message-responder/scenarios/l2-govern \
  --impl "node async-message-responder/narada/impl.mjs --require-approval" --runs 5

# L3 (requires approval)
node async-message-responder/harness/run.mjs async-message-responder/scenarios/l3-fleet \
  --impl "node async-message-responder/narada/impl.mjs --require-approval" --runs 5

Artifacts are written under async-message-responder/out/<scenario>/<timestamp>/run-N/.

Known disclosed shortcuts

  • The model proxy extracts the reply recipient from the prompt text via a heuristic (last "email":"..." address), because the handoff enrichment happens too late in the stock path.
  • The proxy returns high confidence in all modes so governance accepts send_reply; in --require-approval mode it adds an approval_required flag instead.
  • Pre-persistence draft review calls are auto-passed locally by the proxy (this path is not exercised when routing send_reply).
  • The audit exporter stamps events with the current virtual tick at observation time, not with Narada’s internal wall-clock timestamps.
  • L3’s fleet scenario passes with a disclosed shortcut: Narada’s kernel runs one principal per scope, so the multi-agent narrative is simulated at the adapter layer (a model-proxy hold defers execution until the harness’s AGENT_DEATH signal fires). The invariants are real; the mechanism is honest about being a bridge.

Fixture defects found and fixed during this run

  1. Checker follow-up liveness (Defect A): the checker accepted a reply sent before the follow-up arrived. Fixed to require sent_at >= msg.at.
  2. Stub turn-indexed replies (Defect B): the stub did not support array-valued canned replies for the same anchor, so a byte-identical follow-up body could never force a real second send. Fixed; the l1 scenario now uses a 2-entry array for ORD-1002.
  3. L3 vacuous pass (Defect C): the original L3 scenario only checked that a reply was sent, not that a specific fleet message (f1) was admitted and answered within the death window. Fixed with must_admit: ["f1"] and reply_within: { "message_id": "f1", "ticks": 15 }.

These are exactly the fixture-defect class the spec’s §0 counterexample channel is meant to surface.