Collapse alembic 0001..0089 into one baseline #245

Merged
bvandeusen merged 2 commits from dev into main 2026-09-01 01:30:52 -04:00
Showing only changes of commit aa71cbbdbf - Show all commits
+42 -13
View File
@@ -39,22 +39,31 @@ Four things still had to be added by hand, because they are not in the models:
Extensions are database objects, not table metadata, so no model can carry Extensions are database objects, not table metadata, so no model can carry
them. `IF NOT EXISTS` because a re-run must not fail. them. `IF NOT EXISTS` because a re-run must not fail.
2. **The two settings singleton rows.** 0002 and 0003 did not only build 2. **Three seed inserts** — the two settings singletons (0002, 0003) and the
schema — they each inserted a row: three hygiene system tags (0075). Some migrations did not only build schema;
they inserted rows the product needs in order to function, and nothing in
the application ever creates them. Every consumer reads them with
`scalar_one()`, which RAISES `NoResultFound` on an empty result rather than
returning None, so their absence is a crash and not a degradation.
INSERT INTO import_settings (id) VALUES (1) Distinguishing these from the other data statements in the chain is the
INSERT INTO ml_settings (id) VALUES (1) whole trick, and the rule turns out to be mechanical:
and nothing in the application ever creates them. `ImportSettings.load()` * `INSERT ... VALUES (...)` with literal values is a SEED. It creates
and `MLSettings.load()` are `select(...).scalar_one()`, which RAISES something the product ships. It must be carried.
`NoResultFound` on an empty result rather than returning None. So a baseline * `INSERT ... SELECT ... FROM <table>` is a BACKFILL. It derives rows
built from models alone would produce those two tables empty and crash a from rows that already exist, so on an empty database it inserts
fresh install on its first settings access. nothing and carrying it would be pointless. 0034 (artist_visit), 0040
and 0047 (series_chapter) are all of this shape and are correctly
absent here.
This is worth dwelling on, because it is invisible to every automated check This category is invisible to every automated check this project has:
this project has: `baseline.yml` compares SCHEMA, and the schema would have `baseline.yml` compares SCHEMA, and a baseline missing all three seeds still
been a perfect match. Only running the application against a fresh database produces a byte-identical schema and a perfectly green diff. What caught the
finds it. system tags was the integration suite — 36 tests failing on
`NoResultFound` — after a first version of this file shipped with only the
two settings rows. A first-run check against the real application is the
only thing that finds this class of defect.
3. **The `pgvector` import.** Autogenerate emits qualified 3. **The `pgvector` import.** Autogenerate emits qualified
`pgvector.sqlalchemy.vector.VECTOR(...)` references without importing the `pgvector.sqlalchemy.vector.VECTOR(...)` references without importing the
@@ -794,6 +803,26 @@ def upgrade() -> None:
op.execute("INSERT INTO import_settings (id) VALUES (1)") op.execute("INSERT INTO import_settings (id) VALUES (1)")
op.execute("INSERT INTO ml_settings (id) VALUES (1)") op.execute("INSERT INTO ml_settings (id) VALUES (1)")
# The three hygiene system tags, from 0075. These are PRODUCT data, not
# operator configuration — 0075's own docstring says so: "the fix keys on
# SYSTEM tags the product ships". The presentation and process auto-apply
# sweeps look them up with scalar_one(), so without these rows those
# features raise NoResultFound rather than degrading.
#
# 0075 adopted an existing same-name general tag before inserting, because
# an operator might already have tagged `wip` by hand. That cannot happen
# on the empty database this file runs against, but the guard is kept: it
# costs nothing and makes the statement safe to re-run.
for _name in ("wip", "banner", "editor screenshot"):
op.execute(
sa.text(
"INSERT INTO tag (name, kind, is_system) "
"SELECT :name, 'general', true WHERE NOT EXISTS ("
" SELECT 1 FROM tag WHERE lower(name) = lower(:name)"
")"
).bindparams(name=_name)
)
def downgrade() -> None: def downgrade() -> None:
"""Deliberately not implemented. """Deliberately not implemented.