mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
31f92df10d
* feat(core): lazy hook resumption via parallel event write + queue publish (rebased onto #1834 + #3145) Rebase of #3230 onto current main (267765375+ #1834 resilient resumeHook + #3145 event-count-gated replay restart). Reconstructed as a single commit since `git rebase -i` is unavailable in this environment. Reconciliation vs the pre-rebase branch: - Replaces #1834's version-prediction (`supportsQueueHookInput`, `QUEUE_HOOK_INPUT_MIN_VERSION`) with #3230's capability protocol (persisted `hookResumeInputVersion` + static `hookResumeDedup`). - One idempotency protocol: a single `resumeId` + SHA-256 payload digest per resume, sent to both the direct event write and the queue `hookInput`. - Two execution tiers: backend+consumer attest dedup -> parallel `Promise.allSettled(event write, queue publish)`; otherwise plain sequential (no hookInput/resumeId, event-write errors propagate). - Consumer re-ensures the `hook_received` event (keyed by resumeId/digest) after event loading, before replay; skips when already preloaded. - Preserves #3145: event-count guard, `preconditionReinvocations`, in-process replay restart, `insertEventByEventId`. - Removes #1834's resumeId-only test (never released); adds parallel + consumer-preload + world-local dedup/producer-consumer suites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(core): read top-level event.resumeId in replay dedup; reconcile unreleased #1834 docs/changeset - hook.ts: dedup hook_received replay on top-level event.resumeId (the backend now hoists it to a first-class column), with the legacy nested eventData.resumeId retained as a deprecated parse-only fallback. - workflow.test.ts: cover dedup across both top-level and legacy nested forms. - resume-hook.ts: emit producer recovery telemetry when a transient event-write failure is swallowed on the parallel path. - resume-hook.consumer-preload.test.ts: add terminal-run (consume) and transient-conflict (rethrow/redeliver) re-ensure cases. - Consolidate the two overlapping changesets into resilient-resume-hook.md and delete the redundant lazy-hook-resumption.md. - Docs: return type back to Promise<Hook> (resume-hook.mdx), rewrite the resilience changelog to the final parallel/deduplicated design, and correct the WORKFLOW_DISABLE_LAZY_HOOK_RESUME resilience wording. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs,core: rename "Resilient hook resume" → "Lazy hook resume" for consistency - changelog/index.mdx: update the changelog entry title. - hook.ts: update the dedup comment label to "Lazy-resume dedup". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: give #3230 its own changeset instead of repurposing #1834's The lazy-hook-resume work had been folded into #1834's pre-existing `resilient-resume-hook.md` changeset. Give this PR its own changeset and delete the superseded #1834 one, whose `resilientResume: true` flag promise no longer holds (resumeHook() returns plain Promise<Hook>). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: add #3230's own lazy-hook-resumption changeset Follow-up to63d877178, which deleted #1834's superseded changeset but did not stage the replacement. Adds this PR's own changeset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: tighten lazy-hook-resumption changeset Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: leave #1834's resilient-resume changeset/changelog/docs untouched Restore #1834's own artifacts that #3230 had rewritten: - .changeset/resilient-resume-hook.md (restored verbatim) - docs/.../changelog/resilient-resume.mdx (restored verbatim) - docs/.../changelog/index.mdx (restored verbatim) #3230 keeps only its own changeset plus the two docs its code/config genuinely require: the resumeHook() Promise<Hook> return type (ResumedHook is removed from the code) and the new WORKFLOW_DISABLE_LAZY_HOOK_RESUME env var. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Reconcile #1834 ResumedHook contract with #3230 parallel resume Preserve the resilient-resume contract from #1834 on the parallelized resumeHook() fast path instead of dropping it: - Restore the `ResumedHook` type (Hook + optional `resilientResume`) and its exports (`@workflow/core/runtime`, `workflow/api`); resumeHook/resumeHookImpl return `Promise<ResumedHook>`. - Set `resilientResume: true` on the swallow-recover branch (transient direct write failure + successful queue dispatch), absent on the happy/sequential paths. - Restore the producer OTEL convention `workflow.hook.resilient_resume` and the consumer `workflow.hook.resilient_resume_materialized`, wired where the consumer re-ensures the event. - Restore the consumer `occurredAt` derivation from the resume ULID so the materialized hook_received is dated to resume time, not queue-round-trip time. - Fix the #3230 changeset's contradictory "Still returns Promise<Hook>" line and update the resilient-resume changelog + resume-hook API reference to the shipped parallel/dedup behavior. - Port the #1834 failure-path coverage into resume-hook.parallel.test.ts (non-retryable event-write rethrow, both-fail prioritizes the queue error, resilientResume flag + payload delivery on the recovered path). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Address review: drop dead nested resumeId fallback, remove server PR link - Drop the legacy nested `eventData.resumeId` fallback in the hook consumer. The nested form was only ever written by unreleased preview builds and is stripped by `EventSchema` parsing (the `hook_received` eventData schema does not declare it), so the fallback was dead code. Dedup now keys solely off the top-level `event.resumeId` column. Repoint the replay dedup test to the surviving top-level path (it previously exercised the nested form only by building unparsed Event objects in memory). - Remove the internal workflow-server PR reference from world-vercel's capability note (the link 404s outside the org); the note keeps the same information without the dead link. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>