← all branches

feat/sites-1n

needs attentionviewing older commit
83fd5a0 · incrementalpre-PRreviewed 2026-07-08 01:37 UTC3H · 8M · 11L · 13I
The branch
Purpose
Stop incorrectly fused RPU sites from the legacy name-based dedup bug (feat/sites-1n) and un-merge existing data — 379 sites holding 1,583 unrelated RPUs.
Goal
Make the Site ↔ RPU relationship correct (1:N deliberate, not legacy-automatic) so users can manage it via the new UX.
Sub-goals
  • SG-1: Shared site-resolution decision + rewire both coordinators
  • SG-2: Site match/search endpoint
  • SG-3: Wizard accepts existingSitePublicId
  • SG-4: Drawer with Crear/Usar-existente picker
  • SG-5: Site cell pencil → SiteEditModal
  • SG-6: Agrupar por sitio toggle
  • SG-7: Batch preview grouping
  • SG-8: Analytics events + i18n
The changes (whole branch)
What
Added scripts/split-merged-sites.ts (one-shot remediation for 379 multi-RPU sites); expanded design doc §6 with full prod runbook; updated scope.md to clarify script is authored here but executed post-merge.
Why
The code fix (SG-1) stops new merges; this script un-merges existing data before users start creating deliberate groups with the new UX — must run once immediately post-merge.
Areas
scripts+2710docs+3240apps/platform+1211173domains/cross-domain+447131domains/core+331domains/utility+615packages/analytics+450packages/api+182.github+310
Blast
26 files, +2532/-418 cumulative; this increment: scripts/ + docs/. Prod impact: 379 sites / 1,583 RPUs → ~1,204 new 1:1 sites. Category D orgs (26 sites) need heads-up.
one-shot-script prod-data-migration post-merge-execution
CI· No open PR — CI not triggered for pre-PR branchCodeRabbit· No .coderabbit.yaml in repotypecheck· Not run on this diff (pre-PR)

Findings · 15

correctness8

high

Source sites row not locked during INSERT SELECT — concurrent update race window

scripts/split-merged-sites.ts:163

SUC rows are locked with FOR UPDATE OF suc, but the sites sub-SELECT is unlocked. A concurrent lifecycle/metadata update could commit between lock acquisition and INSERT, causing the new site to be created with stale data.

high

Source site version not bumped after losing N-1 SUC links

scripts/split-merged-sites.ts:200

SUC version is bumped but sites.version on the source is not. Any shell using OCC on sites will not detect this structural change.

high

site_locations UNION ALL / LIMIT 1 may silently drop rows for multi-location sources

scripts/split-merged-sites.ts:183

LIMIT 1 applies to the entire UNION result. If source site has multiple site_locations rows, LIMIT 1 picks only one.

medium

Dry-run plan 'keeps rpus[0]' uses contract_number sort — execution uses created_at

scripts/split-merged-sites.ts:234

Plan log can say 'keeps RPU X' while execution keeps RPU Y, undermining pre-run audit.

medium

metric_streams rows not re-pointed to split-off sites

scripts/split-merged-sites.ts:140

Script copies assignees and locations but is silent on metric_streams (may FK to site_id).

medium

Post-verification bounded by --limit — OK message misleading for partial runs

scripts/split-merged-sites.ts:258

findSuspiciousSites reused for verification includes the LIMIT. Drop LIMIT for the verification call.

medium

Failed site IDs not accumulated and re-printed in summary

scripts/split-merged-sites.ts:251

catch block logs and continues. Post-verify shows remaining multi-RPU sites but doesn't list which sites specifically failed.

low

argValue does not guard against flag-as-value (--org --execute)

scripts/split-merged-sites.ts:79

argValue('--org') returns '--execute' if the user writes --org --execute.

security3

medium

CLI filter values logged to stdout — multi-tenant audit concern

scripts/split-merged-sites.ts:214

ORG_FILTER/SITE_FILTER echoed in console.log. If logs ship to a shared sink, this correlates org public IDs with split activity.

medium

site_assignees copy lacks explicit org_id guard

scripts/split-merged-sites.ts:194

No explicit WHERE m.org_id = s.org_id guard. Defense-in-depth against hypothetical data corruption.

low

--limit NaN coercion silently drops LIMIT clause

scripts/split-merged-sites.ts:84

Number('foo') is NaN; NaN is falsy so LIMIT drops silently. With --execute this processes far more sites than intended.

conventions1

medium

Duplicate step 5 in prod runbook — verify step hidden

docs/development/site-contract-relationship.md:229

Runbook has two items labeled '5.' The verify step should be step 6.

tests3

medium

splitSite() has no integration test for transaction invariants

scripts/split-merged-sites.ts:140

For a script that will mutate ~1,583 prod rows, at least one integration test against a fixture would verify the transaction before --execute on prod.

low

categoryOf() has no unit test

scripts/split-merged-sites.ts:101

Pure 4-case function. A 4-assertion unit test eliminates any transcription error.

low

Dry-run does not exercise the INSERT/UPDATE path

scripts/split-merged-sites.ts:238

Dry-run exits before splitSite(). A staging pilot (--limit 1 --execute on a Supabase branch) would give stronger confidence.

History · 8 commits

  1. 7b5ff24needs attentionincremental0H · 2M · 3L2026-07-09 00:58
  2. 6231fe5needs attentionincremental0H · 2M · 4L2026-07-08 22:34
  3. 8fa38b5needs attentionincremental2H · 4M · 5L2026-07-08 01:45
  4. 83fd5a0needs attentionincremental3H · 8M · 11L2026-07-08 01:37current
  5. 23c04fcneeds attentionincremental3H · 3M · 2L2026-07-07 20:30
  6. e70f9f5blockedincremental6H · 11M · 6L2026-07-07 19:28
  7. 499b1a5blockedincremental6H · 9M · 5L2026-07-07 18:26
  8. ff23301needs attentionincremental2H · 6M · 5L2026-07-07 01:05