Skip to main content

Architecture Cleanup Runbook — Jun 2026

Status: Complete as of Jun 19 2026. PRs #149-#171 merged. Local Finder/iCloud duplicate cleanup applied across both worktrees.

This page is a permanent record of the FinMatch repository cleanup that removed stale mirror copies, ghost runtime files, and 2,675+ duplicate snapshots from the working trees. It exists so future agents can verify why a file is missing and avoid re-introducing the drift the cleanup deliberately eliminated.

For the resulting steady-state architecture see Source of Truth Architecture.

1. What was wrong before

The repository had accumulated several classes of drift hazard:

  1. Ghost runtime mirrors on finmatch-shared. Root-level scripts/*.js, css/finmatch.css, and configs/*.json snapshots sat alongside the real finmatch-shared-owned admin/SDK files. They were never deployed (sync-to-gcs.yml correctly does not publish them to gs://finmatch-p/), but they implied edits there could affect production. They could not — and editing them was a silent regression risk.

  2. SDK ghost mirrors on every env branch. Each of finmatch-p, finmatch-s, finmatch-t, finmatch-s-test (retired Jul 2026), and the legacy short-name branches carried a copy of finmatch-finance-marketing-assets/s/scripts/finmatch-sdk.js. A "SDK canonicalization guardrail" attempted to keep them in sync; in practice they drifted, and the SDK is uploaded directly from finmatch-shared anyway.

  3. cors.json snapshots in Git. The live CORS policy on gs://finmatch-shared/cors.json is written by merchant-api. The in-Git copies were stale snapshots representing yet another drift vector.

  4. Historical migration scripts. migrate_merchants.py and scripts/migration/s_branch_to_p_branch_migration.py were one-off tools used during the Dec 2024 environment migration and had no ongoing purpose.

  5. Duplicate root planning docs on env branches. Each env branch carried 55+ root-level *.md planning documents already present on finmatch-shared.

  6. Untracked Finder/iCloud numbered duplicates (* 2.js, * 3.md, CODEOWNERS 2, etc.). 2,675 of them, accumulated from sync-tool collisions.

2. The cleanup, PR by PR

Phase 0: feature work that triggered the audit

PRTitleBranch
#149feat(admin): add Builder Toggle Matrix diagnostic viewshared
#150feat(p): declare transactionEvents universally on lender logicp
#151feat(admin): lender-settings builder + preview UX consolidationshared
#152chore(admin): resolve runtime base via sibling finmatch-p worktreeshared

PR #152 introduced resolveRuntimeBase() in admin/local-server.js so local dev can read storefront runtime from a sibling finmatch-p worktree without needing a copy of those files on finmatch-shared — unlocking the deletions that follow.

Phase 1: the keystone guardrail

PRTitle
#153chore(workflows): block ghost runtime edits on finmatch-shared

Added the ghost-runtime guardrail step in sync-to-gcs.yml. Any push to finmatch-shared that adds or modifies files under root scripts/, css/, or configs/ is now rejected at sync time.

After merge, the workflow was propagated to env branches via ./scripts/deploy-workflow.sh --yes.

Phase 2: delete the ghost runtime mirrors on finmatch-shared

PRTitle
#154chore(shared): delete ghost runtime mirrors (scripts/, css/, configs/)
#155chore(shared): prune one-off root strays
#156chore(p): prune one-off root strays

These removed the parallel runtime tree from finmatch-shared (with the guardrail from #153 ensuring it cannot return) and a number of ad-hoc root files left over from past experiments.

Phase 3: retire SDK propagation

PRTitle
#157chore(workflows): retire SDK drift guardrail and stop propagating SDK to env
#160-#167chore(<env>): delete SDK ghost mirror (canonical lives on finmatch-shared)

Removed the SDK canonicalization step from sync-to-gcs.yml and the SDK propagation logic from scripts/deploy-workflow.sh. Then deleted the finmatch-finance-marketing-assets/s/scripts/finmatch-sdk.js mirror from each env branch (PR per branch for surgical revertability).

Phase 4: retire stale planning docs and migration code

PRTitle
#158chore(docs): retire 55 duplicate root *.md docs from finmatch-p
#159chore(docs): retire 55 duplicate root *.md docs from finmatch-t
#168chore(p): retire historical s-branch-to-p-branch migration scripts

Phase 5: untrack cors.json

PRTitle
#169chore(finmatch-shared): untrack cors.json (live copy in GCS, managed by merchant-api)
#170chore(finmatch-p): untrack cors.json
#171chore(finmatch-t): untrack cors.json

cors.json is now exclusively a merchant-api-managed runtime file in gs://finmatch-shared/. Git no longer holds a snapshot of it. .gitignore continues to block re-introduction.

Phase 6: local-only Finder/iCloud duplicate cleanup

Not a PR — these files were untracked, blocked by .gitignore, and had no presence in Git history. They were removed in two passes from each worktree's working directory:

WorktreeFiles deletedDirectories deleted (containing files)
finmatch-shared639 + 64 dirs
finmatch-p165 + 5 dirs (containing 1,866 files)
Total2,675 files removed68 directories removed

The .gitignore patterns at lines 35-39 (**/* [2-9], **/* [2-9].*, etc.) prevent re-introduction.

3. Architecture diff: before vs after

BEFORE                                              AFTER (post-cleanup)
───────────────────────────────────────────────── ─────────────────────────────────────────────────
finmatch-shared/ finmatch-shared/
├── admin/ (live) ├── admin/ (live)
├── admin-docs/ (live) ├── admin-docs/ (live)
├── cloud-run/ (live) ├── cloud-run/ (live)
├── cloud-functions/ (live) ├── cloud-functions/ (live)
├── .github/workflows/ (live) ├── .github/workflows/ (live)
├── platform/ (live) ├── platform/ (live)
├── developer-docs/ (live) ├── developer-docs/ (live)
├── finmatch-finance-marketing-assets/ ├── finmatch-finance-marketing-assets/
│ └── s/scripts/finmatch-sdk.js (canonical SDK) │ └── s/scripts/finmatch-sdk.js (canonical SDK)
├── finmatch-shared/ (SSOT JSON) ├── finmatch-shared/ (SSOT JSON)
├── scripts/ (mixed: deploy- ├── scripts/
│ ├── deploy-workflow.sh workflow.sh │ ├── deploy-workflow.sh (live)
│ ├── analytics/ + analytics │ └── analytics/ (live)
│ ├── *-logic.js ❌ ghost mirror │
│ └── ... ❌ ghost mirror │
├── css/finmatch.css ❌ ghost mirror │
├── configs/ ❌ ghost mirror │
├── cors.json ❌ stale snap. │
└── migrate_merchants.py ❌ historical │

finmatch-p/ finmatch-p/
├── scripts/ (live runtime) ├── scripts/ (live runtime)
├── css/finmatch.css (live runtime) ├── css/finmatch.css (live runtime)
├── configs/ (live runtime) ├── configs/ (live runtime)
├── .github/workflows/ (drift-checked) ├── .github/workflows/ (drift-checked)
├── finmatch-finance-marketing-assets/ ├── (no SDK mirror — canonical lives on shared)
│ └── s/scripts/finmatch-sdk.js ❌ SDK ghost │
├── cors.json ❌ stale snap. │
├── migrate_merchants.py ❌ historical │
└── ~55 stale *.md planning docs ❌ duplicates │

4. New invariants and how they're defended

InvariantDefended by
Root scripts/, css/, configs/ cannot reappear on finmatch-shared.sync-to-gcs.yml ghost-runtime guardrail step (added in PR #153).
The SDK has exactly one home: finmatch-shared.No env-branch SDK file to drift from. deploy-workflow.sh no longer propagates it. PR #157 removed the now-unneeded guardrail.
Workflow files don't drift across env branches.workflow-drift-check.yml (unchanged behaviour).
Numbered Finder/iCloud duplicates can't be committed..gitignore lines 35-39.
Protected runtime files can't be mutated by Git pushes.sync-to-gcs.yml exclusion lists + pre/post snapshot hash check.
Cloud Run rate cards stay self-consistent against fixtures.snap-rate-card-test.yml (£280 anchor + four scenarios).

5. How to revert any phase

The cleanup was intentionally split into many small PRs precisely so a regression can be traced and undone. To revert:

  1. Identify which PR introduced the regression-correlated change (use git log --oneline --all --grep "cleanup\\|ghost\\|cors\\|untrack" or the table in section 2).
  2. gh pr revert <NUMBER> for the specific PR (each cleanup PR is independently revertable).
  3. The ghost-runtime guardrail (PR #153) will reject re-introduction of the deleted files unless that PR is also reverted first.

6. Open follow-ups

  • finmatch-app / finmatch-app-shell — the legacy thin-shell worktree with p/, s/, t/ subdirs and a cloud-run/ stub. Not cleaned in this round. Treat as historical unless a regression surfaces that depends on it.
  • sandbox-gcs/ on finmatch-p — a separate developer experiment area. Not part of this cleanup.
  • The skill finmatch-architecture/SKILL.md was updated in the same PR as this doc to remove now-stale references to "mirror copies" being acceptable on finmatch-shared.

7. References