Wrap a base resume fn so a run that re-suspends at a LATER gate auto-queues
its next approval(s) — the multi-gate flow (product-launch's two gates); every
suspended path re-queues, not just the first. The base
resume (resumeViaRuntime or a DO-stub fetch) deliberately omits re-queue; this
adds it without coupling to the host's resume topology.
The reviewer whose decision advanced the run becomes the next gate's
requestedBy, so they cannot also decide it (SoD across gates). Guard
fail-CLOSED: refuse to re-queue without a requester rather than fall back to
the system id.
getService is a thunk because the service references this closure at its own
construction (service = new ApprovalService({ resumeRun: resumeRunWithRequeue(base, () => service, sys) }));
the closure only runs on a later decision, so the reference is resolved by then.
audit is optional and should be the SAME sink the service itself uses
(buildHostApprovalService wires it that way): the base resume above has
already durably advanced the run by the time the re-queue below can fail,
so a throw here leaves a gate suspended with no approval record and no
other signal that happened. reconcileApprovalsForSummary is the recovery;
this event is what tells an operator it was needed.
Wrap a base resume fn so a run that re-suspends at a LATER gate auto-queues its next approval(s) — the multi-gate flow (product-launch's two gates); every suspended path re-queues, not just the first. The base resume (resumeViaRuntime or a DO-stub fetch) deliberately omits re-queue; this adds it without coupling to the host's resume topology.
The reviewer whose decision advanced the run becomes the next gate's
requestedBy, so they cannot also decide it (SoD across gates). Guard fail-CLOSED: refuse to re-queue without a requester rather than fall back to the system id.getServiceis a thunk because the service references this closure at its own construction (service = new ApprovalService({ resumeRun: resumeRunWithRequeue(base, () => service, sys) })); the closure only runs on a later decision, so the reference is resolved by then.auditis optional and should be the SAME sink the service itself uses (buildHostApprovalService wires it that way): the base resume above has already durably advanced the run by the time the re-queue below can fail, so a throw here leaves a gate suspended with no approval record and no other signal that happened. reconcileApprovalsForSummary is the recovery; this event is what tells an operator it was needed.