Files
orca/src
Merge Sim ebd22d9ce2 feat(native-chat): decide a restart-stranded send against provider history
`markPendingSubmissionsUnknown` flips every surviving `pending` submission
to `unknown` on attach and stops there. The module written to finish the job
describes the intended two-step in its own header -- "Every surviving
`pending` becomes `unknown` and is then matched against provider history" --
and only the first step ever shipped. `reconcileSubmissions` has been
imported by exactly one test file and nothing else.

So a message stranded by a dead child or a host restart had no recourse but
retyping: Retry correctly refuses to redeliver something that may already be
with the model, the outbox entry drops, and a transient error line is all
that remains. This wires the second step, so those are decided on evidence
instead of refused.

Caller placement is the design decision, because where it runs determines
what a consistent history boundary can mean. It runs in `attachJournal`,
immediately after the sweep: attach happens after the record store's CAS
hands this host the lease and before a provider child starts, so nothing can
append to provider history while it is read, and the window stays valid
until the resume consumes it. The three other settlement sites can all be
overtaken by a newly started child before the read is acted on.

The history source is the Claude project JSONL for the handle chain's
provider session id -- definitionally what a resume replays, which is what
makes absence meaningful. Boundary consistency reuses
`proveClaudeTranscriptBranchFromJsonl` rather than inventing a check:
a fork, a compacted log and a truncated tail each already throw there, and
each maps onto `boundaryConsistent: false`. A null leaf uuid is also false,
because there is no anchor to prove a start from.

Two guards were needed that the reconciler cannot enforce itself, because
Claude echoes no client message id and only the fingerprint pass can fire:

- A transcript records a pasted image as base64, and the block decoder drops
  it silently for want of a url or path. Such a record would enter the
  window advertising a text-only fingerprint, where an unrelated text-only
  submission with identical text could claim it. The window now inspects raw
  content parts before decoding and excludes any record a part would be
  dropped from.
- A submission carrying an image-ref path can never match a transcript that
  keeps only base64. Without a guard it matches nothing by construction
  rather than by absence and falls straight through to `not_delivered`, and
  a Retry would then redeliver an image already sent. Only text-only bodies
  are handed to the reconciler.

Both guards fail a named test when removed.

Limits, stated rather than implied. The exact-match tier needs the provider
to echo our id, which Codex does and Claude does not, so Claude resolves by
fingerprint alone -- and two identical prompts deliberately reach
`ambiguous_match` instead of guessing. Repeated one-word prompts therefore
stay unknown by construction. This decides what it can prove and refuses the
rest, which is the intended contract, not a shortfall in the wiring.

Found while doing this and not fixed here: the block decoder silently
dropping base64 images has a blast radius beyond reconciliation and deserves
its own change.
2026-09-11 11:33:27 -07:00
..