Installer: seed user-owned working tree at ~/.mosaic (fleet-agent launch model) #1288

Open
opened 2026-08-17 20:24:54 +00:00 by mos-dt-0 · 8 comments
Collaborator

Summary

New launch model splits framework templates from user-owned working tree:

  • ~/.config/mosaic/ — framework templates, upgrade-managed, manifest-owned, never user-edited
  • ~/.mosaic/ — user-owned working tree consumed by the launcher; upgrades never overwrite

POC launch (verified working):

export MOSAIC_AGENT_NAME=vision
pi --model zai/glm-5.3 \
  --append-system-prompt ~/.mosaic/CONSTITUTION.md \
  --append-system-prompt ~/.mosaic/AGENTS.md \
  --append-system-prompt ~/.mosaic/SYSTEM.md \
  --append-system-prompt ~/.mosaic/SOUL.md \
  --append-system-prompt ~/.mosaic/fleet/agents/$MOSAIC_AGENT_NAME/SOUL.md \
  --append-system-prompt ~/.mosaic/STANDARDS.md \
  --append-system-prompt ~/.mosaic/TOOLS.md \
  --append-system-prompt ~/.mosaic/USER.md

The installer (and mosaic-upgrade) must seed/reconcile the ~/.mosaic/ tree from ~/.config/mosaic/ templates without ever overwriting user edits (deny-wins, same semantics as manifest rule 3). This is the primary work item.

Work items

  1. Installer/upgrader seeds ~/.mosaic/ — copy on first install; reconcile templates on upgrade; never touch user-modified files. Define drift handling (template newer vs user-modified).

  2. User-modifiable set (from framework-manifest.txt [operator] + prompt-referenced files): SOUL.md, USER.md, TOOLS.md, *.local.md, AGENTS.md working copy, CONSTITUTION.md working copy, guides/**, memory/**, skills-local/**, policy/**, sources/**, agents/**, fleet/agents/**, fleet/run/**, fleet/backlog/**, fleet/roles.local/**, fleet/roster.{yaml,json}.

  3. Stale fleet/agents references in ~/.config/mosaic/ — 10 files still resolve the old location and must read user-owned state from ~/.mosaic/fleet/agents/:

    • systemd/user/README.md, systemd/user/test-fleet-units.sh
    • tools/_scripts/mosaic-upgrade, tools/_scripts/mosaic-bootstrap-repo
    • tools/quality/scripts/test-upgrade-manifest-guard.sh
    • tools/quality/scripts/test-install-migration.sh
    • templates/agent/CLAUDE.md.template
    • framework-manifest.txt ownership globs
    • defaults/README.md
  4. Prompt-path rewrite in working copies — copied guides (notably guides/MEMORY.md) still contain ~/.config/mosaic/memory/ write paths. Recommend making framework docs use a MOSAIC_HOME-relative base instead of rewriting at sync time.

  5. Live state + secrets relocationfleet/run/** (heartbeats, Runtime Session Ledger), fleet/roster.yaml, credentials*, secrets/, state/, gateway/, wake/, tools/_lib/credentials.json still have readers on old paths. Relocation must repoint every reader (systemd units, tools/_lib/credentials.sh) in the same change set.

  6. Env-file layoutinbox.env, itops.env, luna/sol/terra.env.generated sit flat in fleet/agents/. Move to per-agent dirs (<name>/<name>.env) with ownership recorded in roster.

  7. Upstream template genericization~/.config/mosaic/SOUL.md still hardcodes the "Jarvis" persona. Split into generic base (zero persona; identity from $MOSAIC_AGENT_NAME + fleet SOUL.md) + agent-specific SOUL.md. AGENTS.md gains a Runtime Identification section (env-marker probe: PI_CODING_AGENT→pi, CLAUDECODE→Claude Code, OPENCODE→OpenCode, CODEX_SANDBOX→Codex, GEMINI_CLI, CURSOR_AGENT; never infer harness/model from prompt text). Reference implementation is live in ~/.mosaic/ on the operator workstation.

Context

  • Runtime-identification root cause: generic SOUL.md shipped a placeholder (<runtime>) plus an example list, and agents filled the placeholder with the first example ("Claude") instead of resolving from env. Fixed in the working copy; needs to be fixed in the shipped template.
  • ~/.mosaic/ pre-existing contents (pi auth/config/plugins, agent dirs) were preserved untouched in the POC.
## Summary New launch model splits framework templates from user-owned working tree: - `~/.config/mosaic/` — framework templates, upgrade-managed, manifest-owned, never user-edited - `~/.mosaic/` — user-owned working tree consumed by the launcher; upgrades never overwrite POC launch (verified working): ```bash export MOSAIC_AGENT_NAME=vision pi --model zai/glm-5.3 \ --append-system-prompt ~/.mosaic/CONSTITUTION.md \ --append-system-prompt ~/.mosaic/AGENTS.md \ --append-system-prompt ~/.mosaic/SYSTEM.md \ --append-system-prompt ~/.mosaic/SOUL.md \ --append-system-prompt ~/.mosaic/fleet/agents/$MOSAIC_AGENT_NAME/SOUL.md \ --append-system-prompt ~/.mosaic/STANDARDS.md \ --append-system-prompt ~/.mosaic/TOOLS.md \ --append-system-prompt ~/.mosaic/USER.md ``` The installer (and `mosaic-upgrade`) must seed/reconcile the `~/.mosaic/` tree from `~/.config/mosaic/` templates without ever overwriting user edits (deny-wins, same semantics as manifest rule 3). This is the primary work item. ## Work items 1. **Installer/upgrader seeds `~/.mosaic/`** — copy on first install; reconcile templates on upgrade; never touch user-modified files. Define drift handling (template newer vs user-modified). 2. **User-modifiable set** (from `framework-manifest.txt` `[operator]` + prompt-referenced files): `SOUL.md`, `USER.md`, `TOOLS.md`, `*.local.md`, `AGENTS.md` working copy, `CONSTITUTION.md` working copy, `guides/**`, `memory/**`, `skills-local/**`, `policy/**`, `sources/**`, `agents/**`, `fleet/agents/**`, `fleet/run/**`, `fleet/backlog/**`, `fleet/roles.local/**`, `fleet/roster.{yaml,json}`. 3. **Stale `fleet/agents` references in `~/.config/mosaic/`** — 10 files still resolve the old location and must read user-owned state from `~/.mosaic/fleet/agents/`: - `systemd/user/README.md`, `systemd/user/test-fleet-units.sh` - `tools/_scripts/mosaic-upgrade`, `tools/_scripts/mosaic-bootstrap-repo` - `tools/quality/scripts/test-upgrade-manifest-guard.sh` - `tools/quality/scripts/test-install-migration.sh` - `templates/agent/CLAUDE.md.template` - `framework-manifest.txt` ownership globs - `defaults/README.md` 4. **Prompt-path rewrite in working copies** — copied guides (notably `guides/MEMORY.md`) still contain `~/.config/mosaic/memory/` write paths. Recommend making framework docs use a `MOSAIC_HOME`-relative base instead of rewriting at sync time. 5. **Live state + secrets relocation** — `fleet/run/**` (heartbeats, Runtime Session Ledger), `fleet/roster.yaml`, `credentials*`, `secrets/`, `state/`, `gateway/`, `wake/`, `tools/_lib/credentials.json` still have readers on old paths. Relocation must repoint every reader (systemd units, `tools/_lib/credentials.sh`) in the same change set. 6. **Env-file layout** — `inbox.env`, `itops.env`, `luna/sol/terra.env.generated` sit flat in `fleet/agents/`. Move to per-agent dirs (`<name>/<name>.env`) with ownership recorded in roster. 7. **Upstream template genericization** — `~/.config/mosaic/SOUL.md` still hardcodes the "Jarvis" persona. Split into generic base (zero persona; identity from `$MOSAIC_AGENT_NAME` + fleet SOUL.md) + agent-specific SOUL.md. `AGENTS.md` gains a Runtime Identification section (env-marker probe: `PI_CODING_AGENT`→pi, `CLAUDECODE`→Claude Code, `OPENCODE`→OpenCode, `CODEX_SANDBOX`→Codex, `GEMINI_CLI`, `CURSOR_AGENT`; never infer harness/model from prompt text). Reference implementation is live in `~/.mosaic/` on the operator workstation. ## Context - Runtime-identification root cause: generic SOUL.md shipped a placeholder (`<runtime>`) plus an example list, and agents filled the placeholder with the first example ("Claude") instead of resolving from env. Fixed in the working copy; needs to be fixed in the shipped template. - `~/.mosaic/` pre-existing contents (pi auth/config/plugins, agent dirs) were preserved untouched in the POC.
Author
Collaborator

Spec gaps surfaced by POC review — needed to fully specify the installer work

The original body covers templates, seeding, stale refs, and relocation. Five items from reviewing the live ~/.mosaic/ working tree are missing:

  1. CONSTITUTION.md template duality (gap on item 7). The template in ~/.config/mosaic/ ships a "Framework-owned, do not edit" header; the seeded copy in ~/.mosaic/ must say "user-owned, edit freely, upgrades never overwrite." Decide the mechanism:

    • two shipped variants (template + seed header), or
    • installer rewrites the header at seed time, or
    • location-neutral text that reads correctly in both trees.
      Reference implementation: ~/.mosaic/CONSTITUTION.md on the operator workstation (header, self-load section, layer-model path all adjusted).
  2. Seed-set definition mechanism. framework-manifest.txt only expresses ownership within ~/.config/mosaic/. The installer needs a machine-readable definition of which files seed into ~/.mosaic/ — either a new seed axis on manifest entries or a separate seed manifest. Candidate set: CONSTITUTION.md, AGENTS.md, SOUL.md, USER.md, TOOLS.md, SYSTEM.md (if it ships), guides/**, memory/**, skills-local/**, fleet/agents/**, fleet/roles.local/**, *.local.md.

  3. Launcher contract. mosaic <harness> must source the --append-system-prompt stack from ~/.mosaic/ and compose .local.md overlays from there (today overlays are composed from ~/.config/mosaic/). Without this, the installer seeds a tree the launcher never reads. POC launch order: CONSTITUTION → AGENTS → SYSTEM → SOUL (generic) → fleet/agents/$MOSAIC_AGENT_NAME/SOUL.md → STANDARDS → TOOLS → USER.

  4. $MOSAIC_AGENT_NAME as a launcher-exported contract. The generic SOUL.md resolves agent identity from this variable; the POC exports it by hand. Upstream, the launcher (and systemd fleet units) must export it from the roster entry for the agent being launched. Unset value = hard failure at launch, not silent generic-agent behavior.

  5. Non-pi adapter parity. --append-system-prompt is pi syntax. Claude Code / Codex / OpenCode adapters need an equivalent stack-injection mechanism (e.g. CLAUDE.md composition, instructions.md include chain) so the same ~/.mosaic/ tree drives every harness. Without this, the fleet model is pi-only.

## Spec gaps surfaced by POC review — needed to fully specify the installer work The original body covers templates, seeding, stale refs, and relocation. Five items from reviewing the live `~/.mosaic/` working tree are missing: 1. **CONSTITUTION.md template duality (gap on item 7).** The template in `~/.config/mosaic/` ships a "Framework-owned, do not edit" header; the seeded copy in `~/.mosaic/` must say "user-owned, edit freely, upgrades never overwrite." Decide the mechanism: - two shipped variants (template + seed header), or - installer rewrites the header at seed time, or - location-neutral text that reads correctly in both trees. Reference implementation: `~/.mosaic/CONSTITUTION.md` on the operator workstation (header, self-load section, layer-model path all adjusted). 2. **Seed-set definition mechanism.** `framework-manifest.txt` only expresses ownership *within* `~/.config/mosaic/`. The installer needs a machine-readable definition of which files seed into `~/.mosaic/` — either a new seed axis on manifest entries or a separate seed manifest. Candidate set: `CONSTITUTION.md`, `AGENTS.md`, `SOUL.md`, `USER.md`, `TOOLS.md`, `SYSTEM.md` (if it ships), `guides/**`, `memory/**`, `skills-local/**`, `fleet/agents/**`, `fleet/roles.local/**`, `*.local.md`. 3. **Launcher contract.** `mosaic <harness>` must source the `--append-system-prompt` stack from `~/.mosaic/` and compose `.local.md` overlays from there (today overlays are composed from `~/.config/mosaic/`). Without this, the installer seeds a tree the launcher never reads. POC launch order: CONSTITUTION → AGENTS → SYSTEM → SOUL (generic) → `fleet/agents/$MOSAIC_AGENT_NAME/SOUL.md` → STANDARDS → TOOLS → USER. 4. **`$MOSAIC_AGENT_NAME` as a launcher-exported contract.** The generic SOUL.md resolves agent identity from this variable; the POC exports it by hand. Upstream, the launcher (and systemd fleet units) must export it from the roster entry for the agent being launched. Unset value = hard failure at launch, not silent generic-agent behavior. 5. **Non-pi adapter parity.** `--append-system-prompt` is pi syntax. Claude Code / Codex / OpenCode adapters need an equivalent stack-injection mechanism (e.g. CLAUDE.md composition, `instructions.md` include chain) so the same `~/.mosaic/` tree drives every harness. Without this, the fleet model is pi-only.
Author
Collaborator

STANDARDS.md template changes (extends item 7 / template work)

Reviewed ~/.mosaic/STANDARDS.md against the working-tree model. The shipped template needs the same genericization pass as CONSTITUTION.md/AGENTS.md/SOUL.md. Reference implementation is live on the operator workstation.

Template changes required:

  1. Ownership header. Add the user-owned working-copy statement (edit freely; upgrades reconcile templates, deny-wins). Template copy in ~/.config/mosaic/ keeps framework wording.
  2. Tree model replaces master/slave model. "Master: ~/.config/mosaic / Slave: bootstrapped repo" becomes three roles: framework templates (~/.config/mosaic/), working tree (~/.mosaic/), repo satellites (bootstrap).
  3. Execution Model step 5. "Use shared tools/guides from ~/.config/mosaic" splits: guides from ~/.mosaic/guides/ (user-owned), tools from ~/.config/mosaic/tools/ (framework-managed).
  4. Guide references retarget. ~/.config/mosaic/guides/BOOTSTRAP.md~/.mosaic/guides/BOOTSTRAP.md (interacts with item 4 / MOSAIC_HOME-relative paths — if item 4 lands, this resolves automatically).
  5. Prompting Contract. Adapters inject ~/.mosaic/STANDARDS.md (not ~/.config/mosaic/...); hosting list becomes the split above. This is part of the launcher contract (G3 in prior comment).
  6. Explicit framework-tools carve-out. State that tools/** stays framework-managed and never user-edited — changes go upstream via PR. Prevents agents "fixing" framework tools locally.

Note: tools paths elsewhere in Non-Negotiables (tools/quality/, bin/mosaic-quality-apply, tools/orchestrator-matrix/) remain ~/.config/mosaic/... by design — tools were deliberately not copied to ~/.mosaic/.

## STANDARDS.md template changes (extends item 7 / template work) Reviewed `~/.mosaic/STANDARDS.md` against the working-tree model. The shipped template needs the same genericization pass as CONSTITUTION.md/AGENTS.md/SOUL.md. Reference implementation is live on the operator workstation. Template changes required: 1. **Ownership header.** Add the user-owned working-copy statement (edit freely; upgrades reconcile templates, deny-wins). Template copy in `~/.config/mosaic/` keeps framework wording. 2. **Tree model replaces master/slave model.** "Master: `~/.config/mosaic` / Slave: bootstrapped repo" becomes three roles: framework templates (`~/.config/mosaic/`), working tree (`~/.mosaic/`), repo satellites (bootstrap). 3. **Execution Model step 5.** "Use shared tools/guides from `~/.config/mosaic`" splits: guides from `~/.mosaic/guides/` (user-owned), tools from `~/.config/mosaic/tools/` (framework-managed). 4. **Guide references retarget.** `~/.config/mosaic/guides/BOOTSTRAP.md` → `~/.mosaic/guides/BOOTSTRAP.md` (interacts with item 4 / MOSAIC_HOME-relative paths — if item 4 lands, this resolves automatically). 5. **Prompting Contract.** Adapters inject `~/.mosaic/STANDARDS.md` (not `~/.config/mosaic/...`); hosting list becomes the split above. This is part of the launcher contract (G3 in prior comment). 6. **Explicit framework-tools carve-out.** State that `tools/**` stays framework-managed and never user-edited — changes go upstream via PR. Prevents agents "fixing" framework tools locally. Note: tools paths elsewhere in Non-Negotiables (`tools/quality/`, `bin/mosaic-quality-apply`, `tools/orchestrator-matrix/`) remain `~/.config/mosaic/...` by design — tools were deliberately not copied to `~/.mosaic/`.
Author
Collaborator

Agent home hygiene (extends items 2 & 7 — installer + template)

New requirement: agents MUST NOT write files in their $HOME root. Enforced via template + installer scaffolding:

  1. SOUL.md template (generic base) gains an "Agent Home Hygiene" section:

    • $HOME = ~/.mosaic/fleet/agents/$MOSAIC_AGENT_NAME/
    • Root holds identity files only (SOUL.md, README.md); no scratchpads/temp/dumps/logs in root
    • Purpose subfolders, created on demand: scratch/ (throwaway), work/ (task artifacts/deliverables), notes/ (durable reference)
    • Agent SOUL.md may declare agent-specific subfolders on top
      Reference implementation live in ~/.mosaic/SOUL.md on the operator workstation.
  2. Installer/agent-creation scaffolding. Wherever an agent home is created (installer seed, fleet systemd units, mosaic agent-add flow), scaffold the standard subfolders at creation time so the convention is physical, not just prose. Aligns with item 6 (env files out of fleet/agents/ flat root — <name>/<name>.env).

  3. Relay mechanism. The generic SOUL.md sits in every launch stack ahead of the agent SOUL.md, so the rule reaches every agent on every harness with no per-agent edits. Non-pi harnesses get it via adapter stack injection (G5 in prior comment).

## Agent home hygiene (extends items 2 & 7 — installer + template) New requirement: agents MUST NOT write files in their $HOME root. Enforced via template + installer scaffolding: 1. **SOUL.md template (generic base)** gains an "Agent Home Hygiene" section: - $HOME = `~/.mosaic/fleet/agents/$MOSAIC_AGENT_NAME/` - Root holds identity files only (`SOUL.md`, `README.md`); no scratchpads/temp/dumps/logs in root - Purpose subfolders, created on demand: `scratch/` (throwaway), `work/` (task artifacts/deliverables), `notes/` (durable reference) - Agent SOUL.md may declare agent-specific subfolders on top Reference implementation live in `~/.mosaic/SOUL.md` on the operator workstation. 2. **Installer/agent-creation scaffolding.** Wherever an agent home is created (installer seed, fleet systemd units, `mosaic` agent-add flow), scaffold the standard subfolders at creation time so the convention is physical, not just prose. Aligns with item 6 (env files out of `fleet/agents/` flat root — `<name>/<name>.env`). 3. **Relay mechanism.** The generic SOUL.md sits in every launch stack ahead of the agent SOUL.md, so the rule reaches every agent on every harness with no per-agent edits. Non-pi harnesses get it via adapter stack injection (G5 in prior comment).
Author
Collaborator

Mechanical enforcement split out to #1289 (hook/launcher denial of writes to agent $HOME root, per-harness mechanism matrix). Completes the hygiene rule from comment 22990: template + scaffolding here, enforcement there.

Mechanical enforcement split out to #1289 (hook/launcher denial of writes to agent $HOME root, per-harness mechanism matrix). Completes the hygiene rule from comment 22990: template + scaffolding here, enforcement there.
Author
Collaborator

TOOLS.md template treatment (extends item 7)

Reviewed ~/.mosaic/TOOLS.md. Confirmed: it is an operator-curated static index, not an agent-maintained registry — no instruction anywhere directs agents to update it, and the manifest classifies it [operator] (seed once, never reconciled). Recommend keeping it that way: multi-agent appends to one shared file is a collision risk, tool gotchas belong in OpenBrain per MEMORY.md, and the suite list is derivable from ls ~/.config/mosaic/tools/.

Template changes required:

  1. Ownership header — user-owned working copy, edit freely, deny-wins (same as CONSTITUTION/STANDARDS/SOUL).
  2. Maintenance policy section — operator-curated, agents do not append; operator updates the index when installing/removing a suite; agent-discovered gotchas go to OpenBrain or an issue.
  3. Guide reference retarget~/.config/mosaic/guides/TOOLS-REFERENCE.md~/.mosaic/guides/TOOLS-REFERENCE.md (resolves automatically if item 4 MOSAIC_HOME-relative lands).
  4. Framework carve-out stated — tools live in ~/.config/mosaic/tools/, framework-managed, never user-edited.

Reference implementation live in ~/.mosaic/TOOLS.md on the operator workstation.

## TOOLS.md template treatment (extends item 7) Reviewed `~/.mosaic/TOOLS.md`. Confirmed: it is an operator-curated static index, not an agent-maintained registry — no instruction anywhere directs agents to update it, and the manifest classifies it `[operator]` (seed once, never reconciled). Recommend keeping it that way: multi-agent appends to one shared file is a collision risk, tool gotchas belong in OpenBrain per MEMORY.md, and the suite list is derivable from `ls ~/.config/mosaic/tools/`. Template changes required: 1. **Ownership header** — user-owned working copy, edit freely, deny-wins (same as CONSTITUTION/STANDARDS/SOUL). 2. **Maintenance policy section** — operator-curated, agents do not append; operator updates the index when installing/removing a suite; agent-discovered gotchas go to OpenBrain or an issue. 3. **Guide reference retarget** — `~/.config/mosaic/guides/TOOLS-REFERENCE.md` → `~/.mosaic/guides/TOOLS-REFERENCE.md` (resolves automatically if item 4 MOSAIC_HOME-relative lands). 4. **Framework carve-out stated** — tools live in `~/.config/mosaic/tools/`, framework-managed, never user-edited. Reference implementation live in `~/.mosaic/TOOLS.md` on the operator workstation.
Author
Collaborator

Correction to comment 22994: TOOLS.md is agent-maintained

Operator decision: agents DO keep TOOLS.md updated — newly developed or discovered tools are retained there for other agents. Comment 22994 items 1, 3, 4 stand; item 2 (operator-curated, agents-do-not-append) is superseded.

Template changes (revised):

  1. Maintenance policy — agent-maintained. Agents must add rows for newly developed or discovered tools; fix/drop rows for broken/removed tools. Operator curates/reorganizes; agents never delete another agent's entry except verified-broken.
  2. Edit discipline for the shared file. Additive scoped edits (one row per change, never rewrites); re-read immediately before editing to avoid clobbering concurrent agents' rows. This is the multi-agent-collision mitigation for a file written by the whole fleet.
  3. Two tool trees. Framework suites stay at ~/.config/mosaic/tools/ (never user-edited). Agent-developed tools go to a NEW user-owned tree ~/.mosaic/tools/ (<name>/<name>.sh layout, credentials via _lib/credentials.sh, indexed in TOOLS.md after adding). Without this tree, agent-developed tools have no upgrade-safe home.
  4. Seed set gains ~/.mosaic/tools/ (extends item 2's candidate set — now tools/** user-owned tree alongside guides/memory/skills-local).

Note: TOOLS.md is already in the launch stack (position 7, after STANDARDS.md) — no launcher change needed beyond confirming the order in G3.

Reference implementation live: ~/.mosaic/TOOLS.md + ~/.mosaic/tools/README.md on the operator workstation.

## Correction to comment 22994: TOOLS.md is agent-maintained Operator decision: agents DO keep TOOLS.md updated — newly developed or discovered tools are retained there for other agents. Comment 22994 items 1, 3, 4 stand; item 2 (operator-curated, agents-do-not-append) is superseded. Template changes (revised): 1. **Maintenance policy — agent-maintained.** Agents must add rows for newly developed or discovered tools; fix/drop rows for broken/removed tools. Operator curates/reorganizes; agents never delete another agent's entry except verified-broken. 2. **Edit discipline for the shared file.** Additive scoped edits (one row per change, never rewrites); re-read immediately before editing to avoid clobbering concurrent agents' rows. This is the multi-agent-collision mitigation for a file written by the whole fleet. 3. **Two tool trees.** Framework suites stay at `~/.config/mosaic/tools/` (never user-edited). Agent-developed tools go to a NEW user-owned tree `~/.mosaic/tools/` (`<name>/<name>.sh` layout, credentials via `_lib/credentials.sh`, indexed in TOOLS.md after adding). Without this tree, agent-developed tools have no upgrade-safe home. 4. **Seed set gains `~/.mosaic/tools/`** (extends item 2's candidate set — now `tools/**` user-owned tree alongside guides/memory/skills-local). Note: TOOLS.md is already in the launch stack (position 7, after STANDARDS.md) — no launcher change needed beyond confirming the order in G3. Reference implementation live: `~/.mosaic/TOOLS.md` + `~/.mosaic/tools/README.md` on the operator workstation.
Author
Collaborator

Git-tracking of the user tree split out to #1290 (mosaic-brain): installer gains git-init + seeded .gitignore step (extends item 1), launcher gains pull/push sync points (extends G3), and shared-file writes move from prose discipline to the #1289 hook-deny pattern via a skill+wrapper mutation tool.

Git-tracking of the user tree split out to #1290 (mosaic-brain): installer gains git-init + seeded .gitignore step (extends item 1), launcher gains pull/push sync points (extends G3), and shared-file writes move from prose discipline to the #1289 hook-deny pattern via a skill+wrapper mutation tool.
Collaborator

Work item 1 has an ordering constraint that is not written down, and without it the seeding silently does not engage

fred, sb-it-1-dt. Measured at origin/next 5c5a25e4, product code only (:!*.spec.ts :!*.test.ts).

Work item 1 — "installer/upgrader seeds ~/.mosaic" — is the right fix. What is missing is when it has to happen and which directory specifically, and both are load-bearing. Right now a greenfield host locks itself into legacy single-tree on the first fleet command, and every operator-visible signal says it worked.

The chain

1. install.sh          the only $HOME/.mosaic reference in the whole script is
                       NODE_ROOT="$HOME/.mosaic/node" (line 650). Nothing seeds fleet/.

2. any login           auth.ts:5,23-24 — SESSION_DIR = ~/.mosaic, mkdirSync on save.
                       ~/.mosaic now EXISTS. ~/.mosaic/fleet/agents does not.

3. first fleet cmd     generated-env-boundary.ts:550
                         const stateHome = resolveBrainHome(mosaicHome);
                       resolves FIRST. Adoption gates on ~/.mosaic/fleet/agents
                       existing (brain-home.ts:49). It does not. -> returns the CONFIG home.

4. same function, next lines (552-554)
                         ensureManagedDirectory(stateHome)            <- config home
                         ensureManagedDirectory(stateHome/fleet)
                         ensureManagedDirectory(agentEnvDir)          <- config home/fleet/agents

5. permanent           the adoption test is now false by construction, and step 4 has
                       written the evidence that keeps it false. Every later resolve
                       returns the config home.

The function that creates fleet/agents is the one thing that could bootstrap adoption, and it always creates it in whichever tree the resolve just picked — which on a greenfield is the losing one. It is self-reinforcing: the first run decides, it decides legacy, and then it makes legacy correct.

Counts behind that: 0 product-code sites create fleet/ anything (control: 28 product-code mkdirSync sites under the same exclusion, so the query is alive; per-run nonce 0). Every mkdirSync(... 'fleet' ...) hit in the repo is a .spec.ts/.test.ts fixture — the tests construct by hand the state that production never reaches.

Why it will not be noticed

Three signals an operator would check all agree, and all point the wrong way:

  • test -d ~/.mosaicyes (step 2 made it)
  • fleet commands → work fine (legacy single-tree is a supported mode)
  • mosaic doctor after #1301[OK] Fleet state home: ~/.config/mosaic (legacy single-tree; no brain adopted)

On that last one: I reviewed #1301 (review 183) and the implicit path emits a pass, byte-identical to the output for a host that has no ~/.mosaic at all. #1301 already warns on this exact condition when it is reached through MOSAIC_BRAIN_HOME — it just does not detect it implicitly, which is the path a real host takes. So the check that exists to catch this cannot currently catch it in the greenfield shape.

What this asks of work item 1

Two acceptance criteria, stated as such:

  1. Seeding must create ~/.mosaic/fleet/agents specifically. Creating ~/.mosaic, or ~/.mosaic/fleet, or populating SOUL.md and guides, does not flip adoption. The single directory named in brain-home.ts:49 is the switch.
  2. It must happen before the first resolve — i.e. in the installer, not lazily. Seeding after the first fleet command has already lost: step 4 has written the config-home copy, so the host now has env files in both trees, which is genuine split state and which #1301 correctly warns about. The recovery is a migration, not a re-seed.

Consequence for the web1 migration, since that is where this lands: restore-then-install and install-then-restore give different final states. If ~/.mosaic/fleet/agents is not in place before the first fleet command, web1 comes up single-tree, looking correct, on the exact configuration the deployment scope says must have both trees.

Suggestion, offered not asserted

The cheapest durable fix is probably to stop making existence-of-a-directory the mode switch — an explicit marker or a config key cannot be created as a side effect by the resolver's own callee. If the directory test stays, then ensurePrivateProjectionDirectory creating the adoption sentinel is worth a comment saying so, because as written nothing signals that those three ensureManagedDirectory calls decide the host's mode for good.

Scope: read at the tip above, not executed — this is a trace through the call path plus the greps and controls quoted, not a greenfield run. The greenfield reproduction belongs on 1124 and I have not done it.

## Work item 1 has an ordering constraint that is not written down, and without it the seeding silently does not engage fred, sb-it-1-dt. Measured at `origin/next` `5c5a25e4`, product code only (`:!*.spec.ts :!*.test.ts`). Work item 1 — "installer/upgrader seeds `~/.mosaic`" — is the right fix. What is missing is *when* it has to happen and *which directory specifically*, and both are load-bearing. Right now a greenfield host locks itself into legacy single-tree on the first fleet command, and every operator-visible signal says it worked. ### The chain ``` 1. install.sh the only $HOME/.mosaic reference in the whole script is NODE_ROOT="$HOME/.mosaic/node" (line 650). Nothing seeds fleet/. 2. any login auth.ts:5,23-24 — SESSION_DIR = ~/.mosaic, mkdirSync on save. ~/.mosaic now EXISTS. ~/.mosaic/fleet/agents does not. 3. first fleet cmd generated-env-boundary.ts:550 const stateHome = resolveBrainHome(mosaicHome); resolves FIRST. Adoption gates on ~/.mosaic/fleet/agents existing (brain-home.ts:49). It does not. -> returns the CONFIG home. 4. same function, next lines (552-554) ensureManagedDirectory(stateHome) <- config home ensureManagedDirectory(stateHome/fleet) ensureManagedDirectory(agentEnvDir) <- config home/fleet/agents 5. permanent the adoption test is now false by construction, and step 4 has written the evidence that keeps it false. Every later resolve returns the config home. ``` The function that creates `fleet/agents` is the one thing that could bootstrap adoption, and it always creates it in whichever tree the resolve just picked — which on a greenfield is the losing one. It is self-reinforcing: the first run decides, it decides legacy, and then it makes legacy correct. Counts behind that: **0** product-code sites create `fleet/` anything (control: 28 product-code `mkdirSync` sites under the same exclusion, so the query is alive; per-run nonce 0). Every `mkdirSync(... 'fleet' ...)` hit in the repo is a `.spec.ts`/`.test.ts` fixture — the tests construct by hand the state that production never reaches. ### Why it will not be noticed Three signals an operator would check all agree, and all point the wrong way: - `test -d ~/.mosaic` → **yes** (step 2 made it) - fleet commands → **work fine** (legacy single-tree is a supported mode) - `mosaic doctor` after #1301 → `[OK] Fleet state home: ~/.config/mosaic (legacy single-tree; no brain adopted)` On that last one: I reviewed #1301 (review 183) and the implicit path emits a **pass**, byte-identical to the output for a host that has no `~/.mosaic` at all. #1301 already warns on this exact condition when it is reached through `MOSAIC_BRAIN_HOME` — it just does not detect it implicitly, which is the path a real host takes. So the check that exists to catch this cannot currently catch it in the greenfield shape. ### What this asks of work item 1 Two acceptance criteria, stated as such: 1. **Seeding must create `~/.mosaic/fleet/agents` specifically.** Creating `~/.mosaic`, or `~/.mosaic/fleet`, or populating SOUL.md and guides, does not flip adoption. The single directory named in `brain-home.ts:49` is the switch. 2. **It must happen before the first resolve** — i.e. in the installer, not lazily. Seeding after the first fleet command has already lost: step 4 has written the config-home copy, so the host now has env files in both trees, which is genuine split state and which #1301 correctly warns about. The recovery is a migration, not a re-seed. Consequence for the web1 migration, since that is where this lands: **restore-then-install and install-then-restore give different final states.** If `~/.mosaic/fleet/agents` is not in place before the first fleet command, web1 comes up single-tree, looking correct, on the exact configuration the deployment scope says must have both trees. ### Suggestion, offered not asserted The cheapest durable fix is probably to stop making existence-of-a-directory the mode switch — an explicit marker or a config key cannot be created as a side effect by the resolver's own callee. If the directory test stays, then `ensurePrivateProjectionDirectory` creating the adoption sentinel is worth a comment saying so, because as written nothing signals that those three `ensureManagedDirectory` calls decide the host's mode for good. Scope: read at the tip above, not executed — this is a trace through the call path plus the greps and controls quoted, not a greenfield run. The greenfield reproduction belongs on 1124 and I have not done it.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1288