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:
-
Ghost runtime mirrors on
finmatch-shared. Root-levelscripts/*.js,css/finmatch.css, andconfigs/*.jsonsnapshots sat alongside the realfinmatch-shared-owned admin/SDK files. They were never deployed (sync-to-gcs.ymlcorrectly does not publish them togs://finmatch-p/), but they implied edits there could affect production. They could not — and editing them was a silent regression risk. -
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 offinmatch-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 fromfinmatch-sharedanyway. -
cors.jsonsnapshots in Git. The live CORS policy ongs://finmatch-shared/cors.jsonis written bymerchant-api. The in-Git copies were stale snapshots representing yet another drift vector. -
Historical migration scripts.
migrate_merchants.pyandscripts/migration/s_branch_to_p_branch_migration.pywere one-off tools used during the Dec 2024 environment migration and had no ongoing purpose. -
Duplicate root planning docs on env branches. Each env branch carried 55+ root-level
*.mdplanning documents already present onfinmatch-shared. -
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
| PR | Title | Branch |
|---|---|---|
| #149 | feat(admin): add Builder Toggle Matrix diagnostic view | shared |
| #150 | feat(p): declare transactionEvents universally on lender logic | p |
| #151 | feat(admin): lender-settings builder + preview UX consolidation | shared |
| #152 | chore(admin): resolve runtime base via sibling finmatch-p worktree | shared |
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
| PR | Title |
|---|---|
| #153 | chore(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
| PR | Title |
|---|---|
| #154 | chore(shared): delete ghost runtime mirrors (scripts/, css/, configs/) |
| #155 | chore(shared): prune one-off root strays |
| #156 | chore(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
| PR | Title |
|---|---|
| #157 | chore(workflows): retire SDK drift guardrail and stop propagating SDK to env |
| #160-#167 | chore(<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
| PR | Title |
|---|---|
| #158 | chore(docs): retire 55 duplicate root *.md docs from finmatch-p |
| #159 | chore(docs): retire 55 duplicate root *.md docs from finmatch-t |
| #168 | chore(p): retire historical s-branch-to-p-branch migration scripts |
Phase 5: untrack cors.json
| PR | Title |
|---|---|
| #169 | chore(finmatch-shared): untrack cors.json (live copy in GCS, managed by merchant-api) |
| #170 | chore(finmatch-p): untrack cors.json |
| #171 | chore(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:
| Worktree | Files deleted | Directories deleted (containing files) |
|---|---|---|
finmatch-shared | 639 + 64 dirs | — |
finmatch-p | 165 + 5 dirs (containing 1,866 files) | — |
| Total | 2,675 files removed | 68 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
| Invariant | Defended 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:
- Identify which PR introduced the regression-correlated change
(use
git log --oneline --all --grep "cleanup\\|ghost\\|cors\\|untrack"or the table in section 2). gh pr revert <NUMBER>for the specific PR (each cleanup PR is independently revertable).- 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 withp/,s/,t/subdirs and acloud-run/stub. Not cleaned in this round. Treat as historical unless a regression surfaces that depends on it.sandbox-gcs/onfinmatch-p— a separate developer experiment area. Not part of this cleanup.- The skill
finmatch-architecture/SKILL.mdwas updated in the same PR as this doc to remove now-stale references to "mirror copies" being acceptable onfinmatch-shared.