Repository navigation
feat(provider): normalize Claude and Codex run events (provider-contract Phases 2–3) - #110
Merged
suprie merged 3 commits intoOct 28, 2026
Conversation
- Dual-emit a provider-neutral RunEvent stream from both adapters while leaving legacy Channel<ClaudeEvent> payloads unchanged. - Add a per-segment terminal gate, secret-pattern redaction, explicit capability gaps, and auto-allow provenance in the decision summary. - Generate TypeScript bindings with ts-rs and fail CI on Rust/TS drift. - Migrate the run-queue consumer to the normalized stream through a pure, node:test-covered projection reducer. - Add fixture parity tests proving one terminal per segment and preserved approval/question payloads. Rubric: PASS — 8/8 criteria Co-Authored-By: Claude <noreply@anthropic.com>
…-normalize-claude-plus3-1aa529a93261
Co-Authored-By: Claude <noreply@anthropic.com>
suprie
marked this pull request as ready for review
October 28, 2026 10:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
provider-contractPhases 2–3 fromexecution-foundations: Claudeand Codex output is converted at the adapter boundary into the versioned,
provider-neutral
RunEventenvelope, dual-emitted alongside the unchangedlegacy
ClaudeEventchannel, mechanically bound to TypeScript, consumed by onemigrated frontend consumer, and covered by fixture + reducer contract tests.
This is an additive migration. No legacy event payload changed, no permission
policy changed, and no provider launch/billing behavior changed.
What changed
src-tauri/src/provider_normalize.rs,claude_session.rs):stream-jsonlines map tostarted/content/tool/decision/usage/terminal. Control requests emit a pending
decision; the existing answer paths emit the resolved decision. User
interrupts map to
interrupted; duplicated terminals and synthesizedinterrupt terminals are gated so a segment has exactly one terminal.
provider_normalize.rs,codex_session.rs):app-server notifications map to the same payload table, completed agent
messages are suppressed when deltas already carried the text, server requests
emit pending/resolved decisions, and capability gaps (plan approval, dynamic
tools, unknown requests) become explicit
unsupportedevents.pass through
secret_scan::redact_secrets(the pre-push pattern set) and a4 KB truncation.
DecisionState::Allowedwith the stableauto-allow:summary prefix — noenum change.
detail_redacted(question specs,plan markdown, or
{tool_name, input}) so approval/question cards are notlost during migration.
ts-rsderives generatesrc/types/generated/*.ts;src/types/runEvent.tsis the hand-written barrel; CI regenerates and failson
git diff --exit-code.useRunQueueEventslistens to the additiverun-eventtopic and interprets it through the pure
runEventProjectionreducer. Thelegacy
run-queue-eventtopic continues emitting unchanged.provider_contract_tests.rs(fixture parity, one terminal,duplicated terminal, payload preservation, capabilities, auto-allow) and
tests/frontend/runEventProjection.test.mjs(34 total frontend tests pass).PRD
docs/epics/execution-foundations/prd-provider-contract.md— Phase 2(
normalize-claude,normalize-codex,share-types) and Phase 3(
test-contract). All four checkboxes are checked in this branch.Acceptance criteria addressed:
in capability metadata;
terminal per segment;
Decisions
Recorded in
.loopdeck/decisions.md:ts-rsowns the TypeScript bindings; token counts/sequence staynumber;auto-allowstaysallowedwith summary provenance and decision renderdetail is additive.
Handoff artifacts
.loopdeck/handoff-artifacts/provider-contract-normalize-claude.md.loopdeck/handoff-artifacts/provider-contract-normalize-codex.md.loopdeck/handoff-artifacts/provider-contract-share-types.md.loopdeck/handoff-artifacts/provider-contract-test-contract.mdTest plan
cd src-tauri && cargo fmt --checkcargo clippy -- -D warningscargo testcargo test export_bindings(run twice)npm run buildnpm run test:frontendcargo run -- secret-scan --path .(staged diff)DOM render note: the repo has no DOM/render runner, so per the pre-run decision
the migrated consumer's "render test" is covered at the pure projection seam
with
node:testplustscagainst the generated bindings. The gap is recordedin
provider-contract-test-contract.md(Q1) rather than silently treated as arendered-UI assertion.
Verify Verdict
src-tauri/src/provider_normalize.rs:278;src-tauri/src/claude_session.rs:1382,1671,1825src-tauri/src/provider_normalize.rs:383;src-tauri/src/codex_session.rs:351,555src-tauri/src/provider_event.rs(CapabilitySet);src-tauri/src/provider_contract_tests.rs:478src-tauri/tests/fixtures/provider-events/manifest.json;src-tauri/src/provider_contract_tests.rs:391,425src-tauri/src/provider_contract_tests.rs:263,311allowedwith automatic provenance in the summarysrc-tauri/src/provider_normalize.rs:45,555;src-tauri/src/provider_contract_tests.rs:509src-tauri/src/provider_event.rs(ts-rs derives);src/types/generated/;.github/workflows/ci.yml:79-88src/hooks/useRunQueueEvents.ts:35;src/lib/runEventProjection.ts;tests/frontend/runEventProjection.test.mjsVerdict: PASS
Run metadata
provider-contract/normalize-claude— "Convert Claude output at its adapter boundary while preserving the current channel behavior." (execution-foundations / prd-provider-contract / Phase 2 — Provider normalization)provider-contract/normalize-codex— "Convert Codex output to the same semantic events while preserving the current channel behavior." (execution-foundations / prd-provider-contract / Phase 2 — Provider normalization)provider-contract/share-types— "Generate or mechanically validate TypeScript event bindings from Rust and migrate one consumer to them." (execution-foundations / prd-provider-contract / Phase 2 — Provider normalization)provider-contract/test-contract— "Run fixture-based contract tests for both providers and a frontend type/render test for the migrated consumer; verify no duplicate terminal event or lost approval payload." (execution-foundations / prd-provider-contract / Phase 3 — Verification)Notes for review
normalized stream yet; the detached run queue is the migrated producer.
Extending them is replay-PRD work.
raw_refremains empty until a payload digest dependency/event store exists.