fix(async): complete beta.5 webhook convergence - #2660
Conversation
| taskId: metadata.task_id, | ||
| taskType: metadata.task_type, | ||
| status: metadata.status, | ||
| notificationId: metadata.notification_id ?? null, |
There was a problem hiding this comment.
Medium: Adding notificationId: metadata.notification_id ?? null to the fingerprint changes the hash for every v2 entry, not just ones carrying a notification_id — the ?? null folds the field into the canonicalized object unconditionally. A handled-marker or in-flight claim written before this deploy (fingerprint computed without the field) will mismatch the same webhook's post-deploy at-least-once retry (fingerprint now includes notificationId: null), so webhookHandledFingerprint/webhookActiveClaimFingerprint at L883/L890 throw WebhookDedupConflictError → 5xx → publisher retries again → conflict storm on already-handled/in-flight events for the full dedup TTL (default 24h) after upgrade. This is the exact continuity break the v1→v2 key namespace split was created to avoid. Consider omitting the key when absent (...(metadata.notification_id !== undefined && { notificationId: metadata.notification_id })) so pre-existing fingerprints stay stable and only genuine beta.5 events differ.
| if (record.statusMessage) payload.message = record.statusMessage; | ||
| if (record.status === 'completed' && record.result !== undefined) { | ||
| if ( | ||
| args.include_result === true && |
There was a problem hiding this comment.
Medium: Behavior change on tasks_get, and it is not version-gated. Previously record.status === 'completed' && record.result !== undefined returned result unconditionally; now completed results require args.include_result === true. Any existing tasks_get consumer that read result on a completed task without passing include_result now gets undefined — on every served version, not only beta.5. This aligns tasks_get with get_task_status (which already gated on include_result) and the migration doc documents it as intended, so it is arguably a drift fix — but it is a default-response narrowing for a stable wire tool shipping under a patch changeset. Confirm patch is the right severity given a previously-returned field is now omitted by default.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean, heavily-tested beta.5 async adoption with no blocking findings.
What I checked
- Wire-shape logic in
prepareProtocolToolCall, governance fail-closed path, and postgres/in-memory terminal-status fencing — all correct and covered by tests. - Witness-not-translator posture preserved; no fabrication or re-shaping at seams.
- Changeset present (
.changeset/warm-webhooks-converge.md) for thesrc/lib/**changes. high_riskis true only becausesrc/lib/protocols/index.tswas (modified); no medium-or-higher finding on that file, so the modification is presumed safe. No deletions.
Medium findings (non-blocking; 2 total, falls through to row 9)
src/lib/core/AsyncHandler.ts:908— Fingerprint schema change breaks webhook dedup continuity across upgrade.src/lib/server/decisioning/runtime/from-platform.ts:3479—tasks_getcompleted result now gated behindinclude_result, not version-gated.
Two medium findings (neither in data-loss/schema/infra category), no critical/high, no gated-paths gate (gated_paths: false), no no-auto-approve team match, and high_risk (modified) has no accompanying medium on the modified high-risk file. Rows 1–8 do not fire → row 9 approve. The low defensive-throw note is informational.
Medium findings
- src/lib/core/AsyncHandler.ts:908 — Fingerprint schema change breaks webhook dedup continuity across upgrade
- src/lib/server/decisioning/runtime/from-platform.ts:3479 — tasks_get completed result now gated behind include_result, not version-gated
There was a problem hiding this comment.
Ladon verdict: Approve
Approve.
This subsequent-review delta is test-only: it adds the now-required push_notification_config.operation_id field to three test cases and one YAML fixture (create_media_buy_async_submitted.yaml) that previously omitted it. All added values conform to the ^[A-Za-z0-9_.:-]{1,255}$ pattern the PR enforces for beta.5. No library code changed in this delta.
The two prior medium findings (AsyncHandler.ts:908 fingerprint schema continuity, from-platform.ts:3479 tasks_get include_result gating) are absent from the current run and not re-raised — the prior decision was already approve.
Decision-table walk: no critical/high findings (row 1 no); gated_paths is false (row 2 no); high_risk is true but the only high-risk match is src/lib/protocols/index.ts (modified) with no medium finding attached to it (rows 3–5 no); prior decision was approve, so sticky escalation row 6 does not apply; no no-auto-approve team match (row 7 no); zero medium findings, well under the 3-medium threshold (row 8 no). Falls through to row 9 → approve.
Summary
include_resultValidation
npm run typechecknpm run ci:codegen-strictnpm run format:checknpm run test:protocolsnpm run review:codex -- --all --base main: no blocking findings