babylon #331 (resolved 2026-06-23 by @positionmanager): position_manager owns the FULL pmv2_autotrade_* migrator; producers (cohort_algo, etc.) DROP all pmv2_autotrade_* DDL from their own migrations and treat those tables as a read-only precondition. Shared-DB safety is achieved by giving each migrating repo a DISTINCT sqlx migration-history table so PM’s history and a producer’s own-table history can’t collide. This RESOLVES the “physical relocation DEFERRED” hold from position-manager-interface-design and REFINES the “one migrator per database” rule from sqlx-shared-migrations-table-trap.
For Agents — the decision in one screen
- Option chosen: (b), per data-ownership principle D3.
- PM owns the FULL
pmv2_autotrade_*migrator —pmv2_autotrade_config+pmv2_autotrade_sources+pmv2_autotrade_orders+ any v2 columns added to those tables.- Producers (cohort_algo) DROP all
pmv2_autotrade_*DDL from their own migrations. These tables become a read-only precondition for producers: cohort READSpmv2_autotrade_config+pmv2_autotrade_sources, and never touches the orders table.- Shared-DB sqlx safety: each repo that migrates the shared Postgres DB MUST use a DISTINCT migration-history table (a custom sqlx
Migratortable name) so PM’s migration history and cohort’s own-table migration history don’t collide.- Producer-private tables (scoring / consensus / positions) STAY in the producer’s own migrator, with that migrator’s own (distinct) history table.
- FREEZE: no
pmv2_autotrade_*schema changes from anyone until PM’s migrator ships. PM will prioritize the migrator spec before the v2 autotrade migrations land.
Why this is the right split — data-ownership principle D3
The whole point: the component that owns the meaning of a table owns its DDL. PM is the execution authority — it is the sole writer/counter of the orders ledger, the breaker counters, and the authoritative caps. So PM owns the migrator for the whole pmv2_autotrade_* family. Producers only read the config + sources rows to learn their knobs (sizing/gating params, per-source (enabled, mode)); reading a table is not owning it, so producers carry no DDL for it.
This is consistent with the meaning-per-operation framing already established in Database Schema (“Split is ownership of MEANING per column/operation, not a table relocation”) — #331 just finishes the job by moving the DDL to match the meaning, instead of leaving it on the retiring monorepo’s migrator.
| Table | Owner (migrator) | cohort_algo’s role |
|---|---|---|
pmv2_autotrade_config | PM | READS (sizing + gating knobs via SignalConfig) |
pmv2_autotrade_sources | PM | READS (per-source (enabled, mode) keyed by source_id) |
pmv2_autotrade_orders | PM | never touches |
| scoring / consensus / positions tables | the producer (cohort) | OWNS, own migrator + own history table |
Shared-DB sqlx safety — distinct migration-history tables
This REFINES the "one migrator per database" rule
sqlx-shared-migrations-table-trap established “ONE migrator per DB / per
_sqlx_migrationstable” because twosqlx::migrate!sets against the same hardcoded_sqlx_migrationstable each throwVersionMissingon the other’s applied versions. #331’s refinement: the constraint is really one migrator per history table, not per database. If PM and cohort each migrate the shared Postgres DB but each points itsMigratorat a DISTINCT history table (a custom table name, not the default_sqlx_migrations), their histories are isolated and theVersionMissingcross-trip can’t happen. Two migrators on one DB is then safe — provided their physical tables are disjoint (which the D3 split guarantees: PM ownspmv2_autotrade_*, cohort owns its scoring/consensus/positions tables).
The mechanism: sqlx’s Migrator supports overriding the history-table name (the default-_sqlx_migrations builder is configurable). Each repo sets its own. This is what lets:
- PM’s migrator track ONLY the
pmv2_autotrade_*(+ any other PM-owned) migrations in PM’s history table, and - cohort’s migrator track ONLY cohort’s producer-private migrations in cohort’s history table,
…against the same database, with no collision.
Disjoint physical tables are still required
Distinct history tables isolate the bookkeeping, not the DDL. Two migrators must not both
CREATE/ALTERthe same physical table (that’s a real conflict, history tables notwithstanding). The D3 ownership split is what keeps the physical table sets disjoint — that’s why the ownership decision and the history-table mechanism go together.
FREEZE — no pmv2_autotrade_* schema changes until PM’s migrator ships
Schema FREEZE on pmv2_autotrade_*
Until PM’s
pmv2_autotrade_*migrator ships, nobody changes thepmv2_autotrade_*schema — not PM (beyond standing the migrator up), not cohort, not any other producer. PM will prioritize the migrator spec BEFORE the v2 autotrade migrations land. Any newpmv2_autotrade_*column/table waits behind that migrator. This prevents a half-migrated shared family while ownership is mid-transfer.
Action items for cohort_algo — IMPLEMENTED (2026-06-23, a3329dc)
DONE — cohort's half of #333 shipped, but the SOLUTION DIVERGED from this plan
The migration-cleanup below SHIPPED to
github.com/wowjeeez/pmv2-cohort-algomain@a3329dc— see cohort-algo-migration-squash-cleanup for the full implementation. Important divergence: action item #3 below (a distinct sqlx history table) turned out to be unnecessary. PM uses Supabase, not sqlx, so cohort is the SOLE sqlx owner of the shared DB — there is no second sqlx migrator to collide with. Instead of isolating history tables, cohort squashed all 44 migrations into ONE idempotentCREATE TABLE IF NOT EXISTSbaseline (only the 12 tables it owns/reads) and setset_ignore_missing(true)on theMigratorto tolerate the deployed DB’s now-orphaned applied versions. This is cleaner than the distinct-history-table approach and supersedes it for this DB.
The plan was (items #1, #2, #4 done as written; #3 superseded):
- Prune the dead legacy
pm_*tables (~28 of them) from cohort_algo’s migrations — these are leftover from the retiringpolymarket_fetchmonorepo and have no reader in cohort_algo. → DONE (~25 dropped; 3pm_*with cohort readers kept). - Drop the
pmv2_autotrade_*DDL now from cohort_algo’s own migrations (the tables become a read-only precondition owned by PM). → DONE (PM’s migrator4712de9recreates them byte-identical, so cohort’s reads survive). Set a distinct sqlx history table for cohort_algo’s migrator→ SUPERSEDED. Unnecessary because cohort is the sole sqlx owner (PM = Supabase). Replaced by squash +set_ignore_missing(true).- FREEZE any
pmv2_autotrade_*schema changes until PM’s migrator ships. → still in force.
Prod DB surgery is @deploy's, not cohort's
The squash is a migration-source change. Reconciling the deployed DB (PM migrator FIRST → ~25
pm_*DROPs via psql, transcript-first → reset_sqlx_migrationsto the baseline) is @deploy’s coordinated prod op — babylon #380 (resolved) → #397. See Out of scope — the prod DB surgery is @deploy’s job.
Tension with the legacy "don't drop pm_* prod tables" rule — scope matters
An earlier standing rule (see bot-pmv2-native-v1-consensus-deleted) was “don’t drop dead
pm_*prod tables, just leave them dormant.” That rule is about the legacypolymarket_fetchprod database. The #331 pruning is scoped to cohort_algo’s own DB surface / migrations in the new pmv2 fleet — a different context where cohort owns its schema cleanly. Keep the two scopes distinct; pruning here is not a reversal of the legacy-prod rule.
Related
- cohort-algo-migration-squash-cleanup — the IMPLEMENTATION of this decision (
a3329dc, 2026-06-23): cohort squashed 44 migrations → ONE idempotent baseline +set_ignore_missing(true). The solution DIVERGED — cohort is the sole sqlx owner (PM = Supabase), so the distinct-history-table plan was unnecessary and was superseded by squash + ignore_missing - sqlx-shared-migrations-table-trap — the rule this decision REFINES: “one migrator per DB” → “one migrator per (distinct) history table,” with disjoint physical tables still required
- position-manager-interface-design — where migration ownership was originally WALKED BACK and physical relocation DEFERRED; #331 is the coordinated single-migrator change that deferral pointed to (PM now owns the full
pmv2_autotrade_*migrator, not just the ledger CODE) - cohort-algo-component — cohort_algo’s standing architecture; the “Shared DB surface” section (cohort READS
pmv2_autotrade_config/pmv2_autotrade_sources, does NOT migrate them) is exactly this decision - cohort-algo-v2-wire-contract-cutover — the companion 331-era change; the v2 gating/sizing knobs cohort READS come from
pmv2_autotrade_config(a PM-owned, read-only-for-cohort table) - bot-pmv2-native-v1-consensus-deleted — the legacy “don’t drop pm_* prod tables” rule; scoped to the retiring monorepo’s DB, NOT cohort_algo’s own surface
- polymarket-fetch — the retiring monorepo the
pm_*tables came from