Skip to content

feat(managedstream): emit the clean ledger-ingest v1 wire contract - #448

Draft
tumberger wants to merge 1 commit into
jens/eng-608-pin-ledger-ingest-v1-contractfrom
jens/eng-608-clean-v1-producer
Draft

tumberger wants to merge 1 commit into
jens/eng-608-pin-ledger-ingest-v1-contractfrom
jens/eng-608-clean-v1-producer

Conversation

@tumberger

Copy link
Copy Markdown
Contributor

What

Stacked on #447. Moves the daemon's producer onto the published ledger-ingest v1 contract — typed records, clean field names, JCS-hashed receipts — with a deterministic drain path for pre-upgrade backlog and no error-driven form fallback.

How

  • Typed wire model + mapper (internal/ledgeringest): the single legacy→v1 name authority. Renames (canonical_event_type→event_type, decision_at→decided_at, *_json names dropped, tool_use_id+tool_call_id collapsed), removals (fields no producer populates), closed-vocabulary guards, and decided-row mirrors derived from the decision fact itself so the server's cross-checks can never disagree.
  • Clean receipts at creation: payload/proof shape, empty members omitted, stored payload is the RFC 8785 (JCS) bytes the hash commits to, unsigned receipts omit the signature entirely. A payload_form column (additive migration) marks clean rows. Local VerifyReceipts chain verification is unchanged for old and new receipts alike.
  • Session lifecycle stays local: session.start/end rows no longer mint receipts and never reach the wire; session records carry open/close state, as the contract specifies.
  • Per-page deterministic form selection: a page containing any pre-cutover receipt or fact-less decided action ships on the legacy form; pages of post-cutover rows (and heartbeats) ship v1. That drains upgrade backlog — including rows written during a temporary downgrade — with no cutover marker and no retry-as-legacy. A v1 409 replay conflict is isolated like an oversized page (shrink → skip) instead of retrying identical bytes forever.

Tests

  • Transport contract test: captures real flush bytes, decodes them untyped (a typed struct could hide a dropped field), validates against the pinned schema from feat(ledgeringest): pin the published ledger-ingest v1 contract bundle #447, asserts no legacy name leaks and no session rows on the wire; heartbeat covered too.
  • Pre-cutover drain test: rows marked payload_form='legacy' ship on the legacy form with no hybrid markers.
  • Full go test ./... green.

Rollout

Ships only after the server accepts the v1 form in production (staging channel first). The legacy serializer stays for exactly one release to drain pre-upgrade rows; its removal is the follow-up PR.

🤖 Generated with Claude Code

…NG-608)

Moves the producer onto the published typed contract:

- internal/ledgeringest gains the typed v1 wire model and the mapper
  from exported store rows — the single legacy->v1 name authority on
  the CLI (renames, drops, closed-vocabulary guards, decided-row
  mirrors derived from the decision fact so they can never disagree).
- Receipts are now created in the clean payload/proof shape: sections
  omit empty members, tool_call_id is the sole correlation field, the
  stored payload is the RFC 8785 (JCS) bytes the hash commits to, and
  unsigned receipts omit the signature on the wire. A payload_form
  column marks clean rows; local chain verification is unchanged.
- Session lifecycle rows stay local: they no longer mint receipts and
  never reach the wire — session records carry open/close state.
- Contract-form selection is deterministic per export page, never
  error-driven: any pre-cutover receipt or fact-less decided action
  pins its page to the legacy form, which is how pre-upgrade backlog
  (including rows written during a temporary downgrade) drains; pages
  of post-cutover rows ship v1, as do heartbeats. A 409 replay
  conflict on the v1 form is isolated like an oversized page instead
  of retrying the same bytes forever.
- The transport test validates real flush bytes — decoded untyped, so
  a typed struct cannot hide a dropped field — against the pinned
  contract schema, proves session rows never leak, checks heartbeats,
  and covers the pre-cutover legacy drain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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