feat(fleet): update-surviving persona customization (H4) #661

Merged
jason.woltje merged 1 commits from feat/h4-persona-overrides into main 2026-06-24 16:21:01 +00:00
Owner

What

Implements North Star H4 — update-surviving persona customization: an override layer for personas that survives mosaic update, a baseline⊕override resolver, and a mosaic fleet persona CLI.

Override layer location & merge semantics

  • Baseline: <mosaicHome>/fleet/roles/*.md — reseeded on every mosaic update (delivers new baseline personas).
  • Override: <mosaicHome>/fleet/roles.local/*.md — PRESERVE-protected; wins over baseline of the same class and may add entirely new classes with no baseline.
  • Resolver (packages/mosaic/src/commands/fleet-personas.ts):
    • resolvePersona(klass) — override file if roles.local/ defines the class (match by inline `class:` marker, filename fallback), else baseline; null if neither.
    • listPersonaClasses() — UNION of baseline + override classes.
    • personaStatus() — classifies each class as baseline / overridden / custom.

DRY with #660

The inline-class:/LIBRARY/filename extraction is now a single shared helper extractClassesFromDir() in fleet-personas.ts. fleet-profiles.ts imports it; its old listPersonaClasses(rolesDir) is a thin delegate. Profile roster validation now resolves against the override-aware union, so a profile can reference a user-customized or user-added persona.

install.sh — AC-NS-7 guarantee

Added fleet/roles.local to PRESERVE_PATHS:

PRESERVE_PATHS=(... "fleet/backlog" "fleet/roles.local")

Baseline fleet/roles/ continues to reseed normally; fleet/roles.local/ is excluded from rsync --delete, so user overrides survive update.

CLI — mosaic fleet persona <list|show|customize>

  • persona list [--json] — every class with status + domain.
  • persona show <class> [--json] — the RESOLVED persona and which layer it came from.
  • persona customize <class> — copy baseline into roles.local/ (no-clobber if present); --new scaffolds a brand-new persona; auto-creates roles.local/.

Tests (vitest)

fleet-personas.spec.ts: override-wins, custom-add, status classification, AC-NS-7 update-survival simulation (re-seed baseline roles/ while leaving roles.local/ untouched → override still wins, custom class still resolves), and fleet-profiles validation accepting an override-only persona. All 594 mosaic tests pass; typecheck + lint clean.

🤖 Generated with Claude Code

## What Implements North Star **H4 — update-surviving persona customization**: an override layer for personas that survives `mosaic update`, a baseline⊕override resolver, and a `mosaic fleet persona` CLI. ## Override layer location & merge semantics - **Baseline:** `<mosaicHome>/fleet/roles/*.md` — reseeded on every `mosaic update` (delivers new baseline personas). - **Override:** `<mosaicHome>/fleet/roles.local/*.md` — PRESERVE-protected; **wins** over baseline of the same class and may **add** entirely new classes with no baseline. - Resolver (`packages/mosaic/src/commands/fleet-personas.ts`): - `resolvePersona(klass)` — override file if `roles.local/` defines the class (match by inline `` `class:` `` marker, filename fallback), else baseline; `null` if neither. - `listPersonaClasses()` — UNION of baseline + override classes. - `personaStatus()` — classifies each class as `baseline` / `overridden` / `custom`. ## DRY with #660 The inline-`class:`/LIBRARY/filename extraction is now a single shared helper `extractClassesFromDir()` in `fleet-personas.ts`. `fleet-profiles.ts` imports it; its old `listPersonaClasses(rolesDir)` is a thin delegate. Profile roster validation now resolves against the override-aware union, so a profile can reference a user-customized or user-added persona. ## install.sh — AC-NS-7 guarantee Added `fleet/roles.local` to `PRESERVE_PATHS`: ``` PRESERVE_PATHS=(... "fleet/backlog" "fleet/roles.local") ``` Baseline `fleet/roles/` continues to reseed normally; `fleet/roles.local/` is excluded from rsync `--delete`, so user overrides survive update. ## CLI — `mosaic fleet persona <list|show|customize>` - `persona list [--json]` — every class with status + domain. - `persona show <class> [--json]` — the RESOLVED persona and which layer it came from. - `persona customize <class>` — copy baseline into `roles.local/` (no-clobber if present); `--new` scaffolds a brand-new persona; auto-creates `roles.local/`. ## Tests (vitest) `fleet-personas.spec.ts`: override-wins, custom-add, status classification, **AC-NS-7 update-survival simulation** (re-seed baseline roles/ while leaving roles.local/ untouched → override still wins, custom class still resolves), and fleet-profiles validation accepting an override-only persona. All 594 mosaic tests pass; typecheck + lint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jason.woltje added 1 commit 2026-06-24 16:12:03 +00:00
feat(fleet): update-surviving persona overrides (roles.local layer, resolver, persona CLI) (H4)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
92e4ee189a
Add a PRESERVE-protected persona override layer at <mosaicHome>/fleet/roles.local/
that survives `mosaic update` while baseline fleet/roles/ keeps reseeding.

- fleet-personas.ts: shared class-extraction (single source of truth, DRY with
  fleet-profiles.ts), resolvePersona (override wins, baseline fallback),
  listPersonaClasses (baseline ⊕ override union), personaStatus
  (baseline/overridden/custom), and the `fleet persona list|show|customize` CLI.
- fleet-profiles.ts: roster validation now uses the override-aware union so a
  profile can reference a user-customized or user-ADDED persona; the old
  listPersonaClasses(rolesDir) is kept as a thin delegate to the shared helper.
- install.sh: add fleet/roles.local to PRESERVE_PATHS (AC-NS-7 guarantee).
- specs: override-wins, custom-add, status classification, AC-NS-7
  update-survival simulation, and profile-validation-accepts-custom-persona.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jason.woltje merged commit 84d2757817 into main 2026-06-24 16:21:01 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#661