# Position Paper — Pragmatic Coder Lens ## Mosaic Framework Constitution: Layering, Sanitization, Upgrade Safety, Cross-Harness Robustness, Minimalism **Author role:** Pragmatic Coder — cares about implementability, migration cost, and what a maintainer can actually keep working across releases. --- ## Abstract The Mosaic framework has the bones of a sound design but is held back by three entangled problems: personal data baked into shipped defaults, no machine-enforceable boundary between what the framework owns versus what the user owns, and a context-injection budget that is burning down faster than the delivery contract earns back in compliance value. The fixes are mechanical, not philosophical. This paper proposes a four-layer model with a strict ownership contract, a file-naming convention that `rsync --exclude` can enforce, a minimal "always-resident" Constitution that fits comfortably in a shared context window, and a harness-adapter pattern that keeps cross-harness robustness honest without duplicating law. --- ## DQ1 — Layering: Four Layers, Strict Ownership ### The problem with the current two-and-a-half layers Reading `defaults/AGENTS.md`, `defaults/SOUL.md`, `defaults/USER.md`, and `templates/SOUL.md.template` together reveals an informal split that already exists but is not named or enforced. The installer's `PRESERVE_PATHS` array in `install.sh` line 24 is the only machine-enforced boundary, and it conflates three distinct concerns: `SOUL.md` (persona), `USER.md` (operator profile), and `STANDARDS.md` (framework rules). All three land in `~/.config/mosaic/` with no naming convention to tell them apart. Nothing stops `mosaic upgrade` from silently clobbering user edits in a file that accidentally got removed from `PRESERVE_PATHS`. ### Proposed four-layer model | Layer | Name | Owner | Deployed path | User-editable? | |---|---|---|---|---| | 0 | **Constitution** | Framework | `~/.config/mosaic/constitution/` | No | | 1 | **Persona** (Soul) | User (init-generated) | `~/.config/mosaic/SOUL.md` | Yes | | 2 | **Profile** (User) | User (init-generated) | `~/.config/mosaic/USER.md` | Yes | | 3 | **Project** | Repo | `/AGENTS.md` | Yes | **Layer 0 — Constitution** owns everything that must be identical for all users: the hard gates in `defaults/AGENTS.md` (lines 23–56), the steered-autonomy escalation triggers (lines 72–88), the mode declaration protocol (lines 59–68), subagent tier rules (lines 112–121), and the session-closure checklist (lines 148–155). It ships read-only in a dedicated `constitution/` subdirectory. The upgrade path is trivially safe: `rsync --delete` the entire directory on every upgrade because no user edits live there. **Layer 1 — Persona (SOUL)** is the agent's name, tone, communication style, and guardrails that a user may customize. It is generated by `mosaic init` from `templates/SOUL.md.template` and never overwritten by upgrade. The current `defaults/SOUL.md` hardcodes "Jarvis" and "PDA-friendly" — both must move to template tokens. **Layer 2 — Profile (USER)** is the operator's name, timezone, accessibility needs, projects. Same init-generated pattern; `defaults/USER.md` already has the right shape (it's the placeholder version — the problem is the operator-specific content in `defaults/SOUL.md`). **Layer 3 — Project** stays exactly as today: `AGENTS.md` per repo, with the project-local template in `templates/agent/AGENTS.md.template`. ### Precedence rule (explicit, not implicit) When a Constitution rule conflicts with a Persona or Profile preference, Constitution wins. When a Project rule conflicts with Persona or Profile (not Constitution), Project wins for that repo. No exceptions. The SOUL.md template can reference this explicitly: "Communication style preferences apply unless they conflict with Constitution layer hard gates." ### What changes in the file tree ``` packages/mosaic/framework/ constitution/ # NEW — Layer 0, framework-owned CORE.md # Delivery contract, hard gates, escalation triggers GUIDES.md # Conditional guide loading table SUBAGENT.md # Model tier rules CLOSURE.md # Session closure checklist defaults/ AGENTS.md # KEEP but shrink: now just load-order + pointer to constitution/ SOUL.md # DELETE (contaminated) — move to templates only USER.md # KEEP (already scrubbed to placeholder) STANDARDS.md # KEEP (machine-specific, not personal) TOOLS.md # KEEP templates/ SOUL.md.template # Already exists, needs {{PDA_PREFS}} removed USER.md.template # Already exists ``` The deployed layout at `~/.config/mosaic/`: ``` ~/.config/mosaic/ constitution/ # rsync --delete on every upgrade (no user edits) AGENTS.md # Thin dispatcher: read constitution/, then SOUL, USER, runtime SOUL.md # Init-generated, upgrade-preserved USER.md # Init-generated, upgrade-preserved guides/ # On-demand depth (unchanged) runtime/ # Harness-specific (unchanged) ``` The installer's `PRESERVE_PATHS` shrinks to: `SOUL.md USER.md TOOLS.md memory sources credentials`. `constitution/` is explicitly excluded from preservation — it is always overwritten. --- ## DQ2 — Sanitization: Template-then-Init, Zero Fallback Personal Data ### The contamination is surgical, not structural `git grep -i 'jarvis\|jason\|woltje\|pda' packages/mosaic/framework/` will hit: - `defaults/SOUL.md` lines 8, 25 (hardcoded name + PDA) - `runtime/claude/settings-overlays/jarvis-loop.json` (project name + persona) - `defaults/AUDIT-2026-02-17-framework-consistency.md` (audit doc with personal refs) That is approximately three files plus any stray refs in guides. The problem is not pervasive across the whole framework — it is concentrated and surgical to fix. ### What ships vs. what is generated **Ships in the package (no personal data, no placeholder artifacts):** - `constitution/` — pure framework law - `defaults/AGENTS.md` — thin load-order dispatcher, no identity - `defaults/USER.md` — already scrubbed to "(not configured)" placeholders - `defaults/STANDARDS.md`, `defaults/TOOLS.md` — machine-level, not personal - `templates/SOUL.md.template` — tokens only, no "Jarvis", no "PDA" - `templates/USER.md.template` — tokens only - All guides — already clean (spot-check `guides/E2E-DELIVERY.md`, `guides/ORCHESTRATOR.md`: no personal refs) - All runtime files except `settings-overlays/jarvis-loop.json` **Generated at init time (never shipped):** - `SOUL.md` — rendered from template with user answers - `USER.md` — rendered from template with user answers - Any user-project config **Delete or move:** - `defaults/SOUL.md` — delete from package (was a seed copy; now generated only) - `runtime/claude/settings-overlays/jarvis-loop.json` — delete or generalize to an example overlay with no personal names - `defaults/AUDIT-2026-02-17-framework-consistency.md` — move to `docs/` or delete (it's a one-time audit document, not framework content) ### Out-of-box experience without personal defaults The concern about "blank defaults" degrading out-of-box experience is real but solvable. The installer already runs `mosaic init` after `install.sh` — the init wizard generates SOUL.md and USER.md immediately. If init is skipped (non-interactive CI installs), the thin `AGENTS.md` still functions because it only needs `constitution/` to enforce hard gates. SOUL.md absence means no agent persona customization, which is an acceptable degraded state — not a broken state. Add a one-line warning in `AGENTS.md`: "SOUL.md not found — agent will use default identity. Run `mosaic init` to configure." --- ## DQ3 — Customization and Upgrade Safety: File Ownership as the Enforcement Mechanism ### The current PRESERVE_PATHS approach is correct but incomplete `install.sh` line 24 already implements the right idea: `PRESERVE_PATHS=("AGENTS.md" "SOUL.md" "USER.md" "TOOLS.md" "STANDARDS.md" "memory" "sources" "credentials")`. The problem is that `AGENTS.md` and `STANDARDS.md` are framework-owned files that should be freely overwritten on upgrade, but they are listed alongside user-owned files that must never be overwritten. This conflation is the root of the drift problem. ### Fix: Directory ownership, not file-by-file exclusion Replace the mixed per-file `PRESERVE_PATHS` with directory-level ownership: ```bash # Framework-owned directories — always overwritten on upgrade FRAMEWORK_DIRS=("constitution" "guides" "runtime" "templates" "tools" "profiles" "adapters") # User-owned files — never overwritten PRESERVE_PATHS=("SOUL.md" "USER.md" "TOOLS.md" "memory" "sources" "credentials") # Thin dispatchers — seeded on first install, never overwritten thereafter SEED_ONCE=("AGENTS.md" "STANDARDS.md") ``` The rsync command becomes: ```bash rsync -a --delete \ $(for d in "${FRAMEWORK_DIRS[@]}"; do echo "--include=$d/***"; done) \ --exclude="*" \ "$SOURCE_DIR/" "$TARGET_DIR/" ``` This gives the framework a clean ownership contract: everything in `constitution/` is always current; user files in `~/.config/mosaic/` root are always preserved. ### Upgrade-safe customization for users who need to extend guides Some power users will want to extend guides (e.g., add a custom section to `guides/E2E-DELIVERY.md`). The right pattern is user-overlay files, not editing the originals: ``` ~/.config/mosaic/ guides/ E2E-DELIVERY.md # Framework-owned, always overwritten E2E-DELIVERY.local.md # User-owned, never touched by upgrade ``` `AGENTS.md` load-order instructions reference `.local.md` variants: "After loading any guide, check for a `.local.md` variant and merge-read it." This is opt-in and requires no framework change to `constitution/` — just a convention documented in `defaults/AGENTS.md`. ### Version migration The existing `FRAMEWORK_VERSION` variable in `install.sh` line 28 and `run_migrations()` function (lines 160–202) are the right mechanism. Migration v3 should: 1. Move any user-edited content from the old `defaults/SOUL.md` into `SOUL.md` at the root (if SOUL.md does not already exist). 2. Delete `defaults/SOUL.md`. 3. Warn if `defaults/AGENTS.md` has user edits (checksum diff) and offer to merge. This is a concrete, implementable migration — not a "review manually" hand-wave. --- ## DQ4 — Cross-Harness Robustness: Single Law File, Adapter-Only Injection Mechanics ### The current adapter pattern is structurally correct but hollow Looking at `adapters/claude.md`, `adapters/codex.md`, `adapters/pi.md`, `adapters/generic.md`: each adapter is 10–20 lines and correctly says "load STANDARDS.md and project AGENTS.md." The `runtime/{claude,codex,pi,opencode}/RUNTIME.md` files add harness-specific mechanics (settings paths, model tier syntax, MCP config locations). This split is right. The problem is that the Constitution content currently lives in `defaults/AGENTS.md` which is also the load-order dispatcher — if a harness injects a slightly different path, the whole contract is at risk. ### Fix: Constitution as a stand-alone file that adapters reference, not duplicate The proposed `constitution/CORE.md` (from DQ1) must be the single file that all harnesses reference identically. Adapter files should contain exactly one line regarding the constitution: "Load `~/.config/mosaic/constitution/CORE.md` — this is the immutable law." Current per-harness RUNTIME.md files contain no contradictions with AGENTS.md, which is good. They add harness-specific syntax (e.g., Claude's Task tool `model` parameter, `install.sh` line for Pi's `--append-system-prompt`). That pattern should be preserved as-is. What must change is that RUNTIME.md files must not re-state or paraphrase Constitution rules — they must simply reference `constitution/CORE.md`. If a rule needs harness-specific elaboration, it goes in RUNTIME.md as an addendum, not a restatement. Restatements drift; references cannot. ### Cross-harness enforcement checklist (concrete) For each harness adapter, validate: 1. Does injection reach `constitution/CORE.md`? (Yes if `AGENTS.md` loads it and AGENTS.md is injected.) 2. Does the RUNTIME.md contain any rule that contradicts CORE.md? (Audit: `grep` for escalation triggers, hard gate paraphrases — if found, delete and replace with reference.) 3. Does the harness have a native equivalent for sequential-thinking MCP? (Pi: yes, native thinking levels. Claude/Codex/OpenCode: MCP required. This is already documented in `runtime/pi/RUNTIME.md` line 61 — keep it.) The Pi adapter `runtime/pi/RUNTIME.md` is the most complete and honest — it explicitly documents where Pi differs from other runtimes (no permission restrictions, native thinking, model-agnostic). The other RUNTIME.md files are thinner. That's fine; they should stay thin. Thin adapters with a single Constitution reference are more maintainable than thick adapters that duplicate law. ### What to do about harness-specific settings (jarvis-loop.json) `runtime/claude/settings-overlays/jarvis-loop.json` contains personal project names ("jarvis", "~/src/jarvis") and persona-specific presets. This file must not ship. Replace it with a generic example: ``` runtime/claude/settings-overlays/ example-project-overlay.json # Generic example with {{PROJECT_NAME}} tokens README.md # Explains how to create user-local overlays ``` User-local overlays live outside the package (e.g., `~/.config/mosaic/runtime/claude/settings-overlays/my-project.json`) and are never overwritten by upgrade. --- ## DQ5 — Minimalism vs. Completeness: Token Budget is a Real Constraint ### The current "thin core" claim is not thin `defaults/AGENTS.md` is 155 lines and is described as "THE source of truth" in `defaults/README.md`. Add `defaults/SOUL.md` (54 lines), `defaults/USER.md` (~37 lines), and the required-at-session-start `guides/E2E-DELIVERY.md` (which is much longer), and you are burning a meaningful fraction of a shared context window on framework overhead before any project-specific content loads. The brief calls this out: the contract is "large and partly duplicated." Looking at both files, `guides/E2E-DELIVERY.md` and `defaults/AGENTS.md` repeat the mode declaration protocol, escalation triggers, and execution cycle. That is direct duplication — agents reading both files (as instructed) see the same rules twice. ### Concrete split: what is truly always-resident The always-resident Constitution (`constitution/CORE.md`) should contain only rules that an agent absolutely cannot violate without reading them first: 1. Hard gates (the 13 bullets, `AGENTS.md` lines 23–37) — must be resident; violating these is catastrophic and silent. 2. Mode declaration (lines 59–68) — must be resident; it's the first response. 3. Block vs. Done distinction (lines 80–88) — must be resident; determines whether agents stop prematurely. 4. Escalation triggers (lines 72–79) — must be resident; determines when to interrupt humans. 5. Sequential-thinking requirement (line 143) — must be resident; it's a session-start prerequisite. Everything else is on-demand: - Execution cycle details → `guides/E2E-DELIVERY.md` (already there, load on implementation tasks) - Subagent tier selection → `guides/SUBAGENT.md` (new file, extracted from AGENTS.md lines 112–121; load when spawning workers) - Conditional guide table → remain in `AGENTS.md` as a compact lookup table (it's a table, not prose; low token cost) - Session closure checklist → `guides/E2E-DELIVERY.md` (already there) The result: `constitution/CORE.md` targets ~80 lines. `AGENTS.md` shrinks to ~40 lines (load order + guide table + pointer to constitution). Total always-resident budget: ~120 lines vs. the current ~155 in AGENTS.md alone before guides load. ### Deduplication: delete from E2E-DELIVERY.md, not from AGENTS.md `guides/E2E-DELIVERY.md` currently re-states mode declaration and escalation triggers. When these move to `constitution/CORE.md`, delete them from E2E-DELIVERY.md — not from both. The guide can reference: "Mode declaration and escalation triggers are in `constitution/CORE.md` (already resident — do not re-read)." This removes duplication without creating a hole. ### Against further minimalism There is a real risk of over-minimizing: removing rules from the always-resident context to save tokens, then watching agents violate them because they never loaded the relevant guide. The hard gates in particular (`AGENTS.md` lines 23–37) have a known failure mode: agents skip them when they are on-demand. The existing decision to keep them always-resident is correct. Do not move them to on-demand guides. Token cost of 30 lines of hard-gate text is worth paying at every session. --- ## Concrete File Layout Recommendation (Alpha) ``` packages/mosaic/framework/ constitution/ CORE.md # ~80 lines: hard gates, mode declaration, block/done, escalation, seq-thinking req GUIDES.md # Conditional guide loading table (extracted from AGENTS.md) SUBAGENT.md # Model tier rules (extracted from AGENTS.md) defaults/ AGENTS.md # ~40 lines: load order + pointer to constitution/ + guide table ref USER.md # Scrubbed placeholder (already done) STANDARDS.md # Keep as-is TOOLS.md # Keep as-is # SOUL.md — DELETED (generated by init only) # AUDIT-2026-02-17-*.md — DELETED (stale audit doc) templates/ SOUL.md.template # Remove {{PDA_PREFS}} and hardcoded "Jarvis" USER.md.template # Already clean TOOLS.md.template # Already exists agent/ # Keep as-is runtime/ claude/ RUNTIME.md # Add: "Load constitution/CORE.md — law is there, not here" settings-overlays/ # jarvis-loop.json — DELETED example-project-overlay.json # Generic, token-substituted example codex/RUNTIME.md # Same constitution reference addition pi/RUNTIME.md # Same opencode/RUNTIME.md # Same adapters/ claude.md # Add constitution reference; keep thin codex.md # Same pi.md # Same generic.md # Same install.sh # Rewrite PRESERVE_PATHS → FRAMEWORK_DIRS + PRESERVE_PATHS split # Add migration v3: move defaults/SOUL.md → SOUL.md if user-edited ``` --- ## Migration Path (Alpha → Existing Installs) Do not break existing deployments. The migration is: 1. `install.sh` v3 migration: detect old `defaults/SOUL.md` with user edits (MD5 diff vs. shipped `defaults/SOUL.md` at install time). If edited, copy to `~/.config/mosaic/SOUL.md` if that file does not already exist. Warn the user. 2. Move Constitution content from `AGENTS.md` into `constitution/CORE.md`. Update `AGENTS.md` to reference it. Agents that load AGENTS.md still get the full law — they just get it via one more file read. 3. The `~/.claude/CLAUDE.md` thin pointer (`mosaic/runtime/claude/CLAUDE.md`) already says "read `~/.config/mosaic/AGENTS.md`" — no change needed there. 4. Ship `constitution/` as a new directory. Existing installs get it on next upgrade. Existing `AGENTS.md` that is preserved (it's in SEED_ONCE) still works until the user runs `mosaic upgrade` — at that point the new AGENTS.md is seeded and the constitution directory appears. Migration cost for existing users: one `mosaic upgrade`. No manual steps. No data loss. --- ## Biggest Risk **The load-order indirection chain breaks silently across harnesses.** The current chain is: harness injects AGENTS.md → AGENTS.md says "read SOUL.md" → agent reads it. With the proposed change: harness injects AGENTS.md → AGENTS.md says "constitution/ is already resident (I was injected with it)" — but was it? If `mosaic claude` composes a `--append-system-prompt` that includes AGENTS.md but not `constitution/CORE.md`, the hard gates are silently absent. This is not a hypothetical: `defaults/README.md` line 126 shows that `mosaic claude` uses `--append-system-prompt "with composed runtime contract"` but the composition logic is in the npm CLI (`packages/mosaic/src/`), not visible in the framework files. If the composer does not include `constitution/CORE.md` when composing, the law disappears from context with no error. **Mitigation:** `AGENTS.md` must say "if `constitution/CORE.md` is not already in context, read it now" — making the Constitution self-bootstrapping, not injection-dependent. This is the same defensive pattern the current AGENTS.md uses for SOUL.md (line 11: "Read `~/.config/mosaic/SOUL.md`"). The Constitution must not rely on the launcher getting the injection order right; it must be a file the agent is instructed to read regardless. --- ## Single Strongest Recommendation **Extract the hard gates into `constitution/CORE.md` and instruct agents to self-load it from `AGENTS.md` — do not rely on the launcher to inject it.** This one change makes the Constitution harness-agnostic by construction, eliminates the injection-order race, and gives you a clean file to upgrade without touching user-customized content. Every other improvement (sanitization, template generation, upgrade-safe overlays) is valuable but secondary. The Constitution's enforceability depends on agents reliably reading it — make that a file-read instruction, not a launcher implementation detail.