fix(introspect): a serial column is reported, not imported as an ordinary default (#119) - #124
Merged
Merged
Conversation
…nary default (#119) Postgres expands serial into three things — a plain integer column, a sequence it owns, and a nextval default — and marks none of them as an identity, so attidentity is empty and the identity check did not see it. The nextval default carries a regclass cast, which is not the column's own type, so stripCast had no reason to touch it either. The column therefore arrived as an ordinary bigint whose default happened to name a sequence. The table then reported clean and the DDL it produced did not run: FAIL create table coprocess_steps — relation "coprocess_steps_seq_seq" does not exist FAIL index coprocess_steps_session_idx — relation "coprocess_steps" does not exist Two failures from one cause, and no warning before the bootstrap broke — the quiet failure the report exists to prevent. The column is refused rather than stripped of its default alone. Declaring it without the default the database has would leave every Diff proposing to add one back, which is the permanently-red gate stripCast was written to avoid. On the corpus that found it, process-mining goes from 2 apply failures and a residual of 1 to zero of both — a fixpoint — and coprocess_steps moves from a false clean to an honest partial. No other schema in the corpus changes. Declaring sequences outright remains open; this fixes the lie, not the gap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three buckets, kept apart deliberately: opinions the DSL enforces, practices it only recommends, and refusals that are merely unbuilt. Conflating the first and third is how a missing feature gets rewritten as a principle, so the gaps are listed on the same page with their issue numbers. Each enforced practice cites its ADR and states the reason it would survive if sqlb vanished — which is the rule the two schema surveys were triaged by. Counts come from those surveys: valiro-go (68 tables) and studio-apps (ten deployments, 233 tables). ADR-0015's prefixing is the clearest case — studio-apps documents the same rule and enforces it in CI, and it still drifted to 8 violations, whose tables are exactly the ones that collided when the survey built one database per app. A rule enforced by review drifts; a prefix applied by the registry cannot. Also records that ADR-0034's revisit trigger has fired again: composite primary keys appear in 4 of 10 apps, on configuration tables no REST resource mounts. The ADR already names the fix — move the refusal into rest.Resource's mount check rather than lift it — and asked for this to be said in the schema docs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… naming Written to be argued from when meeting a codebase on sqlc or anything else, so every practice now leads with the reason it holds whether or not sqlb exists. A practice justified by "the tool wants it" is special pleading, and an adopter is right to reject it. Three domains rather than a walk through the ADR list: table and column names, SQL and schema design, REST. Each entry states the practice, the independent reason, what goes wrong in real schemas without it, and the mechanism — or admits there is none. New material, measured across the same 301 tables: - timestamptz is enforced and the corpus already agrees — all 82 naive timestamp columns are inside goose's own bookkeeping tables, none in an application table - 8 foreign-key columns have no index leading with that column, which is the one practice here that is only half automatic: ExternalRef adds a join index because it emits no FK, a local Ref does not - 227 varchar against 481 text The gaps stay on the same page, and the overstated claims are marked as such: #116 is unresolved, ADR-0034's refusal is wider than its justification and the narrowing is what to argue, and restcompat only judges the contract sqlb generates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #119, and writes down the schema opinions the survey work kept leaning on.
The bug
Postgres expands
serialinto three separate things — a plain integer column, a sequence it owns, and anextvaldefault — and marks none of them as an identity. Soattidentityis empty and the identity check inbuildColumndid not see it. The default carries aregclasscast, which is not the column's own type, sostripCasthad no reason to touch it either.The column therefore arrived as an ordinary
bigintwhose default happened to name a sequence, the table reported clean, and the DDL it produced did not run:Two failures from one cause, with no warning before the bootstrap broke.
TestBuildReportsWhatItCannotRepresentalready states the principle this violated: "The failure that matters is the quiet one."The fix
Refuse the column and report it, exactly as an identity column is refused.
The column goes rather than only its default. Declaring it without the default the database has would leave every
Diffproposing to add one back — the permanently-red gatestripCastwas written to avoid, and the reason that function's comment says a gate red for a reason that is not real is worse than no gate.Verified against the schema that found it
process-mining, the studio-apps deployment where this surfaced:coprocess_stepsThe table moves from a false clean to an honest partial, and the schema reaches a fixpoint. Re-running the other nine deployments shows no other schema changes — 233 tables, 0 apply failures fleet-wide.
Tests cover the spellings that reach the catalog (
bigserial,serial, schema-qualified, unquoted, uppercase) and, in the other direction, that a default merely mentioning a sequence-like name —'nextval'::text,'seq_' || id— still imports as an ordinary column.Declaring sequences outright remains open. This fixes the lie, not the gap; identity columns are still refused too.
docs/best-practices.md
Three buckets, kept apart on purpose: opinions the DSL enforces (each citing its ADR), practices it only recommends, and refusals that are merely unbuilt. Conflating the first and third is how a missing feature gets rewritten as a principle, so the gaps are listed on the same page with their issue numbers.
Counts come from the two surveys — valiro-go (68 tables) and studio-apps (ten deployments, 233 tables).
Two things came out of writing it:
agentdeploy's(deployment_id, key)configuration tables that no REST resource mounts. The ADR already names the fix — move the refusal intorest.Resource's mount check rather than lift it — and asked for the stance to be stated in the schema docs, which this does.🤖 Generated with Claude Code