Skip to content

docs(driver-sql): state the autonumber contract — unique and monotonic, not gapless - #8488

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-8283-autonumber-not-gapless-contract
Aug 13, 2026
Merged

docs(driver-sql): state the autonumber contract — unique and monotonic, not gapless#8488
os-zhuang merged 1 commit into
mainfrom
claude/issue-8283-autonumber-not-gapless-contract

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#8283

Executes the maintainer ruling of 2026-08-13 (option 1): document the contract, change nothing.

What this is

A TSDoc addition and a changeset. Nothing else.

 .changeset/autonumber-not-gapless-contract.md | 40 ++++++++++++++++++++++++
packages/drivers/driver-sql/src/sql-driver.ts | 45 +++++++++++++++++++++++++++
2 files changed, 85 insertions(+)

All 45 added lines in sql-driver.ts are comment lines; there are zero deletions and zero non-comment additions in the whole diff. Verified mechanically rather than by eye:

$ git diff -U0 origin/main -- packages/drivers/driver-sql/src/sql-driver.ts \
| grep '^+' | grep -v '^+++' | sed 's/^+//' | grep -vE '^\s*\*'
(no output)

The contract, as stated

Placed next to the existing sentencean autonumber is an immutable business identifier (anchored by phrase — the neighbour is at sql-driver.ts:4827 on this base, and line numbers there have drifted repeatedly). The new section says:

  • Unique and monotonic per counter — the (table, tenant, field, scope) key getNextSequenceValue issues from — and NOT gapless.
  • A rejected write consumes the number it reserved: a unique violation on another field, a validation rule, a throwing beforeInsert. The number is issued to no row and never re-issued; the next write gets the one after it. TK-0001, a failed insert, then TK-0003 is the contract behaving correctly.
  • Why it cannot be taken back — the mechanism, since this is the driver that owns it: getNextSequenceValue commits the reservation in its own transaction (runner.transaction over parentTrx ?? this.knex), deliberately independent of the caller's insert.
  • This is ordinary sequence semantics, not a defect — with an explicit instruction not to add reclamation or reservation-reordering, and the reason that shape was rejected.
  • A decided property, citing the ruling, so the next reader can tell it was chosen rather than defaulted.

Accuracy note on the mechanism

I verified the mechanism against the code it sits beside rather than restating the card. runner.transaction nests when a parentTrx is supplied, so the inside-a-caller-transaction path rolls back with the refused insert and burns nothing — which the upsert retry comment right below already records as measured. The TSDoc states the independent-commit case as the contract-relevant one and notes the nested case parenthetically, so the paragraph does not overstate what the code does.

Explicitly not done

Changeset: patch on @objectstack/driver-sql, deliberately

Reasoned rather than copied, on repo evidence:

  • The empty-frontmatter route is not available: check-empty-changeset.mjs rejects a PR that newly adds one (the 182 in the tree are grandfathered by a diff-computed exemption).
  • That leaves patch or the skip-changeset label. The label is for a PR that "declares no release of its own" — the workflow's own textbook case is editing a CI-internal script.
  • This PR's entire product is a consumer-facing contract statement, and it ships two ways: the package builds with declaration: true and no removeComments, and insertOnlyUpsertColumns is protected, so the text is emitted into the published .d.ts; and AGENTS.md frames the changeset body as the channel that reaches consumers as CHANGELOG.md — "what an upgrading agent greps".
  • Routing a statement whose whole purpose is preventing a mis-promise into driver source only would reproduce, at the release-notes layer, exactly the gap that made this card necessary.

The honest counter-argument: AGENTS.md scopes changesets to feature work and functional improvement, and this is neither — that is the case for skip-changeset. I judged the delivery channel to be the deciding factor. Flipping to the label costs one deleted file if the reviewer disagrees.

Verification

CheckResult
pnpm --filter @objectstack/driver-sql typecheckpass
pnpm --filter @objectstack/driver-sql test1431 passed, 52 skipped, 93 files, 0 failed
autonumber + upsert-immutable specs, re-run by name3 files, 18 tests, pass
check:nul-bytespass (7643 files, no raw control bytes)
check:driver-conformancepass (45 cells, 0 debt)
check:changeset-gate-self-testspass
check-empty-changeset / check-changeset-no-major / check-adr-0087-registrationpass, diff-scoped against the merge base
check:objectui-changeset, check:test-source-alias, check:type-source-resolutionpass

Reverse verification does not apply here: there is no behaviour to revert and no diagnostic that could change colour. What I did instead was read the mechanism I describe — getNextSequenceValue's runner assignment and its runner.transaction body — and check the claim against the two independent places in the file that already record it as measured.

check:objectui-pin-fresh and check-dev-prereqs are red on inputs this diff does not touch (a stale .objectui-sha; a workspace needing pnpm build) — known-ambient, reported not chased.


Generated by Claude Code

…c, not gapless (#8283)
Document, beside the existing "an autonumber is an immutable business
identifier" sentence, what that identifier does and does not guarantee:
unique and monotonic per counter, NOT gapless. A rejected write consumes
the number it reserved, because getNextSequenceValue commits the
reservation in its own transaction, independent of the caller's insert.
Comment and changeset only — no behaviour change. Per the maintainer
ruling of 2026-08-13 on #8283 (option 1: document the contract;
reservation-reordering rejected, opt-in gapless mode recorded as a
restart condition, not built).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VoxQqG5FiUHZKCST7KDoZC
@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 3:38pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-sql.

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

  • content/docs/data-modeling/drivers.mdx(via @objectstack/driver-sql)
  • content/docs/getting-started/glossary.mdx(via @objectstack/driver-sql)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/driver-sql)
  • content/docs/plugins/anatomy.mdx(via @objectstack/driver-sql)
  • content/docs/plugins/packages.mdx(via @objectstack/driver-sql)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/driver-sql)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/driver-sql)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/driver-sql)

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

  • content/docs/releases/implementation-status.mdx(via @objectstack/driver-sql)

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.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] a failed insert permanently burns its autonumber (TK-0001 → fail → TK-0003) — is a gapless series in scope at all?

1 participant

@os-zhuang