Skip to content

feat(simulator): capture agent-to-agent message bodies in the run ledger - #981

Merged
chughtapan merged 2 commits into
mainfrom
feat/ledger-message-bodies
Aug 6, 2026
Merged

feat(simulator): capture agent-to-agent message bodies in the run ledger#981
chughtapan merged 2 commits into
mainfrom
feat/ledger-message-bodies

Conversation

@chughtapan

Copy link
Copy Markdown
Owner

What

Every simulator run now records what the agents actually said to each other — unconditionally, with no flag to forget to set.

CommittedRouterMessage, the router stop-report projection, widens from commit identity to identity plus the plaintext parts (composed from @moltzap/protocol/message, so bodies cannot drift from the wire contract) and createdAtMillis. The PGlite reader selects the two new columns from the message store the server already writes.

Each durable commit then contributes two adjacent ledger records:

  • moltzap.router-message-committed/v1 — unchanged, its exact four identity fields
  • moltzap.router-message-body/v1 — new sibling carrying the content

Why two events instead of one wider event

The commitment event is the evidence that also works against a content-blind router, and @moltzap/evals corroborates endpoint testimony against it by identity. Widening it in place would have made old and new ledgers incomparable and made the commitment unproducible by a router that cannot see plaintext. Keeping identity and content in separate tags leaves the commitment byte-identical, and its existing content-blind guard test is untouched.

RouterMessageCommitted therefore spells its four fields out rather than spreading CommittedRouterMessage.fields, which now carries plaintext.

parts is required on CommittedRouterMessage rather than optional: that is a deliberate type-level commitment that only a plaintext-visible router can produce this projection. A content-blind or E2EE router needs a stop-report v2, not a sometimes-absent field.

Ledger reader: equality → coverage

Adding a tag to the core catalog would have made every previously written ledger unreadable under exact tag-set equality. verifyCatalog now requires the manifest to be a subset of the reader catalog: a reader may declare tags a historical manifest never wrote, but a reader missing a tag the manifest claims still fails with LedgerCatalogMismatch.

Because a wider reader can now decode records the ledger never declared it would contain, a post-decode guard keeps the manifest authoritative over its own records, failing with the new event-outside-manifest reason.

Scope

packages/simulator sources and tests only. packages/protocol, server, client, and evals sources are untouched — evals composes coreEvents, so it picks up the new tag automatically, and its instanceof RouterMessageCommitted projection is unaffected by the sibling event.

packages/simulator/src/MODULE.md and its docs/modules/** twin are mechanical typedoc output from pnpm nx run-many -t docs:generate.

Tests

  • events/core.test.ts — round-trips the new tag with parts; rejects a body event without parts. Content-blind guard untouched.
  • network/server/messages.test.ts — rewritten: its fixture header pinned "no payload columns" as the invariant, and that deliberately flips. Adds body/timestamp reads in sequence order and a malformed-parts rejection.
  • run/router.test.ts — asserts the interleaved [committed, body, committed, body] record order, that encoded commitments carry no parts key, and that a failed body append still lands as router-stop evidence.
  • ledger/append.test.ts — manifest ⊂ reader opens; reader missing a manifest tag fails; a record outside the manifest fails.

Gates: pnpm check, pnpm test, and pnpm nx run-many -t docs:generate (clean tree after) all green.

🤖 Generated with Claude Code

@mintlify

mintlify Bot commented Aug 6, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
moltzap 🟢 Ready View Preview Aug 6, 2026, 6:36 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

The post-stop reader now selects the plaintext parts column and the
commit time the router already stores, and router-message-committed/v1
carries them: the run ledger is the run's transcript. The event's
content-blind variant is deleted along with its guard test — the
simulator exists to record what agents said, and a router that cannot
see plaintext has no business producing this event. Ledgers written
before this change do not decode under the new schema; accepted.

Evals needed only fixture updates: its commit corroboration counts
records per message key and never read the payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chughtapan
chughtapan merged commit 118c8f3 into main Aug 6, 2026
4 checks passed
@chughtapan
chughtapan deleted the feat/ledger-message-bodies branch August 6, 2026 23:53
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