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 READS pmv2_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 Migrator table 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.

TableOwner (migrator)cohort_algo’s role
pmv2_autotrade_configPMREADS (sizing + gating knobs via SignalConfig)
pmv2_autotrade_sourcesPMREADS (per-source (enabled, mode) keyed by source_id)
pmv2_autotrade_ordersPMnever touches
scoring / consensus / positions tablesthe 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_migrations table” because two sqlx::migrate! sets against the same hardcoded _sqlx_migrations table each throw VersionMissing on 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 its Migrator at a DISTINCT history table (a custom table name, not the default _sqlx_migrations), their histories are isolated and the VersionMissing cross-trip can’t happen. Two migrators on one DB is then safe — provided their physical tables are disjoint (which the D3 split guarantees: PM owns pmv2_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/ALTER the 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 the pmv2_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 new pmv2_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-algo main @ 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 idempotent CREATE TABLE IF NOT EXISTS baseline (only the 12 tables it owns/reads) and set set_ignore_missing(true) on the Migrator to 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):

  1. Prune the dead legacy pm_* tables (~28 of them) from cohort_algo’s migrations — these are leftover from the retiring polymarket_fetch monorepo and have no reader in cohort_algo. → DONE (~25 dropped; 3 pm_* with cohort readers kept).
  2. 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 migrator 4712de9 recreates them byte-identical, so cohort’s reads survive).
  3. Set a distinct sqlx history table for cohort_algo’s migratorSUPERSEDED. Unnecessary because cohort is the sole sqlx owner (PM = Supabase). Replaced by squash + set_ignore_missing(true).
  4. 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_migrations to 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 legacy polymarket_fetch prod 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.

  • cohort-algo-migration-squash-cleanupthe 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