mcp: session-notes — PBS content-hub sync — base schema fix, seeder, pbs-video-manager forward-fix

This commit is contained in:
Lovebug MCP 2026-06-21 09:49:57 +00:00
parent a0d8e38f8a
commit 0e6c1dd81c

View File

@ -0,0 +1,81 @@
---
created: '2026-06-21'
path: Sources/Dev
project: pbs-membership-platform-overview
tags:
- pbs
- membership
- content-hub
- pbs-video-manager
- mysql
- schema
- seeder
- docker
- ansible
- garden
type: session-notes
---
## Outcome
Continuing the LOE1 push from 2026-06-19 (see [[pbs-membership-phases-1-2-3a-deploy]]). Today's chain (2026-06-20 → 2026-06-21) was supposed to be a quick fix for one pbs-api crash but became a 4-worker reconciliation of the content-hub schema across three sources of truth. Final state: **sandbox is fully synced to current pbs-video-manager `main`** — content hub schema, app, and seeder all in lockstep.
**Workers run this chain (W7 → W10, plus the in-flight W11 for Phase 3b):**
1. **W7 — `00_base_schema.j2` install + retire `01`** (2026-06-20 → 2026-06-21). Travis provided a fresh prod dump of `pbs_automation`. Placed as `00_base_schema.j2` (478 lines), deleted the stale `01_pbs_custom_tables.j2`, applied to the live sandbox after a `DROP DATABASE pbs_automation` (provably empty per `COUNT(*)=0`). pbs-api crash-loop resolved — root cause was a signed/unsigned FK type mismatch (`instagram_posts.pbs_post_id` signed BIGINT vs stale 01's `pbs_recipes.post_id` `bigint UNSIGNED`), not the reported "missing pbs_recipes". Branch `fix/pbs-base-schema` @ `3b524f9` on gitea.
2. **W8 — enable pbs-seeder** (2026-06-21). Uncommented the seeder import in `roles/dockers/tasks/main.yml`, provisioned the `pbs-seeder` container. Three distinct failures fixed in-scope: (a) compose `--profile seed` interaction with shared `common/deploy.yml` doing `up -d` with no default-profile service (added `container_skip_start` flag); (b) vendored seeder models ahead of deployed schema; (c) stale pbs-api image whose `/api/seed` predated `project_types` support. W8 trimmed the seeder to the 10-model subset and added a direct-DB `_seed_project_types` workaround — got data flowing but masked the deeper drift. Branch `feat/enable-pbs-seeder` @ `5412b1a` on gitea. Content hub UI started rendering populated cards.
3. **W9 — content hub schema sync (step 1 of 2)** (2026-06-21). Derived `01_content_hub.j2` from `pbs-video-manager/sql/migrations/001_scene_management.sql` (correctly chose authoritative SQL over the broken ORM). Added 6 tables + 2 columns + 5 FKs + a small Prep default-scene seed, all idempotent via `IF NOT EXISTS` and information_schema-guarded ALTERs (because MySQL 8 doesn't support `ADD COLUMN IF NOT EXISTS` — that's MariaDB-only). Applied to sandbox; pbs-api still healthy. Then **stopped at the upstream blocker:** `pbs-video-manager/pbs_manager/db/models.py` on local `main` is half-reverted — the synthetic-base commit `7083e2d` from the promote-branch merge stripped 191 lines (6 scene/metadata classes + `Project.outline`/`Project.treatment` + relationships); never restored. App boots because `api/routes.py` doesn't import `import_service`/`outline_service` — they're stranded dead code. Branch `feat/content-hub-schema-sync` @ `55ba539` on gitea.
4. **W10 — forward-fix + close out content-hub chain** (2026-06-21). Touched both repos (cwd `/opt/projects/pbs/`). **pbs-video-manager fix:** branch `fix/restore-scene-metadata-models` → merged `--no-ff` to `main` at `429383d`. Restored 6 classes + 2 columns + 4 relationships, AND the Phase-8 `STATUSES` tuple (had silently lost `planned`/`scripted`). Hand-spliced rather than 3-way merge (3-way would have deleted the instagram models — synthetic base has them, last-good state `c7856d6` doesn't). Pushed to gitea `main` + `master`, GitHub untouched. **pbs-api rebuilt** on the sandbox (force-recreate; herbylab is built-image mode, dev-mode is `pbswp`-group only). Recovered from a latent `init_db()` `create_all` multi-worker race (self-healed via docker restart; latent pre-existing flake). **Re-vendored seeder models** — full 9-table restoration (the 6 + 3 Phase-6 ones W8's trim had also dropped). **Un-trimmed db_seeder.py**, removed W8's `_seed_project_types` workaround. **Extended `01_content_hub.j2`** with 3 additive Phase-6 columns (`platform_posts.title`, `projects.trello_card_id`, `projects.trello_last_synced_at`) when the seed crashed on `Unknown column 'title'` — judged in-scope (additive, idempotent, same pattern as W9). Re-seeded with `SEEDER_RESET=true`. **All 16 tables populated**, including 4 scenes, 6 scene_items, 2 default_scenes, 6 default_scene_items, 4 project_metadata_fields, 11 project_metadata rows. Garden side unaffected (regression-checked: `/the-garden/` 302; `/api/interactions/check` 401). wordpress-install branch `feat/content-hub-schema-sync` @ `af34f4c` on gitea.
5. **W11 — Phase 3b: Saved Recipes card live count** (in flight at time of writing). Wires the Garden home's Saved Recipes card subtitle to a real `count(user_interactions)` instead of the placeholder. Branch `feat/phase-3b-saved-count` off `feat/phase-3a-garden-skeleton`. Lands in thread #602.
## Topics Covered
- **The "MariaDB" note in 582 was wrong.** No MariaDB anywhere — herbylab and prod are both MySQL 8. The 582 reference came from somewhere stale (possibly an earlier rough draft). Worth removing if 582 gets re-checkpointed.
- **`pbs_automation` schema management had three sources of truth** (`01_pbs_custom_tables.j2` in wordpress-install, SQLAlchemy models in pbs-video-manager, plus Travis's hand modifications in prod). The mismatch chain we untangled today is the predictable outcome of that. Travis raised the Alembic-centralization conversation mid-session and asked Lovebug to consider scoping it as a separate project — answer was "yes, exhibit A is right in front of us" (the silent ORM revert that nobody noticed because dead-code services masked it). Not yet scoped into a vault plan — left open at session end.
- **The synthetic-base commit pattern from 585's promote-branch merge** has a real failure mode: any file that exists in the synthetic base AND the last-good upstream state AND was being patched will silently end up at whichever wins the manual merge — usually the synthetic base, because it's the "newer" commit in the branch's eyes. Both `models.py` and `conftest.py` got hit by this. The hand-splice diff strategy W10 used (against the last-good upstream commit `c7856d6`, not the synthetic base) is the right reconciliation method for future cases.
- **Seeder ergonomics:** the seeder is a one-shot container behind `compose --profile seed`. The shared `common/deploy.yml` task in the dockers role ran `up -d` with no service argument, which fails on profile-gated services because no default-profile services exist. W8 fixed this with a `container_skip_start` flag — generic, useful for any future profile-gated containers.
- **MySQL 8 idempotency idioms:** `CREATE TABLE IF NOT EXISTS` and `CREATE USER IF NOT EXISTS` work natively. `ADD COLUMN IF NOT EXISTS` does NOT (MariaDB-only); use `information_schema.COLUMNS` guarded prepared statements instead. `ADD CONSTRAINT IF NOT EXISTS` similarly doesn't exist; either guard via information_schema or accept that the patch is first-time-init-only.
- **W10's scope expansion within a single worker** (vs the W9→W10 split originally planned) proved out the "keep interconnected work in one worker" feedback — W10's extending `01_content_hub.j2` mid-flow when the seed crashed was a judgment call only possible because the same context had the schema patch, the seeder code, and the live sandbox state all in mind. A handoff to a second worker would have stalled there.
## Key Learnings
- **a-review picks up stale working-tree files.** W10's a-review on the seeder un-trim flagged a CRITICAL + WARNING that turned out to be false positives — it had ingested a stale `ansible.log` from before the schema patch landed and "found" the old `Unknown column 'projects.outline'` crash. Verification against the live DB voided both findings. Worth knowing for future runs — if a-review flags something dramatic, check whether it's reading old logs or scratch files in the working tree before reacting.
- **`docker_compose_v2` doesn't rebuild on source changes by default** (already learned in W3's deploy report; reinforced this session). Member-garden has `container_compose_build: always`; pbs-api doesn't (per W10's mechanism notes — required manual `docker compose build --no-cache && up -d --force-recreate`). Worth normalizing across containers.
- **The github role pulls master**, not main, per the dockers role's `github_repo_version` default. To make a "main-as-canonical-source-of-truth" workflow work, you either push to both branches or change the role default. W10 did both-branches as the lowest-friction path.
- **pytest can't run pbs-video-manager scene-management tests** because `conftest.py` lost its `session`/`project` fixtures in the same synthetic revert that hit models.py. Fixture reconciliation is a separate cleanup — flagged.
- **`init_db()` `create_all` racing across gunicorn workers** is a real flake in built-image mode (dev-mode bind-mount + single-process avoids it; non-dev multi-worker hits MySQL 1050 on the first boot, then docker restart policy recovers). Latent pre-existing; should be gated to a single worker or moved out of app-boot entirely (init-SQL layer authority).
## Follow-ons
### From W7-W10 specifically
- [ ] **`pbs-video-manager/conftest.py` fixture reconciliation** — same synthetic revert dropped `session`/`project` fixtures. The current conftest uses a Flask-app paradigm with `make_*` fixtures only. Scene-management pytest suite cannot run until reconciled. Last good state is `c7856d6` for the conftest too.
- [ ] **`init_db()` multi-worker race** in non-dev mode — gate `create_all` to a single worker (gunicorn `--preload` + first-worker-only pattern, or move schema authority entirely to the init-SQL layer).
- [ ] **Normalize `container_compose_build: always`** across the dockers role for containers that build from clones (pbs-api especially). Already in place for member-garden; pbs-api needed manual override this session.
- [ ] **`pbs-video-manager` two-branch confusion** — local main, gitea main, gitea master, GitHub origin, GitHub main, GitHub master. Different things on each. Worth a cleanup pass: pick `main` as canonical, retire `master`, update the github role default.
### Schema management (the bigger conversation Travis raised)
- [ ] **Scope an Alembic-centralization vault plan** for `pbs_automation`. Same pattern member-garden already uses for `pbs_garden`. Alembic in pbs-video-manager owns the schema; init-SQL in wordpress-install shrinks to `CREATE DATABASE` + `CREATE USER` + grants. Drift between sandbox/prod becomes a migration backlog (`alembic current`), not silent column divergence. Today's session is exhibit A for why this matters. One-time cost: retro-generate Alembic baseline from current prod with `--autogenerate` + manual review pass to capture Travis's hand-rolled prod changes. Maybe a half-day of work; permanent ergonomics win. Travis acknowledged the case; not yet scoped into a doc.
### Carryover (from yesterday's session note, still open)
- [ ] **CSRF nonce on save/unsave** — Phase 2 production-readiness; needs `X-WP-Nonce` contract added.
- [ ] **member-garden migrations not automated** — bake into image OR add ansible task.
- [ ] **`X-Forwarded-Proto` not honored** in member-garden Flask — ProxyFix.
- [ ] **WP logout confirmation nonce** — needs WP-side mu-plugin route for seamless logout.
- [ ] **WPCode save-button browser test** — needs real recipe content on sandbox + browser session.
- [ ] **Brand color + Sunnie SVG assets** for save button + garden.
- [ ] **`pbs-video-manager` GitHub push** — local main + gitea main/master at `429383d`. GitHub stays behind until Travis is ready for prod release. The merge-to-prod is its own event (with the half-revert mistake also being repaired in this same merge, archaeology-friendly).
## Decisions worth keeping
- **Hand-splice over 3-way merge for reverted code** when the synthetic base and the last-good upstream both have legitimate-but-disjoint content. 3-way against the synthetic base would have deleted the instagram models; 3-way against c7856d6 would have deleted post-revert Phase-6 work. Hand-splicing both directions onto current HEAD is the cleaner reconciliation.
- **Forward-commit-and-restore rather than history rewrite** for repairing a stripped file. Mistake stays visible in `git log` for archaeology and learning; working state is fixed. Travis explicitly checked the motivation here — destructive history rewrite would have been wrong even though it'd "look cleaner".
- **Extending an upstream worker's patch when the dependent step crashes** with an additive, in-pattern fix (W10 extending W9's `01_content_hub.j2` with 3 more columns). The alternative — stop, hand off to a third worker with full context, repeat — burns wall-clock and risks context loss. Judged in-scope = additive, on-branch, same idempotent pattern as the original patch.
- **Sandbox-throwaway gives latitude.** Drop pbs_automation DB, force-rebuild images, SEEDER_RESET=true — all routine when nothing on the sandbox is worth preserving. Snapshot rollback stays gated because it touches WP install state (different concern).
- **Two-remote push policy enforced consistently** across all today's commits. Gitea = test/sandbox truth; GitHub = production; Travis controls when prod gets the changes. No worker pushed to GitHub origin this session.