jonathanpopham

July 26, 2026 · jonathan popham

A monorepo is not a folder: Preserving the full history of Pain to Profit

I consolidated three connected projects without throwing away their commits, dropped work, tests, architecture, or evidence.

I had three projects that were one system in practice:

They lived in separate Git repositories. The code already knew they were connected. Paths reached into sibling directories. Integration tests exercised more than one checkout. Skills in one repository told agents how to invoke tools in another.

Copying those folders into a new directory was easy. It was also not enough.

The project history was part of the system.

What I wanted to preserve

A normal fresh copy gave me the latest files, but it flattened the story into one commit. That would have discarded the sequence of decisions that produced the system.

The source repositories contained:

Component Original reachable commits
Mathematical knowledge graph 109
Business boilerplate generator 18
Venture factory 43
Total 170

The ordinary branches were not the whole story. Git object recovery found another 33 commits that were no longer reachable from normal branch heads. Some came from dropped stashes. Some were intermediate implementations. One linked worktree contained an alternate spec_gen.py implementation and tests that had never reached its main branch.

There were also independent local changes in the old checkouts and the first monorepo checkout. One example was a case-study run ledger where both copies had valid new entries.

“Just use the newest folder” would have silently thrown some of this away.

Inventory before surgery

I started by inventorying every repository, branch, tag, stash, worktree, dirty tracked file, untracked file, reflog, unreachable commit, remote, and overlap with the proposed monorepo.

Before changing history, I created verified Git bundles, binary patches, untracked-file snapshots, and checksums outside the repositories.

Then I created archival refs for the commits that a normal clone would not carry. This made the recoverable objects explicit before any rewrite.

The source master branches were left untouched.

Importing history under permanent paths

Each component needed to live under a stable directory:

pain-to-profit/
  math-knowledge-graph/
  business-boilerplate-generator/
  venture-factory/

I made archival clones and used git filter-repo --to-subdirectory-filter to prefix each component’s entire history with its permanent monorepo path.

The important part was not merely copying the final tree. Each rewritten component tip became a merge parent of the new main branch. As a result, this works:

git log --follow -- math-knowledge-graph/scripts/query_graph.py

It walks back through the original project history even though the file now lives inside the monorepo.

Recovered commits, old master tips, working-state snapshots, and the alternate worktree implementation were pushed under namespaced archival branches:

history/math-knowledge-graph/*
history/business-boilerplate-generator/*
history/venture-factory/*

The resulting remote contains 41 history branches and 215 distinct commits across all refs. The three old GitHub repositories are now read-only archives pointing at the canonical project.

One architecture, independently testable organs

Consolidation did not mean turning the project into a blob.

The mathematical judge, business planner, and venture orchestrator remain independently testable modules. Their dependency direction and evidence boundaries are now documented in one root architecture file.

The pipeline is:

pain text
  -> graph retrieval candidates
  -> independent structural reasoning
  -> case study and falsifiable verdict
  -> business hypothesis and kill criteria
  -> dossier and grounded claims
  -> human gate
  -> ethical demand test
  -> observed signal against a pre-registered threshold

The repository also contains its operational skills. Those skills live beside the code, fixtures, case studies, and references they govern. Agent clients can link to them, but the repository copies are the source of truth.

One gate before anything leaves the machine

The monorepo now has one command:

bash bin/verify-all.sh

It runs:

That is 669 tests plus the evaluation corpus, secret scans, real pipeline runs, and golden checks.

The pre-push hook is fail-closed. It runs the complete gate before Git sends any branch to the remote. GitHub Actions invokes the same script rather than maintaining a second CI definition.

The first remote Actions job did not start because of an account billing or spending-limit block. I recorded that as both a local bead and a GitHub issue. The local pre-push gate passed twice, and a fresh clone from the remote passed the complete gate again. The distinction matters: the code is green, while remote execution is account-blocked.

The regression consolidation exposed

The first full gate found an environment-dependent golden test.

One Python installation had Pillow and generated an optional site/og.png. Another did not have Pillow and generated only the deterministic site/og.svg. The golden tree covered the SVG, so the extra derived PNG made one environment fail.

The fix was not to commit one machine’s PNG. The canonicalizer now excludes only the optional PNG while retaining byte-for-byte coverage of the SVG source. A regression test runs the contract in both environments and proves meaningful SVG content remains covered.

That is why consolidation needs a real gate. Moving files can reveal assumptions that separate checkouts had hidden.

The result

The canonical repository is now private and remotely tracked. main matches the remote. All 41 archival branches match their remote hashes. A fresh clone sees 43 remote refs, 215 commits, and passes the complete verification suite.

No source master branch was rewritten. No dirty work was discarded. No dropped commit had to be trusted to Git’s garbage collector. The old repositories remain readable archives.

A monorepo is not a directory containing several projects. It is one history, one architecture, one evidence policy, and one integration gate.

← back to all posts