fix(mosaic): seed TOOLS.md from defaults on install (#458)
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #458.
This commit is contained in:
110
docs/scratchpads/tools-md-seeding-20260411.md
Normal file
110
docs/scratchpads/tools-md-seeding-20260411.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user