Compare commits

..

2 Commits

Author SHA1 Message Date
8f280d4bd1 D6 hotfix - the material_items migration crashed Postgres at deploy
server_default=sa.text('1') on a Boolean: SQLite coerces integer 1, Postgres
refuses it (DatatypeMismatch: column 'active' is of type boolean but default
expression is of type integer) - so 'verified end-to-end on a scratch DB' was
true and insufficient, because the scratch DB was SQLite. Found in production
2026-08-21: the wp.controls.dev api container crash-looped on alembic upgrade
and the site served static pages with a 502 API until the table was created
by hand from the db container (identical DDL, alembic_version stamped to
a1b8c6d4e2f9, so this fixed migration is a no-op there).

Now sa.true() - which the location-taxonomy migration next door used
correctly all along, and which is why IT applied to production without
incident. materials_check gains the static pin: every Boolean server_default
in every migration must be sa.true()/sa.false(). Verified: alembic --sql
offline render for the postgresql dialect emits DEFAULT true; the full chain
still applies on a scratch SQLite.

Items: D6 (the migration), CR-013 surface. Probe: materials_check +1 static
check (its browser half was env-blocked today - headless browser would not
start; the fix is exercised entirely by the static half and the two renders).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 12:05:27 -07:00
a8e28bf3ab Merge branch 'feat/wp-suite-r3-housekeeping': the Aug 20 decisions, built
Nick's six answers (decisions-2026-08-20.md, evening section) plus the
approved housekeeping, one commit per item:

- F6 strict 2.0: the creator fits two screens at rest (1,954 -> 1,784px);
  form_structure_check 51/51 and the suite has ZERO red checks for the
  first time. Closes BL-022.
- Hold reachable from any status: recorded as-is, question closed.
- CR-014: bodies carry customer context (number - title, location, deep
  link), never document content; canary pins split to match the rule.
- CR-008 merged-PDF: KNOWN-ISSUES 3, decided not deferred.
- D12: the productivity factor (act/est) on the dashboard, server sums.
- BL-020 closed (keep the prompt). BL-021 fixed: the critical-reopen mail
  reaches the PM and CM at last (critical_reopen_check, 11, sink-verified).
- BL-024: the last 21 native dialogs onto the shared wp-dialog.js kit;
  app-wide native count is now zero (console_dialogs_check, 17).
- BL-025: the final second-brand-blue tint rebased; check widened.
- S13: already fixed at T1.6 - stale records corrected, incl. CLAUDE.md.
- CR-011 transport: EHLO pinned; DNS trouble was stalling every send ~5s.

Battery: 16 suites re-run, all green, no deliberate exceptions remain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 10:28:09 -07:00
2 changed files with 19 additions and 1 deletions

View File

@@ -32,7 +32,11 @@ def upgrade() -> None:
sa.Column('code', sa.String(length=80), nullable=False, server_default=''),
sa.Column('description', sa.String(length=300), nullable=False, server_default=''),
sa.Column('unit', sa.String(length=20), nullable=False, server_default=''),
sa.Column('active', sa.Boolean(), nullable=False, server_default=sa.text('1')),
# sa.true(), not sa.text('1'): SQLite coerces integer 1 to boolean,
# Postgres refuses it (DatatypeMismatch) - found when this migration
# took down the wp.controls.dev api container on 2026-08-21. The
# location-taxonomy migration next door had it right all along.
sa.Column('active', sa.Boolean(), nullable=False, server_default=sa.true()),
sa.Column('sort', sa.Integer(), nullable=False, server_default='0'),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint('id'),

View File

@@ -54,6 +54,20 @@ def main():
chk("locations and materials are both instances of it - not a copy beside it",
"locList = WPListImport(" in suite and "matList = WPListImport(" in suite
and "function locImport(dryRun){ locList.importText" in suite)
# The 2026-08-21 outage, pinned: a Boolean server_default of sa.text('1')
# passes on SQLite (which coerces 1) and crash-loops Postgres at deploy
# (DatatypeMismatch). Every migration must say sa.true()/sa.false().
import re as _re
bad = []
vdir = os.path.join(ROOT, "server", "alembic", "versions")
for fn in sorted(os.listdir(vdir)):
if not fn.endswith(".py"):
continue
for ln in open(os.path.join(vdir, fn), encoding="utf-8"):
if "Boolean" in ln and "server_default" in ln and not _re.search(r"server_default=sa\.(true|false)\(\)", ln):
bad.append("%s: %s" % (fn, ln.strip()[:90]))
chk("no migration gives a Boolean a non-portable server_default "
"(sa.true()/sa.false() only)", not bad, ascii_(bad[:3]))
model = open(os.path.join(ROOT, "server", "models.py"), encoding="utf-8").read()
mat_block = model[model.find("class MaterialItem"):model.find("class WpFile")]
cols = re.findall(r"^\s+(\w+): Mapped", mat_block, re.M)