Files
stack/docs/design/framework-constitution/debate/position-coder.md
Jason Woltje c70b217a5c
Some checks failed
ci/woodpecker/push/ci Pipeline failed
docs(design): mosaic framework constitution — expert conference output
Conference of 7 experts (architect/moonshot/contrarian/coder/aiml/devex/steward)
debated layering, sanitization, upgrade-safety, cross-harness robustness.
Artifacts: BRIEF, 7 positions, 7 rebuttals, synthesis-v1, 3 red-team passes,
canonical DESIGN.md, OPEN-QUESTIONS.md, MISSION.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:47:49 -05:00

311 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 | `<repo>/AGENTS.md` | Yes |
**Layer 0 — Constitution** owns everything that must be identical for all users: the hard gates in `defaults/AGENTS.md` (lines 2356), the steered-autonomy escalation triggers (lines 7288), the mode declaration protocol (lines 5968), subagent tier rules (lines 112121), and the session-closure checklist (lines 148155). 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 160202) 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 1020 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 2337) — must be resident; violating these is catastrophic and silent.
2. Mode declaration (lines 5968) — must be resident; it's the first response.
3. Block vs. Done distinction (lines 8088) — must be resident; determines whether agents stop prematurely.
4. Escalation triggers (lines 7279) — 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 112121; 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 2337) 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.