mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
`void | Promise<A> | Promise<void> | Decision` (the agent-loop `finishTurn` hook, codemode's remote client) had no representation: a union arm's index is its runtime tag and a promise arm's tag IS the `instanceof Promise` test, so a second promise arm had no tag. The union was refused (SC2009) and every use site inherited the blocker. JavaScript cannot tell the arms apart: `instanceof Promise` answers true for both and `await` settles to either payload. The union therefore maps to one promise arm `Promise<A | undefined>`: payloads join like union arms (`void` becomes the undefined unit, an already-union payload flattens, identical payloads dedupe, a checked-dynamic payload absorbs the arms the dynamic representation holds faithfully). A payload with no union arm home (a RegExp payload, a promise-of-a-promise) keeps the whole union fenced, and the refusal now says why. A `Promise<A>` value flowing into the merged arm converts with the existing checked promise-payload widening (promisePayloadWidens / promiseWiden): the promise keeps its identity and its settlement turn, and the settled payload is checked against the payload slot. Cost: the merge only runs for a union that already has two promise arms, which is refused today. Every other union interns the same arms in the same order, so a fully typed program without such a union compiles to identical IR. Corpus 8414-promise-union-arms pins identity through the union slot, `instanceof Promise` narrowing, `await`, `.then`, the void arm, the rejection path and a non-promise data arm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>