You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An executable migration spec. It moves model-catalog-api, mint-ensemble-manager and graphql_engine into this repo as plain directories. It retires the submodule superproject.
The spec must contain:
the history import method for each service;
the CI workflow shape for each imported service;
the image tag scheme after consolidation, and the rewrite of update-helm-image-tags, dynamo-bump-from-branch and test-on-k8s-dev;
how dynamo and helm-charts pin images after the change;
the disposition of the four dead submodules and of the four source repositories;
where the agent instruction files land, and which references must be fixed;
the cutover order and the rollback.
The map is complete when nothing is left to decide. The spec is then handed off for execution.
Execution then followed the spec and reached Phase 10 on 2026-08-30. Phase 11, the dynamo pin, remains. It is execution, not a decision, so it sits past this map's
destination. This map does not track it.
Notes
Terms. The end state is the single-repo. Today's layout is the superproject. Do not
write "monorepo" in this map. The repo is named monorepo and the word means both things.
Drivers. Daily friction and agent navigability. Release coordination is the cost of the
change, not the reason for it.
Skills: use /grilling and /domain-modeling on each grilling ticket.
Decisions already made by the dev (do not re-open them):
The map ends at a spec. It does not execute the migration.
Three services come in: model-catalog-api, mint-ensemble-manager, graphql_engine.
helm-charts stays a separate repo. It publishes a Helm chart repository (committed index.yaml and releases/*.tgz up to MINT-9.0.0-beta.6, measured 2026-08-30), and dynamo
pins chart versions from it. global.imageTag does not exist in the chart yet; adding it is a mintproject/mint pull request, sequenced by #147.
ui is not imported. Its submodule stub stays until #81 removes Lit from TACC. This map
does not wait for that.
Four submodules are dropped, not imported: model-catalog-ontology (3 years stale), MINT_USERGUIDE (1 year stale), dynamo-experiment-may (unrelated), model-catalog-fetch-api-client (npm package whose only consumer is ui).
Workspace tooling is out of scope. Each service keeps its own package.json, lockfile and
build.
Agent instruction files must land somewhere and must not point at missing files. Rewriting
their content is out of scope.
All images publish to GHCR. Docker Hub is dropped. This settles the registry half of #142; only the meaning of the tag is still
open. ensemble-manager, mint-ui-lit and mint-ui-react move off Docker Hub. mint-ui-lit
moves even though ui is not imported.
Facts measured on 2026-08-29 at 08cdae9:
Fact
Value
Superproject commits, last 12 months
209, all by one author
Superproject commits that only move a pointer, last 6 months
50 of 191
Submodules declared in .gitmodules
9
Submodules with a commit in the last 12 months
6
.git/modules total
~120 MB, of which ui is 40 MB
Human pull requests open across all submodule repos
2
Dependabot pull requests open on mint-ui-lit
8
The release path — measured, not guessed. See #140 for the full table. Two corrections to
what this map first assumed:
Every service already writes the same three tags: <sha>, <safe-branch>, and latest on
the default branch. The convention is not three-ways-split.
graphql_engine publishes to GHCR, not Docker Hub. The split was 2 GHCR / 3 Docker Hub.
Decision 8 below now removes it.
ui-react is already in-repo and path-filtered (.github/workflows/ui-react.yml). It is the
template the imported services should follow.
Both bump skills break, they do not degrade.update-helm-image-tags/SKILL.md:41 and dynamo-bump-from-branch/SKILL.md:38 both resolve a tag with git ls-remote https://github.com/<owner>/<repo>.git refs/heads/<branch>. After consolidation
there is no per-repo remote to query.
The failure is already live.#70
recorded that ui_react's image tag is a superproject SHA which the bump automation silently
skips, so it is hand-set. This map makes that special case the normal case for every service.
Docs are already stale.CLAUDE.md lists model-catalog-fastapi/ and model-catalog-endpoint/. Neither directory exists. CLAUDE.md also opens with @.wolf/OPENWOLF.md and .claude/rules/openwolf.md requires .wolf/anatomy.md. There is no .wolf/ directory.
Production risk. A Helm upgrade does not run the Hasura migrations. The migration job is a
post-install hook. See #117.
Decisions so far
Inventory the release path — every service
already writes the same three tags; the real split is two registries, and the chart already pins ui_react to a superproject SHA.
Decide the image tag scheme for the single-repo —
tags are total: every single-repo SHA builds an image for every service, because the repo is
public and Actions minutes are free. The chart pins one global.imageTag, and the two bump skills
merge into one.
Decide the history import method for each service —
all three import the same way: git filter-repo --to-subdirectory-filter then merge --allow-unrelated-histories, taking each service's main, mainline only, with one shared
mailmap. Branches and tags do not cross, and saving them is not a cutover precondition.
Decide what happens to the source repositories at cutover —
all four source repos are archived, never deleted: the archive is the only copy of the
unmerged branches, and archiving is what stops a stray push republishing latest. The 12 open
issues transfer out first; every write happens before the archive.
Decide the CI workflow shape for each imported service —
one workflow per service, no path filter, and publish keeps needs: [test]. Filtering would
cost the publish gate, because GitHub path filters are workflow-level. Each service runs only the
checks it already has. Three cutover preconditions land on mint-ensemble-manager.
Decide where the agent instruction files land — nothing breaks and nothing moves: nested CLAUDE.md and directory-scoped skills are path-based,
not repo-based. The root file becomes the index instead, the dead OpenWolf block is deleted, and
root skills go from three to two.
Decide what happens to per-service repo-scoped tooling —
husky is mutual exclusion, not wrong scope: core.hooksPath holds one value and ui-react holds
it, so mint-ensemble-manager drops husky and lint-staged. .release-it.json configures a tool that
was never installed; it and VERSION are deleted. Releases return, cut by the single-repo only,
starting at 0.1.0 via release-please, and a release publishes a <version> image tag. Amends #142 decisions 2 and 5.
Empty. Both patches were resolved while closing #147 and moved to Out of scope. The frontier
reached the destination.
Out of scope
Workspace tooling. npm workspaces, a shared tsconfig, a task runner, hoisted node_modules, shared lint config. The repo move is mechanical; tooling unification is
open-ended. It needs its own effort and its own evidence.
Rewriting agent instruction content. This map fixes references that break on import, and
deletes pointers to files that never existed. It does not redesign what the files say. Amended
while resolving #146: the missing .wolf/
directory was listed here and is now in scope. Deleting a dangling pointer is not rewriting
content, because there is no content behind it.
A CLAUDE.md for graphql_engine. It has none and gains none. Writing one is content, and
content is out of scope. Recorded as a known gap while resolving #146.
Importing ui. Decided above; the stub is removed by #81.
Folding helm-charts into the single-repo. It is a published chart repository with an
external consumer.
The stale ensemble-manager:latest tag. Found while measuring #140, filed as #148. A live production-facing defect that
exists with or without consolidation. It is not on the route.
mint-ui-react:latest has never been published. Found while deciding #142, filed as #151. main is the default branch and is 83
commits stale, so the latest gate never opens. Same class as MINT chart ships a four-month-stale ensemble-manager by default #148, opposite cause. Live with or
without consolidation, and not on the route.
The 8 Dependabot pull requests on mint-ui-lit. Confirmed still 8, all dev or transitive
bumps. mint-ui-lit is not a source repository for the import and nothing about it changes at
cutover. Ruled out while resolving #144. Map #79 owns it.
Required status checks on main and develop. Neither branch is protected, so CI blocks no
merge today. Found while resolving #145. Adding protection is repo governance,
not a consolidation step, and it would fail merges immediately on mint-ensemble-manager's 475
lint errors. The spec states the gap; it does not close it.
The mint-ensemble-manager lint and format backlog. 475 eslint errors and 74 unformatted
files, measured on 2026-08-29. Its husky + lint-staged setup only ever formats files as they are
touched, so the backlog never shrinks. CI runs neither check after cutover. Cleaning it during a
repo migration is not on the route.
A version file for graphql_engine. It has no package.json, so the shared release version
cannot be written into it. Its version lives in the git tag and the image tag only. Adding a package.json to hold one field is new tooling. Recorded while resolving #149.
Removal of the ui submodule stub. Was fog. It sits past this map's destination: decision 4
says this map does not wait for #81, and the
stub survives the cutover untouched. Map #79
owns it. ui's image still moves to GHCR here, because that is a registry change, not an
import.
Whether helm-charts should come in later. Was fog with a revisit condition. The condition
fired — #149 lets the chart pin a <version> instead of a SHA — and the conclusion held. helm-charts is external because it has
external consumers, and the pin format does not change that. This duplicates the ruling above.
Destination
An executable migration spec. It moves
model-catalog-api,mint-ensemble-managerandgraphql_engineinto this repo as plain directories. It retires the submodule superproject.The spec must contain:
update-helm-image-tags,dynamo-bump-from-branchandtest-on-k8s-dev;dynamoandhelm-chartspin images after the change;The map is complete when nothing is left to decide. The spec is then handed off for execution.
The map is complete. It was closed on 2026-08-31. The spec is
docs/runbook-single-repo-cutover.md,with ADR-0003
for the reasoning. Nothing was left to decide.
Execution then followed the spec and reached Phase 10 on 2026-08-30. Phase 11, the
dynamopin, remains. It is execution, not a decision, so it sits past this map'sdestination. This map does not track it.
Notes
Terms. The end state is the single-repo. Today's layout is the superproject. Do not
write "monorepo" in this map. The repo is named
monorepoand the word means both things.Drivers. Daily friction and agent navigability. Release coordination is the cost of the
change, not the reason for it.
Skills: use
/grillingand/domain-modelingon each grilling ticket.Decisions already made by the dev (do not re-open them):
model-catalog-api,mint-ensemble-manager,graphql_engine.helm-chartsstays a separate repo. It publishes a Helm chart repository (committedindex.yamlandreleases/*.tgzup toMINT-9.0.0-beta.6, measured 2026-08-30), anddynamopins chart versions from it.
global.imageTagdoes not exist in the chart yet; adding it is amintproject/mintpull request, sequenced by#147.
uiis not imported. Its submodule stub stays until#81 removes Lit from TACC. This map
does not wait for that.
model-catalog-ontology(3 years stale),MINT_USERGUIDE(1 year stale),dynamo-experiment-may(unrelated),model-catalog-fetch-api-client(npm package whose only consumer isui).package.json, lockfile andbuild.
their content is out of scope.
#142; only the meaning of the tag is still
open.
ensemble-manager,mint-ui-litandmint-ui-reactmove off Docker Hub.mint-ui-litmoves even though
uiis not imported.Facts measured on 2026-08-29 at
08cdae9:.gitmodules.git/modulestotaluiis 40 MBmint-ui-litThe release path — measured, not guessed. See
#140 for the full table. Two corrections to
what this map first assumed:
<sha>,<safe-branch>, andlatestonthe default branch. The convention is not three-ways-split.
graphql_enginepublishes to GHCR, not Docker Hub. The split was 2 GHCR / 3 Docker Hub.Decision 8 below now removes it.
ui-reactis already in-repo and path-filtered (.github/workflows/ui-react.yml). It is thetemplate the imported services should follow.
Both bump skills break, they do not degrade.
update-helm-image-tags/SKILL.md:41anddynamo-bump-from-branch/SKILL.md:38both resolve a tag withgit ls-remote https://github.com/<owner>/<repo>.git refs/heads/<branch>. After consolidationthere is no per-repo remote to query.
The failure is already live. #70
recorded that
ui_react's image tag is a superproject SHA which the bump automation silentlyskips, so it is hand-set. This map makes that special case the normal case for every service.
Docs are already stale.
CLAUDE.mdlistsmodel-catalog-fastapi/andmodel-catalog-endpoint/. Neither directory exists.CLAUDE.mdalso opens with@.wolf/OPENWOLF.mdand.claude/rules/openwolf.mdrequires.wolf/anatomy.md. There is no.wolf/directory.Production risk. A Helm upgrade does not run the Hasura migrations. The migration job is a
post-install hook. See #117.
Decisions so far
Inventory the release path — every service
already writes the same three tags; the real split is two registries, and the chart already pins
ui_reactto a superproject SHA.Inventory every reference that breaks — six
break on import, three of them silently; eight more are already broken today.
Decide the image tag scheme for the single-repo —
tags are total: every single-repo SHA builds an image for every service, because the repo is
public and Actions minutes are free. The chart pins one
global.imageTag, and the two bump skillsmerge into one.
Decide the history import method for each service —
all three import the same way:
git filter-repo --to-subdirectory-filterthen merge--allow-unrelated-histories, taking each service'smain, mainline only, with one sharedmailmap. Branches and tags do not cross, and saving them is not a cutover precondition.
Decide what happens to the source repositories at cutover —
all four source repos are archived, never deleted: the archive is the only copy of the
unmerged branches, and archiving is what stops a stray push republishing
latest. The 12 openissues transfer out first; every write happens before the archive.
Decide the CI workflow shape for each imported service —
one workflow per service, no path filter, and
publishkeepsneeds: [test]. Filtering wouldcost the publish gate, because GitHub path filters are workflow-level. Each service runs only the
checks it already has. Three cutover preconditions land on
mint-ensemble-manager.Decide where the agent instruction files land —
nothing breaks and nothing moves: nested
CLAUDE.mdand directory-scoped skills are path-based,not repo-based. The root file becomes the index instead, the dead OpenWolf block is deleted, and
root skills go from three to two.
Decide what happens to per-service repo-scoped tooling —
husky is mutual exclusion, not wrong scope:
core.hooksPathholds one value andui-reactholdsit, so
mint-ensemble-managerdrops husky and lint-staged..release-it.jsonconfigures a tool thatwas never installed; it and
VERSIONare deleted. Releases return, cut by the single-repo only,starting at
0.1.0viarelease-please, and a release publishes a<version>image tag. Amends#142 decisions 2 and 5.
Design the cutover order and the rollback —
twelve phases, three import pull requests,
developmerged intomainfirst. The point of noreturn is the first
dynamopin, not the archive. The spec is nowdocs/runbook-single-repo-cutover.md.Not yet specified
Empty. Both patches were resolved while closing
#147 and moved to Out of scope. The frontier
reached the destination.
Out of scope
Workspace tooling. npm workspaces, a shared
tsconfig, a task runner, hoistednode_modules, shared lint config. The repo move is mechanical; tooling unification isopen-ended. It needs its own effort and its own evidence.
Rewriting agent instruction content. This map fixes references that break on import, and
deletes pointers to files that never existed. It does not redesign what the files say. Amended
while resolving #146: the missing
.wolf/directory was listed here and is now in scope. Deleting a dangling pointer is not rewriting
content, because there is no content behind it.
A
CLAUDE.mdforgraphql_engine. It has none and gains none. Writing one is content, andcontent is out of scope. Recorded as a known gap while resolving
#146.
Importing
ui. Decided above; the stub is removed by#81.
Folding
helm-chartsinto the single-repo. It is a published chart repository with anexternal consumer.
The stale
ensemble-manager:latesttag. Found while measuring#140, filed as
#148. A live production-facing defect that
exists with or without consolidation. It is not on the route.
mint-ui-react:latesthas never been published. Found while deciding#142, filed as
#151.
mainis the default branch and is 83commits stale, so the
latestgate never opens. Same class as MINT chart ships a four-month-stale ensemble-manager by default #148, opposite cause. Live with orwithout consolidation, and not on the route.
The 8 Dependabot pull requests on
mint-ui-lit. Confirmed still 8, all dev or transitivebumps.
mint-ui-litis not a source repository for the import and nothing about it changes atcutover. Ruled out while resolving
#144. Map
#79 owns it.
Required status checks on
mainanddevelop. Neither branch is protected, so CI blocks nomerge today. Found while resolving
#145. Adding protection is repo governance,
not a consolidation step, and it would fail merges immediately on
mint-ensemble-manager's 475lint errors. The spec states the gap; it does not close it.
The
mint-ensemble-managerlint and format backlog. 475 eslint errors and 74 unformattedfiles, measured on 2026-08-29. Its husky + lint-staged setup only ever formats files as they are
touched, so the backlog never shrinks. CI runs neither check after cutover. Cleaning it during a
repo migration is not on the route.
A version file for
graphql_engine. It has nopackage.json, so the shared release versioncannot be written into it. Its version lives in the git tag and the image tag only. Adding a
package.jsonto hold one field is new tooling. Recorded while resolving#149.
Removal of the
uisubmodule stub. Was fog. It sits past this map's destination: decision 4says this map does not wait for #81, and the
stub survives the cutover untouched. Map #79
owns it.
ui's image still moves to GHCR here, because that is a registry change, not animport.
Whether
helm-chartsshould come in later. Was fog with a revisit condition. The conditionfired — #149 lets the chart pin a
<version>instead of a SHA — and the conclusion held.helm-chartsis external because it hasexternal consumers, and the pin format does not change that. This duplicates the ruling above.