Skip to content

two-folder-integration — A repository that adopts Majordomus gains two directories and two lines, and can give them back

Adoption is a reconciliation, not a template copy. One typed repository model is discovered once and every consumer reads it. A desired integration is computed from that model, diffed against what the repository actually holds, and applied as the smallest mutation that closes the gap — with everything Majordomus owns living under `.ai/` and `.majordomus/`, and anything outside those two namespaces reduced to a small managed bridge whose ownership is recorded. Running the reconciliation twice changes nothing the second time, and uninstalling gives back exactly what was taken and nothing a person authored.

PLANNED 0 of 23 issues done

ready
1
blocked
22
active
0
verify
0
done
0
cancelled
0

Problem

What Majordomus leaves in a repository it adopts is not decided anywhere; it is the sum of what each subsystem happened to write. Provider files at the root, scripts, configuration beside configuration, generated registries that became inputs, and no record of which of them Majordomus put there. The costs compound in three directions at once. A person cannot tell what is theirs and what is the tool's, so nothing can be safely removed. A second provider means a second copy of the same instructions rather than a second adapter. And every subsystem rediscovers the repository for itself — the CLI, the environment entry, the documentation, the server — so the same facts are read four ways and disagree on the fourth.

Outcome

Adoption is a reconciliation, not a template copy. One typed repository model is discovered once and every consumer reads it. A desired integration is computed from that model, diffed against what the repository actually holds, and applied as the smallest mutation that closes the gap — with everything Majordomus owns living under `.ai/` and `.majordomus/`, and anything outside those two namespaces reduced to a small managed bridge whose ownership is recorded. Running the reconciliation twice changes nothing the second time, and uninstalling gives back exactly what was taken and nothing a person authored.

Current state

`.ai/` and `.majordomus/` both exist and carry real material, and directory contracts are already enforced under `.ai/` (ADR 0011). Against that: provider bootstraps are generated to the root, the environment entry and the CLI each discover the repository their own way, `init` writes what it was written to write rather than what the repository lacks, no ledger records what was placed outside the two namespaces, and nothing can undo an adoption.

Desired state

`majordomus init` on a foreign repository adds `.ai/`, `.majordomus/`, a managed block in `AGENTS.md` and — when direnv is wanted — one stable line in `.envrc`. A second run reports no change. `majordomus uninstall` removes what the ledger records and leaves authored knowledge alone. Adding a valid artifact under a discoverable namespace registers it everywhere, and no list anywhere enumerates what discovery already finds.

Scope

  • apps/majordomus-cli/src
  • .ai/repo/rules/project
  • .ai/repo/adrs
  • .majordomus
  • share/schemas
  • share/skeleton
  • docs
  • test/cases
  • .github/workflows

Out of scope

  • A configuration format that restates what the repository can be inspected for
  • Rewriting a working subsystem for symmetry rather than for a defect
  • Deleting user-authored content on any path, under any flag, without an explicit and separate instruction
  • A second worktree convention, a second handover format or a second doctor
  • Provider-specific semantics anywhere but an adapter

Acceptance criteria

  • Everything Majordomus persists in an adopted repository lives under `.ai/` or `.majordomus/`, and every exception outside them is a bridge recorded in the ownership ledger
  • One typed repository model is discovered once per process and every consumer — CLI, environment, API, MCP, Cockpit, documentation, providers, completion — reads that model rather than the filesystem
  • The desired integration is a value, the plan is a diff against what is there, and the apply is the smallest mutation that closes it
  • A second `init` on a conforming repository produces no change, proved by a test rather than by observation
  • `uninstall` reverses every recorded mutation, refuses to touch a bridge a person has edited, and leaves `.ai/` authored content in place by default
  • Every generated artifact declares its provenance, its generator and the inputs it derives from, and no generated artifact is read back as canonical input
  • No hand-maintained list enumerates artifacts that discovery finds, and adding a valid artifact registers it in every consumer with no second edit
  • Environment entry stays local and bounded, with no network and no build, and its cost is measured against a declared budget
  • Every invariant above is a project rule with a check that CI executes, each proved by a negative test

Validation

  • cargo test --workspace --all-features
  • bash test/run.sh
  • bin/majordomus doctor
  • majordomus generate --check
  • majordomus init --dry-run

Evidence required

  • footprint_bounded
  • one_model
  • desired_state_diff
  • init_idempotent
  • uninstall_reverses
  • provenance_declared
  • no_manual_registry
  • entry_budget
  • rules_enforced

Risks

  • A reconciler that computes a desired state can also impose one. Everything it would change outside the two namespaces has to be visible in a dry run before it is applied, and a bridge a person edited is a conflict rather than an overwrite.
  • One repository model is a bottleneck as well as a spine: every consumer that reads it pays its discovery cost. The cost has to be measured against the environment-entry budget before the model becomes mandatory, not after.
  • Migrating this repository's own footprint is the most dangerous phase, because the tool is being changed by the thing it is changing. The migration runs through the reconciler with a dry run first, never through a move script.
  • An ownership ledger that drifts from the filesystem is worse than none: it authorises deletions it cannot justify. Every recorded mutation carries the hash it was written with, and a mismatch stops the removal.
  • Provider adapters invite provider semantics to leak into the model. The test is whether a new provider is an implementation of one trait, or an edit in six places.

Issues

Each one is an execution contract with its own acceptance criteria, validation command and required evidence. The wave column is the layer of the dependency graph it sits in.

issuestatuswavedepends ontitle
I1101 READY 0 What this tool leaves behind, established by inspection rather than by memory
I1102 BLOCKED 1 I1101 The two namespaces are a typed contract, not a habit
I1103 BLOCKED 2 I1102 Whether a file is authored or derived is a field, not a naming convention
I1104 BLOCKED 2 I1102 Both namespaces carry their contracts, all the way down
I1105 BLOCKED 1 I1101 The repository is discovered once, into one model
I1106 BLOCKED 2 I1105 The registries are views of the model, not separate discoveries
I1107 BLOCKED 3 I1106 Discovery is deterministic, cached, and its cost is known
I1108 BLOCKED 3 I1106 The desired integration is a value, and the plan is a diff
I1109 BLOCKED 4 I1108 Everything placed outside the two namespaces is recorded, with what it was
I1110 BLOCKED 5 I1109 The second run changes nothing, and a test says so
I1111 BLOCKED 5 I1109 Uninstall gives back exactly what was taken
I1112 BLOCKED 5 I1109 What touches the root is two small blocks, and they never change again
I1113 BLOCKED 6 I1112 Entering a directory is local, bounded and silent about the network
I1114 BLOCKED 6 I1112 A provider is an adapter over the model, and adding one is an implementation
I1115 BLOCKED 7 I1114 Completion comes from the command model, not from a second list
I1116 BLOCKED 6 I1110, I1112 This repository's own footprint moves, through the reconciler
I1117 BLOCKED 3 I1106 The hand-maintained lists are removed, not left running beside the derivation
I1118 BLOCKED 7 I1110, I1116 Every invariant of this milestone is a rule with a check that runs
I1119 BLOCKED 7 I1113 The budgets are measured, and a regression fails rather than being noticed
I1120 BLOCKED 4 I1108 A refusal says what to do about it
I1121 BLOCKED 6 I1112 Every surface shows the integration from the one model
I1122 BLOCKED 7 I1110, I1111, I1116, I1121 A foreign repository, adopted, upgraded and given back
I1123 BLOCKED 8 I1104, I1107, I1115, I1117, I1118, I1119, I1120, I1122 The milestone is accepted on its evidence

The graph

two-folder-integration dependency graphfrom .ai/repo/project
flowchart LR
    I1101["I1101<br/>What this tool leaves behind, established by inspection rather than by memory"]:::ready
    I1102["I1102<br/>The two namespaces are a typed contract, not a habit"]:::blocked
    I1103["I1103<br/>Whether a file is authored or derived is a field, not a naming convention"]:::blocked
    I1104["I1104<br/>Both namespaces carry their contracts, all the way down"]:::blocked
    I1105["I1105<br/>The repository is discovered once, into one model"]:::blocked
    I1106["I1106<br/>The registries are views of the model, not separate discoveries"]:::blocked
    I1107["I1107<br/>Discovery is deterministic, cached, and its cost is known"]:::blocked
    I1108["I1108<br/>The desired integration is a value, and the plan is a diff"]:::blocked
    I1109["I1109<br/>Everything placed outside the two namespaces is recorded, with what it was"]:::blocked
    I1110["I1110<br/>The second run changes nothing, and a test says so"]:::blocked
    I1111["I1111<br/>Uninstall gives back exactly what was taken"]:::blocked
    I1112["I1112<br/>What touches the root is two small blocks, and they never change again"]:::blocked
    I1113["I1113<br/>Entering a directory is local, bounded and silent about the network"]:::blocked
    I1114["I1114<br/>A provider is an adapter over the model, and adding one is an implementation"]:::blocked
    I1115["I1115<br/>Completion comes from the command model, not from a second list"]:::blocked
    I1116["I1116<br/>This repository's own footprint moves, through the reconciler"]:::blocked
    I1117["I1117<br/>The hand-maintained lists are removed, not left running beside the derivation"]:::blocked
    I1118["I1118<br/>Every invariant of this milestone is a rule with a check that runs"]:::blocked
    I1119["I1119<br/>The budgets are measured, and a regression fails rather than being noticed"]:::blocked
    I1120["I1120<br/>A refusal says what to do about it"]:::blocked
    I1121["I1121<br/>Every surface shows the integration from the one model"]:::blocked
    I1122["I1122<br/>A foreign repository, adopted, upgraded and given back"]:::blocked
    I1123["I1123<br/>The milestone is accepted on its evidence"]:::blocked
    I1101 --> I1102
    I1101 --> I1105
    I1102 --> I1103
    I1102 --> I1104
    I1104 --> I1123
    I1105 --> I1106
    I1106 --> I1107
    I1106 --> I1108
    I1106 --> I1117
    I1107 --> I1123
    I1108 --> I1109
    I1108 --> I1120
    I1109 --> I1110
    I1109 --> I1111
    I1109 --> I1112
    I1110 --> I1116
    I1110 --> I1118
    I1110 --> I1122
    I1111 --> I1122
    I1112 --> I1113
    I1112 --> I1114
    I1112 --> I1116
    I1112 --> I1121
    I1113 --> I1119
    I1114 --> I1115
    I1115 --> I1123
    I1116 --> I1118
    I1116 --> I1122
    I1117 --> I1123
    I1118 --> I1123
    I1119 --> I1123
    I1120 --> I1123
    I1121 --> I1122
    I1122 --> I1123
    classDef done stroke:#16a34a,fill:#052e16,stroke-width:2px
    classDef active stroke:#2563eb,fill:#eff6ff,stroke-width:2px
    classDef verify stroke:#7c3aed,fill:#f5f3ff,stroke-width:2px
    classDef ready stroke:#0891b2,fill:#ecfeff,stroke-width:2px
    classDef blocked stroke:#b45309,fill:#fffbeb,stroke-width:2px
    classDef cancelled stroke:#6b7280,fill:#f9fafb,stroke-width:2px

Canonical record: .ai/repo/project/milestones/two-folder-integration.yaml. Read it back with majordomus plan show two-folder-integration.