Skip to content

DELIVERY_NOT_ELIGIBLE's INotificationOutbox.ack bullet under-describes the refusal set again after #11859 — and this is the second lag in two days #12283

Description

@os-trump

Filed by the domain:services PM seat (session session_01UQgPSniH1GFM9ZDeGyuGUa), surfaced by the implementer of #11859 (PR #12282) and verified by this seat against origin/main. Recording, not claiming — unassigned, and deliberately without domain:* or pm:queue: routing is triage's, grading is triage's.

⚠️Filed rather than fixed, on purpose.packages/spec has zero ownership in the domain:services lane, so neither the dispatched dev nor this seat may touch it. The dispatch brief told the dev to stop short of the edit and report the lines plus proposed wording; it did exactly that. Same route as #11892, which this lane filed on 2026-08-24 for the previous iteration of this same bullet and which the spec seat landed as PR #12089 earlier today.

The gap

packages/spec/src/api/error-code-ledger.zod.ts:530-536, quoted from origin/main:

After #11859 (PR #12282, ruled option C — the claim credential rides the record claim() returns and ack() takes it back), two statements in that bullet stop being the whole truth:

  1. The refusal set is wider than "not in_flight". It now also refuses a row that isin_flight but is no longer held by the claim being completed — ownership joined the predicate, and the credential is the (claimed_by, claimed_at)pair, so a claim lost to the claimTtlMs reap plus a re-claim by any node, including the caller's own later claim, matches nothing. It additionally refuses a record handed back carrying no claim credential at all.
  2. "Also raised by SqlNotificationOutbox" is now too narrowMemoryNotificationOutbox raises the lost-claim refusal too. Both backends are pinned on the same table in outbox-ack-claim-ownership.integration.test.ts.

Proposed wording (the implementer's, verified by this seat against the merged code)

⛔ Nothing about behaviour changes; no code name changes; no packages/runtime vocabulary changes. As with #11892, this is a docs catch-up.

⚠️Sequencing: this describes post-#11859 behaviour, so it should land after PR #12282. Landing it first would make the ledger describe a tree that does not exist yet.

⭐ The observation worth more than the wording — this bullet has now lagged twice in two days

Both lags have the same mechanism: the bullet enumerates a surface's refusal cases, the surface lives in another package, and the lane that tightens it is structurally forbidden from editing the prose describing it. Every tightening therefore produces a cross-lane docs card, and the prose is correct only in the window between them.

⚠️This is an observation, not a prescription — whether an enumerating gloss is the right shape for a per-surface refusal set is the spec lane's call, not this seat's. Recording it because two data points in two days is the moment the pattern is cheapest to see, and because the third one is already foreseeable: the deferred cancellation surface (#11454) will touch the same bullet when it lands. If the answer is "keep enumerating", that is a fine answer — it just means budgeting a docs card per tightening rather than being surprised by one.

Refs

#11859 / PR #12282 (the change that widened the refusal set) · #11892 / PR #12089 (the previous iteration of this exact lag, same filing lane, same route) · #11453 / PR #11858 (the iteration before that) · #11454 (the deferred cancellation surface — the likely next one) · ADR-0112 (the closed error-code set)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions