Skip to content

fix(service-automation): report a durable http callout as unmeasured, not acted (#7882) - #7962

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-7882-http-node-enqueued-acted
Aug 12, 2026
Merged

fix(service-automation): report a durable http callout as unmeasured, not acted (#7882)#7962
huangyiirene merged 1 commit into
mainfrom
claude/issue-7882-http-node-enqueued-acted

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes#7882

The defect

http-nodes.ts:139 returned metrics: { acted: 1 } for a delivery that had only been enqueued. enqueueHttp() hands back the id of a pendingsys_http_delivery row; HttpDispatcher decides the real outcome afterwards, and that outcome includes dead-lettering the callout (non-retriable status, or an exhausted retry budget). So an operator reading the run summary could see acted: 1 for a callout the durable record shows as dead — the summary asserting an effect that never happened.

The fix

The durable path now reports unmeasuredEffect. That is the platform's existing word for "an effect happened but its outcome is not yet knowable" (the same qualifier connector_action uses), and pointedly not a bare acted: 0connector.zod.ts:613 forbids that, and it would trip the documented broken-sweep alert (selected > 0 AND acted = 0 AND unmeasured = 0) on every healthy durable callout. A pending delivery now suppresses that alert without asserting success.

The step still succeeds and its output is still { deliveryId, enqueued: true }: the flow did everything it can do synchronously, and not blocking on the callout is the whole point of durable mode. No synchronous wait was added.

Did #7875's shape apply? Partly — the ruling, not the structure

#7875 needed EmitResult split into delivered vs enqueuedinside service-messaging, because MessagingService.emit() hides two outcomes behind one call: inline (P0) fan-out, which really does know the result, and the P1 outbox, which does not. The node could not tell them apart without the service-side narrowing.

enqueueHttp() has no such ambiguity. It returns a row id and nothing else, and the row it writes is unconditionally pending — there is nothing to split. The two-path structure already sits in the node: durable: true enqueues, everything else calls fetch() inline, and the inline half already reported honestly (acted: 0 for a read, acted: 1 for an accepted mutation, unmeasuredEffect for a rejected/timed-out one). So the whole disagreement was one branch of one node. Nothing in service-messaging changed.

Reverse-verification on origin/main

New test run against unmodified origin/main node code:

Tests 2 failed | 2 passed (4)
× durable delivery that dead-letters — expected { acted: 0, unmeasured: 1 }, got { acted: 1, unmeasured: 0 }
× durable delivery that later succeeds — expected { acted: 0, unmeasured: 1 }, got { acted: 1, unmeasured: 0 }
✓ inline mutating call — acted: 1
✓ inline GET — acted: 0

Every durable-record assertion (sys_http_delivery row is dead / pending / success, responseCode: 404, attempts: 0) passed on main — the defect is real and lives only in the summary. After the fix: 4/4 green, and the full package suite is 940 passed (79 files).

The test wires the realMessagingService + MemoryHttpOutbox + HttpDispatcher and ticks the dispatcher manually, for the same reason #7875 did: the outcome is decided after the run settles, which a fake answering in one shot cannot express. Assertions are on the two durable operator-facing records — the folded run summary and the outbox row — never on call counts. Both directions are covered so the fix cannot degenerate into "dead-lettered deliveries are special": the enqueued-then-succeeded case reports the same unmeasured, and the two inline cases keep their measured counts.

Consumer check

Grepped deliveryId, enqueued, and the durable acted claim across the repo:

  • { deliveryId, enqueued: true } output — read by nothing outside the node and its own tests. plugin-webhooks' deliveryId is an unrelated request-body field on the redeliver endpoint.
  • metrics — folded generically by run-summary.ts; no consumer special-cases the http node.
  • content/docs/automation/flows.mdx:737 documented http, durable: true → acted: 1 — the outbox row is a real, durable effect. That row is now corrected — it was the one place the old behavior was promised. Updated in this PR.
  • No packages/spec/src/** file was touched, so no gen:schema / gen:docs regeneration is needed (io-node-config.zod.ts's doc comment describes the { deliveryId } output, which is unchanged, and makes no acted claim).

check:doc-authoring, check:doc-anchors, check:docs-audit-scope and typecheck are green locally. Changeset: .changeset/http-node-enqueued-not-acted.md.


Generated by Claude Code

… not acted (#7882)
The `http` node's durable path returned `metrics: { acted: 1 }` for an enqueue.
`enqueueHttp()` returns the id of a `pending` sys_http_delivery row — the
HttpDispatcher decides the outcome afterwards, including dead-lettering — so a
run summary could claim `acted: 1` for a callout the durable record shows as
`dead`.
It now reports `unmeasuredEffect`: the platform's existing qualifier for an
effect whose outcome is not yet knowable, and not a bare `acted: 0`, which
connector.zod.ts forbids and which would trip the broken-sweep alert on every
healthy durable callout.
Smaller than the sibling fix at `notify` (#7747): `emit()` hid two outcomes
behind one call and needed EmitResult split; `enqueueHttp()` has no such
ambiguity, so nothing in service-messaging changed. The inline path keeps its
measured counts.
Fixes#7882
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016U4fTquAhViZT7RcMfcmSq
@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 12, 2026 8:59am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-automation.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/flows.mdx(via @objectstack/service-automation)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/service-automation)
  • content/docs/plugins/packages.mdx(via @objectstack/service-automation)

2 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/service-automation)
  • content/docs/releases/v9.mdx(via @objectstack/service-automation)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 12, 2026
@huangyiirene
huangyiirene marked this pull request as ready for review August 12, 2026 09:33
@huangyiirene
huangyiirene added this pull request to the merge queueAug 12, 2026
Merged via the queue into main with commit 078c448Aug 12, 2026
27 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-7882-http-node-enqueued-acted branch August 12, 2026 09:51
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding: the HTTP node reports acted: 1 for a merely-enqueued outbox delivery — same overstatement #7747 fixed for notify, against sys_http_delivery

2 participants

@huangyiirene@claude