Skip to content

spec: S10.1 item 5 gates a MUST on a rejection mechanism the spec never defines #12

Description

@ssilvius

The defect

SPEC.md S10.1 item 5 reads:

  1. Honor atomic: true on batch requests when not rejected.

"when not rejected" qualifies a MUST against a rejection the spec never defines. There is no way for a conforming server to decline atomicity:

  • The registered error-code table in S7 has no not_supported (or equivalent). The set is bad_request, sql_error, not_allowed, auth_error, permission_error, payload_too_large, rate_limited, internal_error. not_allowed is scoped to statement shape rejected by server policy, which does not cover refusing the atomic field.
  • S4.2's only rejection clause is statement-count overflow to 413 / payload_too_large.
  • Nothing in S6 echoes back whether the server actually ran the batch in a transaction, so a client has no positive signal either.

So a server that cannot do transactions has exactly two options, and both are non-conforming: silently lie (accept atomic: true and not honor it), or misuse an error code. A client cannot distinguish either case from an unrelated failure, and S7's "treat unknown error.code values as the closest registered code by HTTP status family" guarantees a vendor: code cannot carry the signal interoperably.

This is issue #3's exact shape -- a normative keyword gated on a condition the spec leaves undefined -- reproduced in the conformance section rather than in a field definition.

Why it is being filed now

rafters-studio/smugglr PR #308 makes this gap load-bearing. Its migrate design decision 6 ("design as if no transaction exists") now rests on the reasoning above: the spec gives a server no conforming way to decline atomicity, so atomic honoring is unobservable to a client and correctness must not depend on it. That reasoning is correct against v0.1 on both main and #9's head -- I verified it. It is also reasoning about an absence, which means closing this gap rots a downstream repo's prose. Reviewed and flagged there; the fix requested downstream is to pin v0.1 into those sentences.

The design conclusion survives either resolution below, because absence of a rejection is still an unverifiable claim of support. Only the "lie or misuse a code" sentence depends on the gap.

The two candidate resolutions

A. Drop the qualifier. Item 5 becomes "Honor atomic: true on batch requests." Unconditional, matches what S4.2:64 already says (the MUST has no escape hatch there), and matches both reference servers. A server that cannot do transactions is simply non-conforming for batch -- which is arguably already the truth S4.2 states, with item 5 accidentally weakening it.

B. Define the decline path. Register a code (not_supported, 400) and state in S4.2 that a server unable to execute a batch atomically MUST reject a request carrying atomic: true with it, rather than executing non-atomically. This makes the rejection real, makes item 5's qualifier meaningful, and gives a client a distinguishable signal -- but it is a new registered code, so per S11 it is a v0.2 additive change, not a v0.1 clarification.

Preference: A, and it is the smaller claim. v0.1's S4.2 already puts an unqualified MUST on the server; item 5's "when not rejected" is the only text in the spec that implies a negotiation exists, and no other section supports it. Removing it makes the conformance section agree with the field definition instead of quietly contradicting it. B can be considered on its own merits for v0.2 -- a real decline signal has value -- but it should not be motivated as a fix for this, because the fix for a dangling qualifier is to delete the qualifier.

Whichever lands, the check-before-writing rule from #9 applies: verify both reference servers first. Both currently honor atomic (examples/cloudflare-worker-to-d1/src/index.ts:92 via db.batch(), examples/cloudflare-durable-object/src/index.ts:124 via ctx.storage.transactionSync), so A codifies shipped behavior and neither server becomes non-conforming.

Acceptance criteria

  1. S10.1 item 5 no longer gates a normative obligation on an undefined condition.
  2. S4.2's atomic obligation and S10.1's conformance item state the same rule -- no reader can find a negotiation in one and not the other.
  3. Both reference servers are checked against the resulting text before it is written, and remain conforming.
  4. conformance/README.md does not grant or imply a permission the spec does not issue (the P0: non-atomic batch failure has no normative behavior (forbid partial success + REQUIRE statementIndex on non-atomic + state that preceding statements persist) #1 failure mode).
  5. If resolution B is chosen instead, the version bump per S11 is stated explicitly in the PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions