26 Alembic revisions and none had ever been executed by CI. `alembic upgrade
head` ran for the first time when the operator's container started, and the
schema the migrations build had never been checked against the models that read
it. M13 dropped three columns and rebuilt a STORED GENERATED column with nothing
watching but a server boot.
Copied from FabledScribe's `integration` job, which had already solved the parts
that are easy to get wrong — and which are family rules precisely because they
were: a separator-free job key with no `name:` (act_runner derives the service
container name from the truncated display name, and the discovery step filters
`docker ps` by it), bridge-IP resolution because service hostnames aren't
routable on this runner, and a Python readiness wait because `run:` is busybox
sh with no `/dev/tcp`.
`postgres:16-alpine` to match the production compose. The schema is built by
real migrations, never metadata.create_all — that step IS the migration test.
Six tests, each pinning something that has only ever been checked by hand:
- an ORM insert against the migrated schema, which is the model/migration
agreement nothing has verified until now;
- `notes.title`, `notes.kind` and `note_revisions.title` are actually gone, and
`note_links` with them — a silently no-op migration shows up here;
- the rebuilt `search_vector` indexes both the name and the body, which matters
because 0026 had to DROP and recreate a generated column rather than alter it;
- a note keeps its body AND its items, the shape step 2 made normal;
- `_apply_note_items` leaves items alone when a change doesn't mention them —
the data-loss path step 2 removed, pinned so its return would be caught;
- a note with no body is still named by its first item, the hole that made
removing the title unsafe until checklists stopped being their own kind.
Runs for visibility; does not gate the build, matching `test` and Scribe.
No local equivalent: running it means standing up Postgres on the workstation,
which rule 12 reserves for an explicit request. Documented in ci-requirements
alongside the Rust, Kotlin and frontend gates.
Foundation & Identity backend for ThoughtSync:
- Quart app factory (create_app) with /api/health + SPA history-fallback
- async SQLAlchemy 2.0 + asyncpg engine/session (lazy; boots without a DB)
- native email+password auth via signed-cookie session (register/login/logout/me
+ login_required guard); bcrypt password hashing (72-byte safe)
- multi-user sharing-ACL spine (rule 47): users, groups, group_members, and a
polymorphic shares table + visible_to_user() SQL predicate (owner OR direct
share OR group share) that M1's notes will scope through
- Alembic async env (adapted from family pattern) + 0001 foundation migration
- DB-free unit tests (app/health/auth-guard, password roundtrip, ACL compile)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm