Skip to content

sql-driver: one emission-identity source — loud DDL refusal for redshift/cockroachdb, pgnative joins the Postgres family - #12591

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-11991-emission-identity-source
Aug 26, 2026
Merged

sql-driver: one emission-identity source — loud DDL refusal for redshift/cockroachdb, pgnative joins the Postgres family#12591
os-warren merged 2 commits into
mainfrom
claude/issue-11991-emission-identity-source

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #11991

Lands the #11756 maintainer ruling (2026-08-25, verbatim 「同意」 on 「C,但 pgnative 归入 Postgres 家族」; record = #11756 comment 5404884704). The ruling's state was re-verified on GitHub before any code was written: #11756 is closed completed and the comment stands as quoted.

Verified at d215aaa903. Base: b307bfd2ae.

Premise re-check first

The card's premise was measured on 2026-08-24 and is partly stalesql-driver.ts moved twice since. Re-measured on origin/main at b307bfd2ae (no line numbers quoted below; every anchor is a symbol name):

So part 1 of the ruling lands here as naming that extension once, not as a re-convergence of tables that were already converged. The premise otherwise holds in full.

Reproduction, before repairing

Measured on origin/main at b307bfd2ae with a throwaway probe (deleted; git status clean at commit time). Same probe re-run after the change.

Before:

POSTGRES_EMIT_CLIENTS   = pg, postgres, postgresql
POSTGRES_WIRE_CLIENTS   = cockroachdb, pg, postgres, postgresql, redshift
DIALECT_CONNECT_TIMEOUT = cockroachdb, mysql, mysql2, pg, postgres, postgresql, redshift

client: pgnative       isPostgres=false  dialectName=unknown  DDL gate=PROCEEDS
                       nowDefault(date)=CURRENT_TIMESTAMP     in wire set=false  timeout row=null
client: redshift       isPostgres=false  dialectName=unknown  DDL gate=PROCEEDS
client: cockroachdb    isPostgres=false  dialectName=unknown  DDL gate=PROCEEDS

…and what knex actually compiles for one CREATE TABLE, per client:

pg            create table "probe_t" ("id" varchar(255), "created_at" timestamptz default CURRENT_TIMESTAMP,
                                      "body" text, constraint "probe_t_pkey" primary key ("id"))
pgnative      … byte-identical to pg …                       (dialect=postgresql driverName=pgnative)
cockroachdb   … byte-identical to pg …                       (dialect=postgresql driverName=cockroachdb)
redshift      create table "probe_t" ("id" varchar(255) not null, "created_at" timestamptz default CURRENT_TIMESTAMP,
                                      "body" varchar(max));
              alter table "probe_t" add constraint "probe_t_pkey" primary key ("id")

That is the ruling's "silently mis-built table" as a measurement rather than a claim: on Redshift the old path did not fail — it built a different table (varchar(max) for text, a not null id, the primary key moved into a second statement) and the deployment would find out on write.

After:

POSTGRES_EMIT_CLIENTS   = pg, pgnative, postgres, postgresql
POSTGRES_WIRE_CLIENTS   = cockroachdb, pg, pgnative, postgres, postgresql, redshift
DIALECT_CONNECT_TIMEOUT = cockroachdb, mysql, mysql2, pg, pgnative, postgres, postgresql, redshift

client: pgnative       isPostgres=true   dialectName=postgres  DDL gate=PROCEEDS
                       nowDefault(date)=(timezone('utc', now())::date)   in wire set=true
                       timeout row={"key":"connectionTimeoutMillis","urlKey":"connectionString"}
client: redshift       DDL gate=REFUSED SQL_DIALECT_EMISSION_UNSUPPORTED / 501
client: cockroachdb    DDL gate=REFUSED SQL_DIALECT_EMISSION_UNSUPPORTED / 501

What changed

One declared extension. POSTGRES_WIRE_ONLY_CLIENTS = { cockroachdb, redshift } is new and is the single place those two names are written. POSTGRES_WIRE_CLIENTS and the pg arm of DIALECT_CONNECT_TIMEOUT are now POSTGRES_EMIT_CLIENTS ∪ POSTGRES_WIRE_ONLY_CLIENTS, and the refusal reads the same set — so "which clients get the connect bound", "which get the #11389 parser" and "which are refused for DDL" cannot drift apart again. The derivation direction is unchanged and documented as load-bearing: emission is the source, never derived from a union of the tables.

pgnative joins the emission family. One name added to POSTGRES_EMIT_CLIENTS; its wire-table and connect-timeout memberships arrive by derivation with no edit to either table — which is also the proof that the derivation is real. It gains the #11389 calendar-day parser pin it never had, and loses the bare CURRENT_TIMESTAMP DATE default (the exact #11550 defect) it inherited by being lumped in with Redshift.

The refusal. SqlDriver.assertDialectEmits is called from assertSchemaMutable — the existing single DDL choke point (initObjects/syncSchema, dropTable, rotateShards, reconcileManagedSchema) — after the unchanged schemaMode check, so it fires only where DDL would previously have proceeded. It throws UnsupportedDialectEmissionError (packages/drivers/driver-sql/src/dialect-emission-refusal.ts): code SQL_DIALECT_EMISSION_UNSUPPORTED, status 501, structured client / operation / supportedClients, and a message rendered from the driver's own tables that names the client, every client the driver does emit for, and the skipSchemaSync / OS_SKIP_SCHEMA_SYNC=1 posture that keeps the database usable. It runs before ensureDatabaseExists and before the first hasTable probe, so nothing is half-built — pinned with a real round-trip.

mariadb keeps its third state. The refusal is keyed on the named set, never on "the getters do not recognise this". A gate keyed on dialectName === 'unknown' would have swept in mariadb (explicitly out of the ruling's scope) and knex's bring-your-own-Client-constructor hatch. Both are pinned as unrecognised and unrefused.

Ledger. SQL_DIALECT_EMISSION_UNSUPPORTED is registered under a new @objectstack/driver-sql key in ERROR_CODE_LEDGER (ADR-0112 D3). Registered rather than left driver-local because it is wire-reachable — publishing a drafted object calls engine.syncObjectSchemaSqlDriver.syncSchema → the DDL gate on a server already serving HTTP. That is the exact test #8035 applied when it unregistered MONGODB_MULTI_TENANT_UNSUPPORTED (a boot refusal the CLI rethrows pre-HTTP, which no envelope could carry). No standard-catalog member covers the condition: NOT_IMPLEMENTED means "not yet", and the content of the ruling is that this is decided.

Part 4 — the rewrite is the ruling landing, done explicitly

sql-driver-11550-dialect-client-spellings.test.ts carried a block whose stated job was to be consciously rewritten when #11756 was decided. It is rewritten here as its own act, with the ruling named on the line:

  • redshift and cockroachdb parse pg wire but do NOT emit Postgres DDL… and are REFUSED for DDL: the silent fall-through assertions are replaced by the refusal, with the wire and connect-timeout memberships pinned as unchanged;
  • a new case pins pgnative as the Postgres family, asserted off a live knex client (dialect === 'postgresql', driverName === 'pgnative') rather than from knex's docs;
  • the mariadb case gains the not.toThrow() half;
  • three expected-membership lists gained pgnative, and the connect-timeout membership case now states that the extension is declared once.

The refusal's own behaviour is pinned in a new sql-driver-11991-emission-identity-refusal.test.ts (11 cases).

Clause ② — yes

git diff --stat b307bfd2ae..d215aaa903:

 .changeset/sql-emission-identity-one-source.md     |  55 ++++
 .../driver-sql/src/dialect-emission-refusal.ts     | 135 ++++++++++
 packages/drivers/driver-sql/src/index.ts           |  11 +
 ...l-driver-11550-dialect-client-spellings.test.ts | 147 +++++++---
 ...-driver-11991-emission-identity-refusal.test.ts | 296 +++++++++++++++++++++
 packages/drivers/driver-sql/src/sql-driver.ts      | 215 ++++++++++++---
 packages/spec/src/api/error-code-ledger.zod.ts     |  17 ++
 7 files changed, 807 insertions(+), 69 deletions(-)

Accept/reject behaviour moves in both directions, and the public surface widens:

  • reject — a redshift / cockroachdb datasource that previously completed schema sync now throws at the DDL gate (packages/drivers/driver-sql/src/sql-driver.ts, assertSchemaMutableassertDialectEmits);
  • accept — a pgnative datasource gains emission identity, a different DATE default, a connect bound and a wire parser it did not have;
  • surfacepackages/spec/src/api/error-code-ledger.zod.ts gains a member of the generated ErrorCode union, and @objectstack/driver-sql's index exports a new error class, code and renderer.

needs:contract-review is already on the card and the measurement agrees with it.

Verification

Every exit code captured before any pipe (cmd > log 2>&1; rc=$?). Heavy commands ran through scripts/pm/os-verify-lock.sh; where a wrapper script held the lock, the wrapper's own exit is not the verdict — per-command exits are quoted below.

Union re-derived on the real changeset after the final commit: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (answer sourced from this checkout of objectstack-ai/objectstack; 6 paths; 176 families discovered).

Green (per-command exit 0, verdict lines quoted where the gate prints one):

what evidence
@objectstack/driver-sql full suite Test Files 138 passed | 8 skipped (146) · Tests 2125 passed | 127 skipped (2252)
@objectstack/driver-sql typecheck tsc --noEmit, exit 0
@objectstack/spec typecheck check:test-typecheck: OK — @objectstack/spec's test layer compiles … 55 file(s) / 263 error(s) held (unchanged)
@objectstack/spec src/api + type-alias-convention.pin Test Files 40 passed (40) · Tests 1257 passed (1257)
@objectstack/driver-sqlite-wasm Test Files 25 passed (25) · Tests 395 passed (395)
@objectstack/driver-turso Tests 1001 passed (1001); one file (date-bucket-parity.test.ts) first failed to collectFailed to resolve entry for package "@objectstack/verify", an unbuilt closure, not a finding. After --filter '@objectstack/driver-turso^...' build it passes Test Files 1 passed (1)
pnpm lint (whole repo, eslint . --no-inline-config) exit 0, no findings — run in full, so no narrowing to justify
check:dispatcher-error-vocabulary check-dispatcher-error-vocabulary: OK — 21 unregistered code-stamping site(s), all classified (unchanged count: the new code is registered, so it is not a site)
check:error-code-casing exit 0
check:error-status-conformance ✓ every derivable runtime status is documented, and every documented status is reachable.
check-adr-0087-registration ✓ … 1 declared-breaking changeset(s), each carrying an ADR-0087 dispositionnot-required (no-migration-prescription)
check:nul-bytes check-nul-bytes: OK (scanned 6939 text file(s) … no raw ASCII control bytes)
others, all exit 0 check:driver-conformance · check:engine-double-contract · check:where-matcher · check:objectql-double-limit · check:query-options-erasure · check:test-source-alias · check:cross-package-test-inputs (+ scripts/check-cross-package-test-inputs.mjs) · check:type-source-resolution · check:published-files · check:page-declaration-shape · check:slot-lookup · check:type-check-coverage · check-comment-mask-adoption · check-plugin-teardown-shape · check-ci-filter-parity · check:authorable-surface · check:liveness · check:empty-state · check:strictness-ledger · check:variant-docs · check:spec-parsed-alias · check:doc-authoring · check:doc-formula-expressions · check:merge-driver · check:changeset-gate-self-tests · check:objectui-changeset · check-changeset-no-major · check-empty-changeset · release-rehearsal-clone --self-test · docs-audit/check-affected-docs

Red: none.

Not measured — declared, not counted as green:

  • check:type-check-debt (--re-measure) — needs the whole workspace built; its structural half (check:type-check-coverage) is green. CI owns it.
  • docs-audit/check-drift-comment — needs PR context that does not exist locally.
  • The live-database cells: 8 skipped files / 127 skipped tests in driver-sql, and 3 skipped in sql-driver-11389-date-tz-skew.test.ts (set OS_TEST_POSTGRES_URL / OS_TEST_MYSQL_URL). No PostgreSQL or MySQL server exists in this container; CI's Temporal Conformance (live PG + MySQL) job runs them. pgnative's new wire pin is asserted through the recording-connection probe, not against a live server.
  • The 48 downstream consumers of driver-sql were not swept in full — narrowed, deliberately, to the two packages that actually subclass SqlDriver and therefore inherit the changed gate (driver-sqlite-wasm, driver-turso). Both resolve SqlDriver through package exportsdist, so driver-sql was rebuilt first and the reach proved on disk before the runs: SQL_DIALECT_EMISSION_UNSUPPORTED ×1, POSTGRES_WIRE_ONLY_CLIENTS ×11, 'pgnative' ×1 in dist/index.js.

Ablation — one leg, prediction written first

Prediction recorded before the mutation (scratchpad ablation-prediction.txt): direction red, baseline 0 failures → exactly 5, named test by test, with the pgnative cases, the wire/timeout cases and both not.toThrow() cases predicted to stay green (an absent refusal cannot redden an assertion that nothing is thrown).

Mutation: assertDialectEmits returns unconditionally. Proved on disk before any result was read — grep -cF on the original predicate 1 → 0, on the injected marker 0 → 1, git diff --stat 1 file / 1 insertion / 1 deletion. Restored under trap … EXIT INT TERM; restore verified by an empty git diff --stat, clean git status, predicate back at 1 and marker at 0.

Result — 5 failed | 24 passed, and the five are the five predicted, by name:

× 11991 … > initObjects refuses with ONE coded, actionable error
× 11991 … > the message names the client, the supported set and the way out
× 11991 … > refuses BEFORE any DDL runs — nothing is half-built
× 11991 … > emission and the refusal are complements, never overlapping
× 11550 … > redshift and cockroachdb parse pg wire and are REFUSED for DDL

No rebuild was needed for this leg and the reason was checked rather than assumed: both suites import ./sql-driver.js relative, inside the same package, so vitest resolves the mutated source with no exportsdist hop. The lock wrapper reported command-exit 0 for that hold while the inner vitest exit was 1 — the per-command exit is the verdict, and it is the one quoted here.

Not done, on purpose

Generated by Claude Code


Generated by Claude Code

… for redshift/cockroachdb, pgnative in the Postgres family

Lands the maintainer ruling of 2026-08-25 (option C, plus pgnative joining the
Postgres family). The pair `cockroachdb, redshift` is declared once as
POSTGRES_WIRE_ONLY_CLIENTS; the connect-timeout table, the wire table and the
new DDL refusal all extend the emission sets through it. `pgnative` becomes a
member of POSTGRES_EMIT_CLIENTS and reaches the other two tables by derivation.

A redshift/cockroachdb datasource reaching schema DDL now gets
UnsupportedDialectEmissionError — SQL_DIALECT_EMISSION_UNSUPPORTED / 501 —
before any statement is issued, with the supported clients and the
skipSchemaSync posture named in the message. mariadb stays out of scope:
neither recognised nor refused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6HFzyH98W1YaQXhJUJt6o
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 26, 2026
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/driver-sql, @objectstack/spec, touching 14 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/drivers/driver-sql/src/index.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

10 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via ERROR_CODE_LEDGER (symbol))
  • content/docs/api/error-catalog.mdx (via ERROR_CODE_LEDGER (symbol))
  • content/docs/api/error-handling-server.mdx (via ERROR_CODE_LEDGER (symbol))
  • content/docs/data-modeling/drivers.mdx (via SqlDriver (symbol))
  • content/docs/data-modeling/index.mdx (via SqlDriver (symbol))
  • content/docs/kernel/contracts/data-engine.mdx (via ERROR_CODE_LEDGER (symbol))
  • content/docs/plugins/packages.mdx (via SqlDriver (symbol))
  • content/docs/protocol/kernel/index.mdx (via SqlDriver (symbol))
  • content/docs/protocol/kernel/lifecycle.mdx (via SqlDriver (symbol))
  • content/docs/protocol/objectql/query-syntax.mdx (via SqlDriver (symbol))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via ERROR_CODE_LEDGER (symbol), SqlDriver (symbol))

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.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/drivers/driver-sql/src/index.ts) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 128 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f93df4dbe314d5133f4a8c395b255ba0e2aeeaffpackageMentionDocs.

Which tree this was computed on

This run read content/docs from fbb2923bd24f556fd2050922376ed79e336d8a3b — the merge of head 78586928e22ad42c435c09b62169b4f95719c4e9 into base f93df4dbe314d5133f4a8c395b255ba0e2aeeaff, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin fbb2923bd24f556fd2050922376ed79e336d8a3b && git checkout fbb2923bd24f556fd2050922376ed79e336d8a3b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f93df4dbe314d5133f4a8c395b255ba0e2aeeaff 78586928e22ad42c435c09b62169b4f95719c4e9 && git checkout -B drift-repro f93df4dbe314d5133f4a8c395b255ba0e2aeeaff && git merge --no-ff 78586928e22ad42c435c09b62169b4f95719c4e9

node scripts/docs-audit/affected-docs.mjs --json f93df4dbe314d5133f4a8c395b255ba0e2aeeaff

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f93df4dbe314d5133f4a8c395b255ba0e2aeeaff → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-warren
os-warren marked this pull request as ready for review August 26, 2026 13:55
@os-warren
os-warren enabled auto-merge August 26, 2026 13:56

Copy link
Copy Markdown
Collaborator Author

PM review — accepted, flipped ready, auto-merge armed. One protocol failure of mine first.

⛔ I never posted the claim comment on #11991

The dev's report says the card claimed one was posted and none exists. They are right. I ran the assign, and the comment I wrote in that same turn went to #5499 — a different card. Verified now: before the dev's own reports, #11991 carried exactly one comment, the triage note.

Under this repo's rule a claim is assign plus a claim comment carrying the session ID and branch, precisely because all agents share one GitHub identity and the assignee field cannot tell anyone whose claim it is. So for the whole of this dispatch the card was assigned to an identity with no evidence of which session held it. Another seat re-reading the comments — the check that is supposed to catch a double-dispatch — would have found nothing and been entitled to take it.

The dev did the right thing: looked for a competing session ID, found none, proceeded, and reported the gap rather than assuming. The claim record is now posted retroactively on #11991, dated honestly.

The card was partly stale, and the dev re-measured instead of implementing it

The card's 2026-08-24 drift reading — "identity getter only pg/postgresql, timeout table also cockroachdb, wire table also redshift"no longer describes the tree. PR #11783 had already converged the three tables onto one source, and #11784 gave redshift its timeout row.

What was not converged is the extension: the pair cockroachdb + redshift was hand-written into DIALECT_CONNECT_TIMEOUT and again into POSTGRES_WIRE_CLIENTS — and the new refusal would have been a third copy. So part 1 landed as naming that extension once, POSTGRES_WIRE_ONLY_CLIENTS. That is the ruling's actual intent applied to the tree as it is, rather than the card's words applied to a tree that has moved. Exactly right, and exactly the re-verification I failed to do on another card today.

The convergence proves itself. pgnative was added to POSTGRES_EMIT_CLIENTS and nothing else was edited — its wire-table and connect-timeout memberships arrived by derivation, with no edit to either table. A convergence you have to hand-write into three places is not a convergence; one where adding a member in one place propagates is. That is the strongest available evidence part 1 actually landed, and it is structural rather than assertive. And no line numbers appear anywhere in the PR — every anchor is a symbol name.

The refusal

assertDialectEmits fires from the existing assertSchemaMutable choke point, after the unchanged schemaMode check, throwing SQL_DIALECT_EMISSION_UNSUPPORTED / 501 with structured client / operation / supportedClients and a message rendered from the driver's own tables — so the supported list cannot drift from the tables that decide it. It throws before ensureDatabaseExists and before the first hasTable probe, pinned by a real round-trip proving no table was created. "Refuses loudly" and "refuses before anything is half-built" are different claims and only the second one matters to an operator mid-deploy.

Two judgement calls I want on the record because both are precedent-driven rather than invented:

Ablation

Prediction written to a file before the mutation ran, naming five tests individually and naming what must stay green — every pgnative case, the membership cases, and both not.toThrow() cases, on the reasoning that an absent refusal cannot redden an assertion that nothing is thrown. Observed 5 failed / 24 passed, the same five by name. The mutation script aborts on an anchor count ≠ 1 and voids the reading if post-mutation counts disagree — the check that stops a no-op edit from passing as a mutation.

⚠️ The wrapper lied again, and the dev caught it

"The verify-lock wrapper printed VERDICT command-exit 0 for that hold while the inner vitest exit was 1."

That is the #12288 / #12365 family, live for the second time today, and this instance is the sharpest yet: the wrapper reported success for a run that had failed. The dispatch warned about it and the dev read the per-command exit anyway. Cross-posting to #12365 as evidence.

The 9 gate families — the dev's refusal to file blind was right

They declined to file the "layout moved under a gate that still spells the old path" observation because they could not run the mandated dedup read, and reasoned that a card is "very likely already open" since the tool prints it on every dispatch. It is: #12514, pm:queue / domain:devx, naming the same nine families. And its root cause has its own card, #12568 (extensionless module specifiers read as dead paths), already closed. Filing would have minted a duplicate. Corroboration posted there instead — three independent dev seats hit this today.

Declared narrowings

The 48 downstream consumers were not swept in full; narrowed to the two packages that actually subclass SqlDriver and inherit the changed gate, with driver-sql rebuilt first and dist reach proved on disk (SQL_DIALECT_EMISSION_UNSUPPORTED ×1, POSTGRES_WIRE_ONLY_CLIENTS ×11, pgnative ×1 in dist/index.js) — because both resolve through package exports, not source. pgnative's new wire pin is asserted through the recording-connection probe, not a live server, and that is declared as a skip rather than counted as a pass.

CI is the remaining gate, including the live PG/MySQL cells this container cannot run.


Generated by Claude Code

…UPPORTED

CI's `Type Check · source gates` was red on `check:docs`, which named both
files and the exact remedy. The new ErrorCode union member added by this PR's
ERROR_CODE_LEDGER registration flows into two GENERATED reference pages.

Regenerated with the repo's own tooling (gen:schema then gen:docs); the diff is
the two files CI named and nothing else, and `check:docs` now reports
'229 generated files in sync with packages/spec'.

These pages are generated and carry a do-not-hand-edit banner; nothing here was
written by hand.

Copy link
Copy Markdown
Collaborator Author

CI was red on check:docs — fixed, head now 78586928e2

Type Check · source gates failed. The rollup only said "lane typecheck-source-gates concluded failure"; the cause is one level down, and the gate named both the files and the remedy:

✗ content/docs/references/ is out of date with packages/spec:
  ~ content/docs/references/api/contract.mdx
  ~ content/docs/references/api/error-code-ledger.mdx

These files are GENERATED — do not hand-edit them. Regenerate and commit:
  pnpm --filter @objectstack/spec gen:schema && pnpm --filter @objectstack/spec gen:docs

The SQL_DIALECT_EMISSION_UNSUPPORTED registration this PR adds to ERROR_CODE_LEDGER becomes a member of the generated ErrorCode union, and that union is rendered into two reference pages. Regenerated with the repo's own tooling — the diff is the two files CI named, +3/−1, nothing else — and check:docs now reports "229 generated files in sync with packages/spec".

The whole content of the change is the code appearing where it belongs:

+ * `SQL_DIALECT_EMISSION_UNSUPPORTED`        (error-code-ledger.mdx)
+ … | 'VALUE_OUT_OF_RANGE' | … +289 more>     (contract.mdx — the union's rendered arity)

Nothing hand-edited; both pages carry a do-not-hand-edit banner.

Why it reached CI

⚠️ This is the second generated-artifact miss to reach CI today on a PR that registered a new exported name, and the second where the local derivation could not name the gate that would have caught it. #12585 was check:api-surface; this is check:docs. Both are in #12514's nine families whose declared path population matches nothing in the tree.

The pattern is now specific enough to state: registering a name into a generated union has a fan-out into artifacts that the path derivation does not follow. The report's own gate list ran check:docs and it was green at the time — the ledger entry landed after, or the run predated the final commit. Either way, the instrument that should have re-caught it at the final head could not be selected. Cross-posted to #12514.

For the next card of this shape, the cheap rule: if the diff adds a member to a generated union, run check:docs, check:api-surface and check:export-origins by hand at the final head regardless of the derived union.

Everything else on the previous head was green, including Temporal Conformance (live PG + MySQL), driver-sql's full suite and the repo-wide lint. Auto-merge stays armed; this returns to the queue when the checks re-run.


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sql-driver: one emission-identity source — loud refusal for redshift/cockroachdb DDL, pgnative joins the Postgres family (#11756 ruling)

2 participants