Skip to content

Providers phase 2b: registry-owned transcript-entry mapping with kind routing + shadow comparison - #398

Merged
Juliusolsson05 merged 1 commit into
mainfrom
refactor/providers-phase2b-entry-routing
Jul 6, 2026
Merged

Juliusolsson05 merged 1 commit into
mainfrom
refactor/providers-phase2b-entry-routing

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Part of #394 (phase 2b). Builds on #395/#397.

What

  • New registry capability: createTranscriptEntryMapper() + extractProviderSessionId on RendererProviderCapabilities. Codex's mapper is stateful (the rolling turn cursor, with get/setTurnCursor so live ingest persists it across bursts); Claude's is stateless and carries the conversation/compact filter.
  • Kills the quadruplicated mapping: the codex quartet (marker/turn-cursor/map/stamp/terminal-clear) existed 3×, the claude unwrap+filter 4× — and the copies had already drifted (differing marker policies, differently-seeded cursors). Sites keep their genuinely site-specific policies (dedupe, indexing, marker-update rules, optimistic reconciliation).
  • Live ingest routes by session kind, not per-line shape sniffing, with two safety nets: shape-sniff fallback for bursts that arrive before session meta exists (restore races), and a shadow comparison reporting routeShadowMismatches + a sample type on the existing jsonl_entries feed-debug record. Nonzero = Claude JSONL transcript can disconnect while proxy/semantic stream stays live, breaking resume identity #290-class foreign-transcript attach or discriminator drift, now visible instead of rendering as someone else's conversation.
  • Opportunistic cleanups (per standing preference): the misplaced provider parsers moved out of shell code (workspace/{codex,claude}/ → providers/{codex,claude}/renderer/transcript/, pure git renames, ~600 LOC); three negated-form literal-pair guards phase 1 missed; a stale import.

Deliberate behavior unifications (each commented in place)

  1. Claude live marker policy → kept-entries-only, matching the other three sites (worst case: pagination re-reads a few filtered lines, which get filtered again).
  2. Filtered claude lines no longer enter the seen-uuid set (re-arriving filtered lines are simply filtered again).
  3. pendingCompaction clearing on compact summaries now applies to both providers' mapped entries (was claude-only; no-op for codex today).
  4. Preview stamps codex turn ids (harmless extra fields).

Verification

  • tsc --noEmit clean on both tsconfig projects (the real gate, per the phase-1 lesson)
  • electron-vite build clean (main + preload + renderer)
  • vitest 170/170 — rollout.test.ts moved with its module and still runs
  • Shadow-mismatch counter starts observable in feed-debug from the first live session on this build; the follow-up that removes the shape-sniff fallback entirely should wait until real sessions show zero mismatches.

🤖 Generated with Claude Code

…ting

Kills the quadruplicated per-provider transcript mapping (#394 §4.5,
§9): the codex quartet (marker / rolling turn cursor / rollout map /
turn stamp / terminal clear) was copy-pasted across live ingest,
initial history, and older pagination; the claude progress-unwrap +
conversation/compact filter existed four times (those three plus the
session preview). The copies had ALREADY drifted — the live claude
path recorded pagination markers before its filter while the other
sites recorded kept-only, and the live codex path cleared its turn
cursor while pagination used a differently-seeded local.

What changed:

- New registry capability `createTranscriptEntryMapper()` (+
  `extractProviderSessionId`) on RendererProviderCapabilities. The
  mapper interface lives in providerConfig.ts; implementations in
  @providers/{claude,codex}/renderer/transcript/mapper.ts. Codex's is
  stateful (rolling turn cursor with get/setTurnCursor so live ingest
  persists it across bursts); Claude's is stateless.

- The misplaced provider parsers moved home (opportunistic cleanup):
  workspace/codex/{entries,rollout,rollout.test,eventCursor}.ts →
  providers/codex/renderer/transcript/; workspace/claude/history.ts →
  providers/claude/renderer/transcript/. Pure git renames + import
  updates; ~600 LOC of provider knowledge out of shell code.

- Live ingest (useIpcSubscriptions) now routes by SESSION KIND, not
  per-line shape sniffing, with two safety nets: (1) bursts arriving
  before session meta exists fall back to the old shape sniff, so
  early codex session_meta lines aren't misrouted during restore
  races; (2) a shadow comparison counts lines where the legacy sniff
  disagrees with the kind route and reports them on the existing
  jsonl_entries feed-debug record (routeShadowMismatches +
  routeShadowSampleType). Nonzero means a #290-class foreign-
  transcript attach or discriminator drift — visible instead of
  silently rendering as someone else's conversation.

- initialHistory / history / previewModel lose their forks entirely;
  sites keep their genuinely site-specific policies (dedupe, tool
  indexing, marker-update rules, prepend-vs-append). The codex
  optimistic-user + queuedMessages reconciliation stays live-site-
  local, now gated on routed kind instead of line shape.

Deliberate behavior unifications (each commented in place):
- Claude live marker policy is now kept-entries-only like the other
  sites (worst case: pagination re-reads a few filtered lines).
- Filtered claude lines no longer enter the seen-uuid set (they were
  deduped before filtering; re-arriving filtered lines get filtered
  again).
- pendingCompaction clearing on compact summaries now applies to both
  providers' mapped entries (was claude-branch-only; no-op for codex
  today).
- Preview now stamps codex turn ids (harmless extra fields; it used
  the bare mapper before).

Also fixed three negated-form literal-pair guards phase 1's pattern
missed (initialHistory ×2, history ×1) and removed initialHistory's
stale isCodexRolloutEntry import.

Verified: tsc clean on both projects, electron-vite build clean,
vitest 170/170 (rollout.test.ts moved with its module and still
runs; the 1 failing suite is the pre-existing voice-dictation
harness quirk).

Part of #394.

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