There are three layers and exactly one is the source of truth.
| Layer | Path | Role |
|---|---|---|
Flat clone root |
|
Single source of truth. All work and branches happen here, in ordinary independent clones. Per-repo PRs/issues unchanged. |
Aggregator |
|
Coordination layer over the clones: the |
Published artifact |
|
Generated output. Submodule pointers are an auto-bumped index that keeps GitHub Pages, the GitLab/Bitbucket mirror, and the governance workflows working. |
Never cd into repos-monorepo to edit code. Never hand-resolve a submodule
there. It is regenerated, not maintained.
-
repos.toml— generated fromrepos-monorepo/.gitmodulesbyscripts/gen-repos-manifest.sh. Regenerate withjust repos-manifest. NOTE:0-AI-MANIFEST.a2mlprefers generated files out of source control;repos.tomlis a deliberate exception — it is the coordination index and must be present on a fresh clone / in CI without a regeneration step. It carries an SPDX header and is fully reproducible from the documented command. -
repos.groups.toml— hand-maintained. Names the repo sets a cross-repo thread spans (epics). Survives manifest regeneration.
A thread is one logical change across N repos. Each repo still merges through its own normal PR — the runner only orchestrates the fan-out.
just thread resolve --kind julia # who is in scope
just thread start standards-130 --repos echo-types,affinescript # branch per clone
just thread pr standards-130 --refs standards#130 # one PR per repo
just thread status standards-130 --repos echo-types,affinescript # CI/merge state
just thread land standards-130 --repos echo-types,affinescript --yesSelectors: --group <name> (from repos.groups.toml), --kind <kind> (from
repos.toml), or --repos a,b,c. start skips dirty worktrees; land
refuses without --yes and only merges PRs GitHub reports MERGEABLE.
just sync-aggregator # DRY-RUN: show pointers that would bump
just sync-aggregator --commit # record the bump
just sync-aggregator --push # record + trigger Pages deploy + mirrorSubmodules are updated one at a time (a single git submodule update
--recursive aborts on the URL-less .git-private-farm entry — see
scripts/gitlink-drift.jl). Run after thread land, or on a schedule, so the
artifact never drifts.
sync-aggregator leaves un-fetchable submodules as warn … (left as-is)
rather than aborting. scripts/fix-stale-submodule-urls.sh classifies them
into four classes and remediates only on explicit, separately-gated flags:
| Class | Cause | Remediation |
|---|---|---|
|
Repo renamed; |
|
|
|
|
|
|
|
|
Repo exists and URL is canonical — a fetch blip. |
Re-run |
sh scripts/fix-stale-submodule-urls.sh # classify (dry-run)
sh scripts/fix-stale-submodule-urls.sh --apply-renames # safe: URL rewrites
sh scripts/fix-stale-submodule-urls.sh --prune-nonexistent # destructive: pruneIt refuses to run while sync-aggregator holds the aggregator git lock, and
never commits/pushes — re-run just sync-aggregator --push afterwards.
-
[x] Rule declared (this document).
-
[ ] One-shot pointer reconcile:
just sync-aggregator --push(fixes every stale/empty gitlink — incl.echo-types— in one commit). Run deliberately; it fetches ~300 repos. -
[x]
repos.tomlgenerated from.gitmodules(297 repos). -
[x]
repos.groups.tomlstub +thread/sync-aggregator/aggregator-driftrecipes. -
[ ] Schedule
sync-aggregator --push(cron/CI) so the artifact self-heals. -
[ ] Optional: have
reposystem scanassert manifest⇔clone parity.