Files
stack/docs/scratchpads/tools-md-seeding-20260411.md
Jason Woltje 03a53c543a
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
fix(mosaic): seed TOOLS.md from defaults on install
Closes #457.

The bash framework installer only seeded AGENTS.md and STANDARDS.md from
defaults/, even though TOOLS.md is listed in PRESERVE_PATHS and AGENTS.md
declares it as mandatory reading at position 5 of the load order. A fresh
bootstrap install therefore left ~/.config/mosaic/TOOLS.md missing and the
agent contract pointing at a non-existent file.

Fixes:

- packages/mosaic/framework/install.sh — extend the explicit defaults-seed
  loop from "AGENTS.md STANDARDS.md" to "AGENTS.md STANDARDS.md TOOLS.md".
- packages/mosaic/src/config/file-adapter.ts — replace the greedy
  readdirSync loop in syncFramework with an exported DEFAULT_SEED_FILES
  whitelist, so the TS wizard no longer silently seeds the Jarvis-flavored
  defaults/SOUL.md, placeholder defaults/USER.md, or internal
  README.md/AUDIT-*.md into the mosaic home. Also align preservePaths with
  the bash PRESERVE_PATHS list (AGENTS.md, STANDARDS.md, sources, and
  credentials were previously missing) so both install paths have the
  same upgrade-preservation semantics.
- packages/mosaic/framework/templates/TOOLS.md.template — replace stale
  ~/.config/mosaic/rails/ references with ~/.config/mosaic/tools/. The
  rails/ tree was renamed to tools/ in the v1→v2 framework migration.

Tests:

- packages/mosaic/src/config/file-adapter.test.ts (new, 5 tests): pins
  the whitelist, asserts SOUL.md/USER.md/README.md/AUDIT-*.md are not
  seeded, verifies existing user contract files (including AGENTS.md)
  survive a keep-mode sync, and asserts a no-op when defaults/ is absent.

Baselines: mosaic typecheck / lint green. Full mosaic vitest 275/276 —
the one failure (src/commands/uninstall.spec.ts:138) is a pre-existing
EACCES issue on main and is unrelated to this change. Repo-wide
typecheck / lint / format:check green. Live smoke of
`bash framework/install.sh` against a tmp MOSAIC_HOME confirms the
installer now prints "Seeded TOOLS.md from defaults" and the file lands.

Ships in @mosaicstack/mosaic 0.0.30.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 20:56:55 -05:00

111 lines
5.9 KiB
Markdown

# Hotfix Scratchpad — `install.sh` does not seed `TOOLS.md`
- **Issue:** mosaicstack/stack#457
- **Branch:** `fix/tools-md-seeding`
- **Type:** Out-of-mission hotfix (not part of Install UX v2 mission)
- **Started:** 2026-04-11
- **Ships in:** `@mosaicstack/mosaic` 0.0.30
## Objective
Ensure `~/.config/mosaic/TOOLS.md` is created on every supported install path so the mandatory AGENTS.md load order actually resolves. The load order lists `TOOLS.md` at position 5 but the bash installer never seeds it.
## Root cause
`packages/mosaic/framework/install.sh:228-236` — the post-sync "Seed defaults" loop explicitly lists `AGENTS.md STANDARDS.md`:
```bash
DEFAULTS_DIR="$TARGET_DIR/defaults"
if [[ -d "$DEFAULTS_DIR" ]]; then
for default_file in AGENTS.md STANDARDS.md; do # ← missing TOOLS.md
if [[ -f "$DEFAULTS_DIR/$default_file" ]] && [[ ! -f "$TARGET_DIR/$default_file" ]]; then
cp "$DEFAULTS_DIR/$default_file" "$TARGET_DIR/$default_file"
ok "Seeded $default_file from defaults"
fi
done
fi
```
`TOOLS.md` is listed in `PRESERVE_PATHS` (line 24) but never created in the first place. A fresh bootstrap install via `tools/install.sh → framework/install.sh` leaves `~/.config/mosaic/TOOLS.md` absent, and the agent load order then points at a missing file.
### Secondary: TypeScript `syncFramework` is too greedy
`packages/mosaic/src/config/file-adapter.ts:133-160``FileConfigAdapter.syncFramework` correctly seeds TOOLS.md, but it does so by iterating _every_ file in `framework/defaults/`:
```ts
for (const entry of readdirSync(defaultsDir)) {
const dest = join(this.mosaicHome, entry);
if (!existsSync(dest)) {
copyFileSync(join(defaultsDir, entry), dest);
}
}
```
`framework/defaults/` contains:
```
AGENTS.md
AUDIT-2026-02-17-framework-consistency.md
README.md
SOUL.md ← hardcoded "Jarvis"
STANDARDS.md
TOOLS.md
USER.md
```
So on a fresh install the TS wizard would silently copy the `Jarvis`-flavored `SOUL.md` + placeholder `USER.md` + internal `AUDIT-*.md` and `README.md` into the user's mosaic home before `mosaic init` ever prompts them. That's a latent identity bug as well as a root-clutter bug — the wizard's own stages are responsible for generating `SOUL.md`/`USER.md` via templates.
### Tertiary: stale `TOOLS.md.template`
`packages/mosaic/framework/templates/TOOLS.md.template` still references `~/.config/mosaic/rails/git/…` and `~/.config/mosaic/rails/codex/…`. The `rails/` tree was renamed to `tools/` in the v1→v2 migration (see `run_migrations` in `install.sh`, which removes the old `rails/` symlink). Any user who does run `mosaic init` ends up with a `TOOLS.md` that points to paths that no longer exist.
## Scope of this fix
1. **`packages/mosaic/framework/install.sh`** — extend the explicit seed list to include `TOOLS.md`.
2. **`packages/mosaic/src/config/file-adapter.ts`** — restrict `syncFramework` defaults-seeding to an explicit whitelist (`AGENTS.md`, `STANDARDS.md`, `TOOLS.md`) so the TS wizard never accidentally seeds `SOUL.md`/`USER.md`/`README.md`/`AUDIT-*.md` into the mosaic home.
3. **`packages/mosaic/framework/templates/TOOLS.md.template`** — replace `rails/` with `tools/` in the wrapper-path examples (minimal surgical fix; full template modernization is out of scope for a 0.0.30 hotfix).
4. **Regression test** — unit test around `FileConfigAdapter.syncFramework` that runs against a tmpdir fixture asserting:
- `TOOLS.md` is seeded when absent
- `AGENTS.md` / `STANDARDS.md` are still seeded when absent
- `SOUL.md` / `USER.md` are **not** seeded from `defaults/` (the wizard stages own those)
- Existing root files are not clobbered.
Out of scope (tracked separately / future work):
- Regenerating `defaults/SOUL.md` and `defaults/USER.md` so they no longer contain Jarvis-specific content.
- Fully modernizing `TOOLS.md.template` to match the rich canonical `defaults/TOOLS.md` reference.
- `issue-create.sh` / `pr-create.sh` `eval` bugs (already captured to OpenBrain from the prior hotfix).
## Plan / checklist
- [ ] Branch `fix/tools-md-seeding` from `main` (at `b2cbf89`)
- [ ] File Gitea issue (direct API; wrappers broken for bodies with backticks)
- [ ] Scratchpad created (this file)
- [ ] `install.sh` seed loop extended to `AGENTS.md STANDARDS.md TOOLS.md`
- [ ] `file-adapter.ts` seeding restricted to explicit whitelist
- [ ] `TOOLS.md.template` `rails/``tools/`
- [ ] Regression test added (`file-adapter.test.ts`) — failing first, then green
- [ ] `pnpm --filter @mosaicstack/mosaic run typecheck` green
- [ ] `pnpm --filter @mosaicstack/mosaic run lint` green
- [ ] `pnpm --filter @mosaicstack/mosaic exec vitest run` — new test green, no new failures beyond the known pre-existing `uninstall.spec.ts:138`
- [ ] Repo baselines: `pnpm typecheck` / `pnpm lint` / `pnpm format:check`
- [ ] Independent code review (`feature-dev:code-reviewer`, sonnet tier)
- [ ] Commit + push
- [ ] PR opened via Gitea API
- [ ] CI queue guard cleared (bypass local `ci-queue-wait.sh` if stale origin URL breaks it; query Gitea API directly)
- [ ] CI green on PR
- [ ] PR merged (squash)
- [ ] CI green on main
- [ ] Issue closed with link to merge commit
- [ ] `chore/release-mosaic-0.0.30` branch bumps `packages/mosaic/package.json` 0.0.29 → 0.0.30
- [ ] Release PR opened + merged
- [ ] `.woodpecker/publish.yml` auto-publishes to Gitea npm registry
- [ ] Publish verified (`npm view @mosaicstack/mosaic version` or registry check)
## Risks / blockers
- `ci-queue-wait.sh` wrapper may still crash on stale `origin` URL (captured in OpenBrain from prior hotfix). Workaround: query Gitea API directly for running/queued pipelines.
- `issue-create.sh` / `pr-create.sh` `eval` bugs. Workaround: Gitea API direct call.
- `uninstall.spec.ts:138` is a pre-existing failure on main; not this change's problem.
- Publish flow is fire-and-forget on main push — if `publish.yml` fails, rollback means republishing a follow-up patch, not reverting the version bump.