Skip to content

The split

Until 2026-10-02 marola was one repo, marola-dev/marola, holding the Scala app, the map, the corpus, the Python tooling and the team's docs. MIP-0070 split it into single-purpose repos under an umbrella. This page is the record of why, what was decided and what it cost; how the repos fit together today is Repos.

Why

One tree forced one build and one set of gates on everything. site.yml ran sbt to build the boards, so a CSS change to the map waited on a Scala compile. docker-local.yml fired on the corpus, the fine-tune's Modelfile and the app's resources at once, and ci.yml path-filtered six directories to avoid running everything. Parallel sessions each needed their own worktree (30+ at the time) to stay out of each other's way, and the planned OODS data tree would have added daily bot commits to the code's history.

The monorepo's one real advantage was that an agent saw everything at once. The umbrella keeps it: checked out with submodules, the whole workspace is one directory again.

The decisions and what they beat

Decision What it beat, and why
An umbrella with every repo as a submodule, keeping marola-dev/marola as the umbrella Keeping marola-dev/marola as the Scala app with a new umbrella beside it: the issues, the MIP history and every #N reference would have had to move or keep pointing at a code repo
OODS split into code and data: the ingest code stays in marola-app, data/oods/ goes to marola-oods Moving the code with the data: the OODS module depends on the app's local module, and the Scala modules are not published (next row). Daily data commits stay out of the code's history
Contracts instead of paths: a producer publishes a versioned artifact, a consumer pins it Reading a sibling's tree inside the umbrella checkout: no repo could then build or test on its own, and a break would surface far from its cause
No published Scala libraries: core, local and cli stay one sbt build Full modularity with semver: every cross-module change would take two PRs and a release
Aggregated docs: one site at docs.marola.dev, built by the umbrella from every repo Per-repo sites, or per-repo mkdocs.yml (MIP-0070 §5.5): seven theme configs to keep in step. MIP-0074 then made the umbrella README the landing and left each repo its low-level docs
The devkit as a flake input, a plugin marketplace and reusable workflows, pinned to a tag Requiring the umbrella for shared tooling (a single-repo clone could not run just pr); vendoring the files with a sync bot (the copy-and-drift an earlier split out of a shared repo had already hit); the devkit as a submodule (a second pin besides flake.lock)
Issues per repo, coordinated on the org Project Every issue in the umbrella: Closes would always cross repos, and a repo's own backlog would be invisible from it
One fine-grained PAT for dispatches, site-data pushes and pointer sync A GitHub App: scoped per repo and with no personal owner, but more setup. Revisit if the user-tied token becomes a problem

What moved where

Repo Took
umbrella AGENTS.md, CLAUDE.md, the READMEs, PHILOSOPHY.md and the health files, rewritten for the workspace; docs/3-*, docs/4-*, docs/MIPs/; mkdocs/; the docs aggregator and pointer sync; repo_stats, arxiv_digest, awesome_agentic_digest, mip_graph, gh-billing
marola-devkit The dev-flow scripts (stack, uprd, pr, cost-split, issues, the label tools) and scripts/lib/; the git hooks and Claude Code hooks; the generic skills and agents; the PR template, labels and issue forms; the runner scripts; the base flake and just module
marola-app The sbt build and its sources, the OODS module and its ingest workflow, the Dockerfile and compose file, the Scala CI, docker.yml, docker-smoke.yml, marola-e2e.yml, Scala Steward, the Scaladoc job, docs/1-* and docs/2-* (since split again by MIP-0074)
marola-site The static map, site.yml, site-health.yml, the site-data branch, the site checks, the site-frontend skill
marola-corpus The knowledge documents, the corpus-doc and eli5 skills
marola-ml The DSPy compile, the fine-tune, Dockerfile.local, docker-local.yml, marola-sea-publish.yml, the pdoc job, the benchmark gate and docs/benchmarks/
marola-oods data/oods/

Each extraction kept its history (git filter-repo --path … --replace-message, a bare #N rewritten to marola-dev/marola#N), and the open issues for each area moved with gh issue transfer.

Timeline

Date PR Step
2026-09-30 #521 MIP-0070
2026-09-30 #574, #576, #577 Prep inside the one tree: --site writes board data only; the app reads knowledge from one directory; ml reads the resources tarball
2026-09-30 #578, #579 The tooling resolves MIPs from the umbrella; the invariants block
2026-10-01 #580 Issues per repo
2026-10-01 marola-devkit v0.1.0, #585 The devkit extracted, then consumed as a flake, plugin and workflows
2026-10-01 #586, #589 The docs aggregator; marola-site extracted, taking Pages and marola.dev, while docs move to docs.marola.dev
2026-10-01 #591 marola-corpus
2026-10-02 #593 marola-ml
2026-10-02 #597, #598 marola-app; marola-oods joins, empty
2026-10-02 #599 The umbrella finished: only the team layer left, pointer sync on

The docs followed in MIP-0074 (#603), which made the umbrella the site's landing and moved each repo's internals into its own pages.

What was given up

  • One PR for a cross-cutting change. A board-schema change is now two PRs and an image bump. The Scala modules stayed together so a core change does not pay this.
  • Seeing a break where it is made. A producer's change fails in its consumers, later, when they bump the pin; a renamed page fails only the umbrella's next docs build.
  • Old links into moved paths. GitHub links such as marola-dev/marola/blob/main/core/… broke; redirects cover the docs site only.
  • One issue list. Issues are spread over seven repos; the org Project is the one view, and an issue not on it is invisible to the queue.
  • Submodule ergonomics: detached heads, pointer noise, a forgotten --recurse-submodules.
  • An owner-free credential. The cross-repo PAT is tied to the person who created it.