docs(design): mosaic framework constitution — expert conference output
Some checks failed
ci/woodpecker/push/ci Pipeline failed
Some checks failed
ci/woodpecker/push/ci Pipeline failed
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>
This commit is contained in:
310
docs/design/framework-constitution/debate/position-coder.md
Normal file
310
docs/design/framework-constitution/debate/position-coder.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# 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 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.
|
||||
Reference in New Issue
Block a user