The three composables commit the carve inside onCarve via the existing
commitCarve helper instead of after the flow, falling back to a URL
lookup for no-mutation carves (which fire no hook). On
failed-funds-returned with a rotation, the re-secured note is added
before the burned carved note is marked spent.
A flow that carves and then waits minutes on a settlement poll used to
commit the carve only after the whole flow resolved - an abort mid-wait
left burned inputs spendable locally and stranded the fresh outputs.
ensureExactAmount now reports a mutation-bearing carve through the new
FundOperationOptions.onCarve checkpoint the moment its outcome is known;
payWithBearers and transferBetweenMints thread it through.
Because the carve may now be committed before the flows' classification
rotates run, those rotations no longer rewrite carve.note in the result -
returned funds come back as a separate verified rotatedNote, so the
committed carve and the re-secured note can both be persisted. NWC's
payChangeset adapts to the rotatedNote field; its own commit timing is
unchanged (follow-up).
A split, merge or rotate whose answer is 'Invalid or already spent k1.'
may be the refusal of a retried request that already LANDED - believing
it strands the fresh outputs server-side and desyncs the wallet (the
input is burned at the mint but still spendable locally, so every later
operation fails). All three flows now probe a would-be output when a
refusal carries fresh secrets: live outputs are adopted (or surfaced
unverified when the probe can't tell), a genuine refusal still fails.
The redeem callback is a GET and HTTP stacks retry GETs, so a mint can
execute the first attempt and refuse the repeat as an already-spent input
(LUD-25 says SHOULD replay; real mints refuse). The kit attaches the fresh
output secrets to every refusal - probeMutationOutput tells a landed
mutation from a genuine refusal. The harness wrapper reproduces the retry
against the mock.
Review feedback: bind the claim answer to the secret that was polled
before building a note from it. The kit already guarantees this -
claimMintedNote returns the queried secret by construction and
fetchNoteInfo rejects a mismatched k1 echo - so the assert cannot trip
through the kit (and is therefore not separately testable); it exists so
a future kit regression fails loudly instead of tracking a wrong secret.
Named mints claim through the wallet's own secret, so a missing LUD-21
verify URL no longer blocks receiving; unnamed mints still require it -
there the preimage it reveals IS the claim.
The named-mint branch claims by the wallet's secret and treats the verify
preimage as an ordinary payment receipt - but keeps the rotated guard
before handing it out, because the rescue path (a mint that credited the
preimage despite the binding) claims through claimFromPreimage, where a
failed rotate leaves that preimage a live note secret. possibleCopy is
committed exactly like the unnamed path.
An unknown-still-pending transfer to a named mint carries the note at the
wallet's own secret in its claim material - persist it unverified like the
settled-claim-failed path already does, instead of dropping it with the
result object.
A named target needs no verify URL: the note appearing at the wallet's own
secret is the settlement proof, and it is claimed without a rotate. The
claim material carries the secret - and the unverified note at it - from
the start, so a melt settling after the poll budget is never a lost note;
the source-note oracle classification is unchanged.