Files
stack/docs/design/framework-constitution/DESIGN.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

36 KiB
Raw Blame History

Mosaic Framework Constitution — Canonical Design (Alpha)

Status: CANONICAL. This is the single design of record for the alpha. It supersedes synthesis-v1.md where they differ. It integrates synthesis-v1.md and the three red-team passes (debate/redteam-contrarian.md, debate/redteam-devex.md, debate/redteam-steward.md), each finding either mitigated here or explicitly accepted with rationale (§9). A PRD derives from this document; implementation derives from the PRD.

Scope: DQ1DQ5 of BRIEF.md, plus the non-DQ release blockers (LICENSE, hardcoded credential path) the debate surfaced. Every claim is grounded in the real tree at packages/mosaic/framework/ and packages/mosaic/src/; paths and line numbers were re-verified against the working copy, not trusted from the prior papers.


0. What changed vs synthesis-v1

The synthesis layer model and "subtraction not addition" doctrine survive the red team intact and are adopted wholesale. What the red team broke — and this document fixes — is the seam between the spec and the mechanisms it assumed already existed. Three facts re-verified here change the plan:

  1. The resident contract is the root file ~/.config/mosaic/AGENTS.md, seeded once and never re-seeded. launch.ts:326 reads root AGENTS.md; install.sh:236 seeds it only when absent ([[ ! -f ... ]]); file-adapter.ts:187 (if (existsSync(dest)) continue) does the same in the npm path. Removing files from PRESERVE_PATHS does NOT update them — it only stops preserving a file the seed loop then declines to recreate. The synthesis's headline drift fix is mechanically wrong (contrarian R1, steward RISK-04). Fixed in §3/§5.
  2. mosaic <harness> already self-heals a missing SOUL.md via checkSoul() (launch.ts:55-68): it runs the setup wizard, so deleting defaults/SOUL.md does not brick a mosaic-launched session. The real hole is (a) bare launches that bypass mosaic, and (b) the wizard hanging on a non-TTY host (devex B1, contrarian R4). Fixed in §3/§4.
  3. The contamination is broader than synthesis-v1 enumerated — re-grep finds the private credential path in three scripts (incl. tools/health/stack-health.sh:23), a private domain brain.woltje.com in the shipped prevent-memory-write.sh hook, and operator tokens across tools/, guides/, and the init generator's default role string — none of which the synthesis fix list or the proposed grep scope covered (devex B3, steward RISK-01/03). Fixed in §6.

There are two dual implementations of the upgrade logic (install.sh bash + file-adapter.ts npm), kept in sync only by a comment (file-adapter.ts:148). Every mechanism change in this document is specified as "in both installers, proven by one shared fixture suite" (contrarian R10). This is promoted to a first-class design constraint, not an afterthought.


1. Layering & Precedence (final model)

1.1 The legitimacy test

A layer boundary is legitimate iff the two sides differ in owner, upgrade-fate, OR residency. This single test (from synthesis-v1.md §1, banked by all three red teams) decides every split below and rejects gratuitous ones.

1.2 The canonical layers

Five concerns, four owned layers plus a non-resident governance spec.

# Layer Owns Owner Upgrade fate Residency Deployed path
L0 Constitution Irreducible non-negotiable law: the hard gates, escalation triggers, block-vs-done, mode declaration, the two-axis precedence rule, the "hooks are the gate" doctrine, the "no operator context in framework PRs" firewall, and the universal merge-disambiguation rule (see §1.4) Framework Overwritten wholesale every upgrade (unconditional copy, never seed-if-absent). User MUST NOT edit. Always resident, byte-budgeted ~/.config/mosaic/CONSTITUTION.md
L1 Standards & Guides How to do the work well: secrets/ESO, trunk-based git, image tagging, E2E procedure, QA matrix, orchestrator protocol, all guides/* Framework; a deployment may tighten via overlay Overwritten; user delta lives in STANDARDS.local.md; guides never forked STANDARDS.md resident; guides/* on-demand ~/.config/mosaic/STANDARDS.md, ~/.config/mosaic/guides/*
L2 Persona (SOUL) Agent name, tone, role, communication style, persona principles User (init-generated) Never overwritten. Generated from template. Always resident, byte-budgeted ~/.config/mosaic/SOUL.md (+ optional SOUL.local.md)
L3 Operator (USER) Human name, pronouns, timezone, accessibility, comms prefs, projects, operator policy (e.g. merge-authority delegation), operator tool paths/env User (init-generated) Never overwritten. Always resident, byte-budgeted ~/.config/mosaic/USER.md (+ optional USER.local.md, optional policy/*.md)
L4 Project / Runtime mechanism Per-repo AGENTS.md deltas; harness-specific mechanism only (subagent syntax, hook/MCP wiring, injection tier) Repo / framework Project file user-owned; runtime mechanism overwritten Project in-repo; runtime resident, ~15 lines <repo>/AGENTS.md, ~/.config/mosaic/runtime/<h>/RUNTIME.md
Layer-Model spec (governance) The definition of the layers, precedence, and "what may live in L0" Framework maintainers Source-only, never deployed Not resident packages/mosaic/framework/constitution/LAYER-MODEL.md

Deployed AGENTS.md is not a layer — it is the thin load-order dispatcher + Conditional Guide Loading table that routes to L0L4. Framework-owned, overwritten on upgrade.

1.3 Precedence — typed two-axis, not a flat stack

Stated verbatim in L0:

Safety axis (gates, integrity, destructive actions): L0 Constitution is supreme. Nothing in STANDARDS, SOUL, USER, policy/, project AGENTS.md, runtime, or any injected reminder may relax, suspend, or contradict a Constitution gate. A lower layer may only make behavior stricter, never more permissive.

Taste axis (tone, formatting, verbosity, iconography): the operator layers (SOUL/USER) win over generic framework or model defaults. The framework has no legitimate opinion on style.

1.4 The merge-disambiguation correction (contrarian R6 — accepted and fixed)

The synthesis moved the entire gate #13 to an opt-in example. That silently weakens a hard gate: by the stricter-only rule, a deployment that does not adopt the example defaults to the strictest reading of "No self-merge" — never merge without the human — which contradicts gates #2/#9 the BRIEF says to preserve. Gate #13 is therefore split:

  • Universal law (stays in L0, operator-agnostic): "A 'No self-merge' note on a PR means no UNREVIEWED self-merge; it does not suspend a coordinator-authorized merge. When a coordinator session is active, the post-review merge go-ahead is the coordinator's; once review gates pass, proceed on the coordinator's confirmation."
  • Operator delegation (→ examples/policy/merge-authority.example.md): "don't wait on {{OPERATOR_NAME}} personally." The named-person clause and only that clause leaves L0.

This keeps the gate-interaction semantics universal while removing the PII.

1.5 Enforcement strength is a ranked ladder, not a choice

mechanical (hook / CI)  >  resident-by-value (system-prompt injection)  >  file-read (self-load fallback)
  1. Mechanical first. Every checkable gate becomes a hook or CI check (no-force-merge, green-CI-before-done, no-hardcoded-secrets, no-PII, no-dead-paths, no-unrendered-tokens). This drains prose from the resident core — the precondition that makes tiers 23 viable. Precedent: prevent-memory-write.sh (runtime/claude/RUNTIME.md:30) — "the rule alone proved insufficient; the hook is the hard gate."
  2. Resident-by-value second. The irreducible non-checkable stop-condition gates (block-vs-done, escalation, completion-definition) injected by value at primacy, restated as a ≤5-bullet anchor at recency (bottom).
  3. File-read third (fallback). Tier-3/bare launches: unconditional read (see §1.6).

1.6 Tier-aware self-load (contrarian R9 / steward RISK-07 — accepted)

The fallback read instruction differs by tier:

  • Tier-1 (injected by value): "CONSTITUTION.md is already in your context above; do not re-read." (true, because the launcher demonstrably injected it).
  • Tier-3 (bare-launch pointer): unconditional"READ ~/.config/mosaic/CONSTITUTION.md now, before your first action." No "if not already in context" introspection — models are unreliable at judging their own window, and this is the exact drift-prone path the fallback exists to protect.

This removes the false unconditional "already in your context — do not re-read" at defaults/AGENTS.md:11 (every paper flagged it; it is still live in the tree).


2. File-by-File Move / Sanitize Plan

2a. New files

New file Content Source
defaults/CONSTITUTION.md → deploys to ~/.config/mosaic/CONSTITUTION.md L0, one flat file, ~7090 lines. The 13 hard gates with the §1.4 split applied (operator name removed, disambiguation kept); 5 escalation triggers; block-vs-done; mode-declaration; the §1.3 two-axis precedence rule verbatim; the "hooks are the gate" doctrine; the §4 "no operator context in framework PRs" firewall; the §1.6 tier-aware self-load lines; one pointer to the guide index. Gates keep full wording; procedure (wrapper paths, --purpose flags) moves to L1. L0 is authored in capability verbs — no tool-named "else stop" (see §7, devex M7). Extracted from defaults/AGENTS.md:23-87,143
constitution/LAYER-MODEL.md The §1 model + precedence + "what may live in L0" + the overlay-eligibility list (§4). Source-only, never deployed, never resident. This document
examples/personas/execution-partner.md Sanitized, placeholdered essence of the Jarvis persona — a worked example, copied on request, never auto-loaded defaults/SOUL.md (sanitized)
examples/overlays/e2e-loop.json Sanitized essence of jarvis-loop.json (~/src/<your-project> placeholders) runtime/claude/settings-overlays/jarvis-loop.json
examples/policy/merge-authority.example.md The operator delegation clause from §1.4 defaults/AGENTS.md:37
LICENSE (monorepo root) + packages/mosaic/framework/LICENSE MIT text + "license": "MIT" in package.json new (D8)
CONTRIBUTING.md (framework package) Layer model, PII/secrets prohibition, dedup rule, how to add a harness adapter, the re-contamination rule, the dual-installer parity rule, the known-limitations list (§9) new
tools/quality/scripts/verify-sanitized.sh The blocking CI gate (§6) new
.woodpecker.yml (framework package or monorepo root) Wires verify-sanitized.sh, the resident line-count check, and the composer unit test as blocking steps new (steward RISK-02 — the gate is prose until wired)

2b. Files that shrink / change role

File Change DQ
defaults/AGENTS.md Gut 155→~50-line dispatcher: load order + Conditional Guide table + tier-aware self-load. Zero restated gates. Remove the false line 11. Change seed semantics to unconditional overwrite (see §3). DQ1, DQ5
defaults/STANDARDS.md Drop "Master/slave" framing (line 5 → "Primary / satellite"); stop re-asserting L0 gates; end with the STANDARDS.local.md additive-include convention. Becomes overwrite-on-upgrade. DQ1,3,5
defaults/TOOLS.md Delete the MANDATORY jarvis-brain rule block (lines 40-44). Generic index only. DQ2
defaults/README.md:72 --name Jarvis --user-name Jason --timezone America/Chicago → placeholder names. DQ2
templates/SOUL.md.template Already clean. Keep. Ensure every {{TOKEN}} resolves to a non-empty value in init (no token survives into a resident file). DQ2
templates/agent/AGENTS.md.template and templates/agent/projects/*/{AGENTS,CLAUDE}.md.template Delete the restated Hard-Gates block. Replace with: "This project is governed by ~/.config/mosaic/CONSTITUTION.md. Add only project-specific extensions below." Fix every rails/git/tools/git/, rails/codex/tools/codex/ across BOTH AGENTS.md.template and CLAUDE.md.template families (devex m10 — synthesis named only the AGENTS family). DQ4,5
runtime/{claude,codex,pi,opencode}/RUNTIME.md Strip restated policy. Reduce to harness mechanism + one-line CONSTITUTION.md reference. Rewrite the four "sequential-thinking MCP is required / else stop" lines to capability-verb form (§7). DQ4,5
tools/_lib/credentials.sh:19, tools/git/detect-platform.sh:89, tools/health/stack-health.sh:23 ${MOSAIC_CREDENTIALS_FILE:-$HOME/src/jarvis-brain/credentials.json}${MOSAIC_CREDENTIALS_FILE:?MOSAIC_CREDENTIALS_FILE must be set} (fast-fail per STANDARDS.md:35). Document the env var in USER.md.template under ## Tool Paths. Three sites, not two (steward RISK-01, devex B3). DQ2 (blocker)
tools/qa/prevent-memory-write.sh:29 https://brain.woltje.com/v1/thoughts${OPENBRAIN_URL:?OPENBRAIN_URL must be set}/v1/thoughts. This hook prints its URL to the agent on every blocked write — a private domain in every install. DQ2 (blocker-class)
tools/_scripts/mosaic-init:277-278 Default AGENT_NAME "Assistant" + the verbatim Jarvis role string ("execution partner and visibility engine"). Fail-closed on persona in --non-interactive unless --agent-name given; replace the role default with a neutral placeholder. (devex B2 — the generator re-creates the bug verify-sanitized.sh can't see.) DQ2
tools/_scripts/mosaic-doctor:312 mosaic-jarvis skill → mosaic-agent (generic). DQ2
guides/ORCHESTRATOR.md (99,111,152), ORCHESTRATOR-LEARNINGS.md:127, ORCHESTRATOR-PROTOCOL.md:4, TOOLS-REFERENCE.md (149,182,226), BOOTSTRAP.md Replace jarvis-brain/... paths with ~/.config/mosaic/... canonical paths; remove the MANDATORY jarvis-brain rule block. (steward RISK-03 — broader than synthesis named.) DQ2

2c. Files deleted / relocated

File Action Why
defaults/SOUL.md Delete. Persona generated at init from template; mosaic self-heals via checkSoul(); bare-launch hole closed in §3. Primary contamination vector
runtime/claude/settings-overlays/jarvis-loop.json Delete → sanitized examples/overlays/e2e-loop.json Personal project map
defaults/AUDIT-2026-02-17-framework-consistency.md Move to monorepo docs/ Maintainer artifact, not agent context

3. Customization + Upgrade-Safety Mechanism

The single sentence a user can rely on: "Edit SOUL.md/USER.md and the *.local.md overlays freely — upgrades never touch them. Never edit CONSTITUTION.md/STANDARDS.md/guides/*/AGENTS.md — they update automatically every upgrade. To change framework behavior, add a .local.md overlay or a policy/ file (tighten-only)."

3.1 The seam = ownership, enforced by overwrite semantics (contrarian R1 / steward RISK-04 — the central fix)

The synthesis's "remove from PRESERVE_PATHS" is necessary but not sufficient. The seed-if-absent logic must be replaced with unconditional overwrite for the framework-owned root files, in BOTH installers:

  1. Split the seed lists by ownership. DEFAULT_SEED_FILES (file-adapter.ts:16) and the install.sh:236 seed loop are split into:
    • FRAMEWORK_OWNED = CONSTITUTION.md, AGENTS.md, STANDARDS.mdalways copied (overwrite) on every upgrade. Never in PRESERVE_PATHS.
    • USER_SEEDED = TOOLS.md (generated-then-tuned) → seed-if-absent, kept in PRESERVE_PATHS, retains .bak.<ts>-on-regenerate.
  2. SOUL.md, USER.md, *.local.md, policy/, memory, sources, credentials are the only PRESERVE_PATHS entries. AGENTS.md and STANDARDS.md are removed.
  3. Test the injected bytes, not file presence (contrarian R1). The migration fixtures assert what buildPrompt/launch.ts:325-333 composes, because testing defaults/AGENTS.md content would pass while the resident root contract stayed stale.

3.2 Additive overlays, launcher-composed (steward RISK-06 / devex M6 — build it, don't assume it)

mosaic compose-contract <harness> does not exist and is alpha-blocking, not assumed. Minimum viable spec:

  • Concatenates, in precedence order, base + .local deltas before injection, so the model gets one pre-merged blob (no redundant read-merge ritual).
  • Per-harness emission (the four harnesses are not symmetric):
    • Pi / mosaic claude / mosaic codex — append the merged blob via --append-system-prompt.
    • Codex / OpenCode — write the merged blob into the instructions file (~/.codex/instructions.md, ~/.config/opencode/AGENTS.md).
  • Bare launches that bypass mosaic get base-only overlays (the launcher never ran to compose them). This is documented loudly as a known limitation (§9), and the AGENTS.md self-load fallback emits a one-line "overlays require mosaic <harness>; run mosaic doctor" nudge.
  • Alpha scope cut (accepted): ship SOUL.local.md + USER.local.md (the two files users actually customize) and STANDARDS.local.md. Defer policy/*.md composition to v2 if build budget is tight — but the L0 merge-disambiguation rule (§1.4) means policy/ is additive delegation only, never load-bearing for a gate, so deferral is safe.

3.3 Versioning & migration

  1. One global FRAMEWORK_VERSION integer + linear migrations (existing install.sh:157-198 scaffold). No per-layer version matrix (combinatorial test cliff). Per-layer template versions survive only as a mosaic doctor advisory.
  2. Bump FRAMEWORK_VERSION 2→3. The v2→v3 migration:
    • Snapshot ~/.config/mosaic/~/.config/mosaic/.backup-v3/ first (contrarian R2 — today there is no snapshot; the cp-fallback rm -rf at install.sh:140 can lose SOUL.md/ credentials on interrupt). Implement as atomic snapshot → sync → on-failure-restore in BOTH installers; a fixture kills the process mid-sync and asserts no data loss.
    • Vendor the v2 baseline of AGENTS.md/STANDARDS.md into the migration. If the installed file differs from the v2 baseline (it was user-edited — the sanctioned customization until now), copy it to AGENTS.md.pre-constitution.bak / STANDARDS.local.md and print a one-line notice before overwriting (contrarian R2, devex M5). Never silently delete; never auto-merge (Markdown has no merge semantics — a half-resolved merge leaves <<<<<<< markers in the resident identity file). Fixture 3 asserts the delta landed in .local.md, not merely that a backup exists.
    • Install CONSTITUTION.md as a new file nothing previously owned (avoids reclassifying a user-edited flat AGENTS.md).
  3. Headless bootstrap (devex B1 / contrarian R4 — the hole checkSoul half-covers). mosaic <harness> self-heals a missing SOUL.md via checkSoul() (launch.ts:55), but the wizard hangs on a non-TTY host. Fix: install.sh runs mosaic-init --non-interactive after sync so a valid SOUL.md/USER.md always exists post-install; the wizard's non-interactive path is fail-closed on persona (devex B2) — it errors asking for --agent-name rather than silently shipping an agent named "Assistant" with the Jarvis role string.

3.4 The migration is the biggest risk — gate the alpha on a falsifiable fixture matrix

Alpha cannot tag until these pass with no interactive prompt, no hang, run against both install.sh and FileConfigAdapter.syncFramework from one shared suite (contrarian R10):

  1. Fresh install → valid resident CONSTITUTION.md+AGENTS.md+SOUL.md+USER.md exist; assert injected bytes.
  2. Legacy-flat user-edited install (MOSAIC_INSTALL_MODE=keep, the upgrade default; steward RISK-05) → law moves to CONSTITUTION.md, root AGENTS.md is overwritten with the new dispatcher, the user's old edits land in AGENTS.md.pre-constitution.bak, SOUL.md/credentials survive.
  3. User-tuned-standard install → the STANDARDS.md delta survives as STANDARDS.local.md and the framework STANDARDS.md updates.
  4. Unattended install (no TTY) → valid resident SOUL.md/USER.md exist, zero read calls, no agent named "Assistant".
  5. Interrupt-during-sync → snapshot restore leaves no data loss.

3.5 Detection without enforcement

mosaic doctor reports drift / unrendered-tokens / budget-overflow / template-version-skew as advisories (warn, never block launch). --check-constitution is opt-in diagnostic, not a gate. Accepted limitation: drift on bare launches that never invoke mosaic is undetected by doctor (devex m9) — documented in CONTRIBUTING.md; the self-load fallback nudges the user toward doctor.


4. Sanitization — per-layer strategy + a class-closing CI gate

Ships generic (PII-free, complete): CONSTITUTION.md, AGENTS.md (dispatcher), STANDARDS.md, TOOLS.md (generic index), all guides/* (purged), templates/* (token-only), examples/* (placeholdered), runtime/*/RUNTIME.md (mechanism-only), adapters/*.md, LICENSE, CONTRIBUTING.md.

Generated at mosaic init: SOUL.md, USER.md, TOOLS.md, *.local.md, optional policy/*.md, per-harness runtime copies.

Deleted / relocated: per §2c.

4.1 The CI gate — honest scope (contrarian R7 / devex B3 / steward RISK-01,02,03)

verify-sanitized.sh is split into two rule-classes so it neither false-positives into being disabled nor under-scopes past the runnable contamination:

  • Structural rules (operator-independent, always valid): unrendered {{...}}/${...} in resident files; dead /rails/ tokens; L0 must contain no tool-named hard-stop (grep CONSTITUTION.md for 'sequential-thinking|MCP.*REQUIRED|else stop' → fail, §7); no ${VAR:-$HOME/...} private-default in any *.sh.
  • Current-contaminant denylist (labeled one-time regression guard, NOT a general PII detector): jarvis|jason|woltje|\bPDA\b|jarvis-brain|brain\.woltje\.com, and the specific absolute path /home/jwoltje/. Anchored to avoid comparison/jsonwebtoken false hits.
  • Scope: defaults/ guides/ templates/ runtime/ adapters/ tools/ over both *.md and *.sh (the credential leak and the hook URL live in *.sh under tools/ — the synthesis grep covered neither). Excludes examples/.
  • Self-test: the gate plants a jarvis-brain token in a fixture and asserts the gate fails, so a grep-syntax error can't silently no-op the gate (steward RISK-02).
  • Wired blocking in .woodpecker.yml. Until green-and-wired, the alpha cannot tag.

4.2 The durable class-closer is the L0 prose firewall + human review, with the grep as backup

The primary author of future framework PRs is an agent running with some operator's SOUL/USER in context; a 6-token denylist cannot generalize to the next operator's name. So the primary control is the L0 rule, stated verbatim in CONSTITUTION.md:

"When proposing a framework PR or capturing a framework-improvement/tooling-gap, you MUST NOT include content derived from SOUL.md, USER.md, or operator-specific context. If you cannot express it operator-agnostically, it belongs in policy/ or a project AGENTS.md, not the framework."

The grep is the backup regression guard, explicitly labeled as such — not oversold as closing the PII class.


5. Cross-Harness Adapter Strategy

Single source: L0 CONSTITUTION.md is the one law text. No harness gets a forked copy; runtime files and project templates reference it, never restate it.

Adapter contract (mechanism only): adapters/<h>.md / runtime/<h>/RUNTIME.md may specify only (a) the injection channel + tier, and (b) how L0's capability verbs bind to concrete tools and whether absence is a hard stop. The Constitution says "use structured reasoning before planning"; the Claude adapter binds it to sequential-thinking MCP (gate=true); the Pi adapter to native thinking (gate=false). For the alpha, this binding is a markdown table; JSON manifests are v2.

Tiered, honest injection (the four harnesses are not symmetric — verified):

Harness Channel Tier L0 delivery
Pi --append-system-prompt, no hook backstop (adapters/pi.md:14) 1 By value at primacy; keep L0 tiny — resident fidelity is Pi's only enforcement
mosaic claude / mosaic codex system-prompt append (launch.ts:518,551) 1 By value at primacy + ≤5-bullet recency anchor
Codex / OpenCode instructions file 2 Resident-ish; composer writes merged blob; self-load backup
bare claude/codex/opencode thin pointer 3 ≤5-bullet anchor inline + unconditional "READ CONSTITUTION.md NOW"

Tier-3 anchor must be a literal L0 substring, not a paraphrase (devex M4 — accepted). You cannot forbid paraphrasing gates (D7) and then ship a 5-bullet paraphrase as the Tier-3 payload. The anchor is the exact bytes of the 5 irreducible stop-condition gate lines, so Tier-3 is a strict subset of Tier-1, never a divergent text. The composer unit test asserts byte-equality of the anchor against its L0 source lines.

Verification control — re-scoped (contrarian R3 / steward RISK-11 — accepted). The synthesis's "live-launch each harness in CI and assert effective context" is impractical (no Codex/OpenCode prompt dump; Tier-3 unassertable without reading model behavior). Replace with a composer unit test: assert buildPrompt(harness) output contains the irreducible-gate anchor for each tier, and that the Tier-3 anchor is byte-equal to its L0 source. This is real and cheap. Live-launch smoke testing is a v2 aspiration. Codex/OpenCode hook parity is a tracked gap in CONTRIBUTING.md's compliance matrix, not something the alpha closes.

sequential-thinking contradiction (devex M7 — accepted). It lives in four RUNTIME files (runtime/{claude,codex,opencode}/RUNTIME.md:3 say "required"; runtime/pi/RUNTIME.md:61 says "not gated"). All four are rewritten in the same PR to capability-verb form; L0 carries no tool-named "else stop"; the structural CI rule (§4.1) enforces it; a fixture asserts a bare pi launch does not emit a sequential-thinking halt.


6. Phased Implementation Plan (alpha — ordered, each phase independently shippable)

Each phase is a self-contained, CI-green PR. Order is dependency-driven: legal/safety first, then the extraction the rest depends on, then mechanism, then cross-harness, then the gate that locks it.

  • Add MIT LICENSE (root + framework) + "license": "MIT" in package.json.
  • Fix the credential path in all three *.sh sites → ${MOSAIC_CREDENTIALS_FILE:?...}.
  • Fix brain.woltje.com in prevent-memory-write.sh${OPENBRAIN_URL:?...}.
  • Ships independently; closes the legal window and the executable-leak class. No layer changes yet.

Phase 1 — The sanitization gate (the lock comes before the cleanup)

  • Write verify-sanitized.sh with the §4.1 two-class rules + self-test; wire blocking in .woodpecker.yml. Build goes red on the current contamination — intended; it scopes Phase 2.
  • Ships independently as "CI now fails on operator data," even before the data is removed (the red build is the worklist).

Phase 2 — Sanitize the existing tree to green (mechanical, no architecture)

  • Purge all operator tokens across guides/, defaults/TOOLS.md, README.md, mosaic-doctor, mosaic-init defaults; rails/tools/ across both template families; drop "Master/slave".
  • Delete defaults/SOUL.md, jarvis-loop.json; relocate the AUDIT file; create examples/*.
  • Phase 1's gate goes green. Ships independently; package is now PII-free but still pre-Constitution.

Phase 3 — Extract L0 by subtraction

  • Create defaults/CONSTITUTION.md (gates one place, §1.4 split, capability-verb authored, precedence verbatim, firewall rule, tier-aware self-load).
  • Gut defaults/AGENTS.md to the ~50-line dispatcher; remove the false line 11.
  • Create constitution/LAYER-MODEL.md. Strip restated policy from STANDARDS.md + the four RUNTIME files; rewrite the sequential-thinking lines to capability verbs.
  • Add the L0 line-count CI ceiling over framework-owned resident files only (§7).
  • Ships independently; no install/migration changes yet — fresh installs get the new structure.

Phase 4 — Overwrite semantics + migration + headless bootstrap

  • Split seed lists into FRAMEWORK_OWNED (overwrite) vs USER_SEEDED (seed-if-absent) in BOTH installers; remove AGENTS.md/STANDARDS.md from PRESERVE_PATHS; add CONSTITUTION.md.
  • Implement snapshot→sync→restore; vendor the v2 baseline; v2→v3 migration moves user edits to .local/.bak. Bump FRAMEWORK_VERSION=3.
  • install.sh runs mosaic-init --non-interactive (fail-closed persona).
  • Land the shared fixture suite (§3.4) run against both installers. Gates the tag.

Phase 5 — Overlay composer + cross-harness composer test

  • Build mosaic compose-contract <harness> per §3.2 (SOUL.local.md+USER.local.md+ STANDARDS.local.md; per-harness emission; documented bare-launch base-only behavior).
  • Composer unit test (§5): per-tier anchor present; Tier-3 byte-equal to L0.
  • Ships independently as "customization now survives upgrades."

Phase 6 — Docs, compliance matrix, alpha tag

  • CONTRIBUTING.md (operator-hygiene, dual-installer parity rule, known-limitations §9, harness×gate compliance matrix with the hook-parity gap marked).
  • PRD ↔ design reconciliation; tag the alpha after the full DoD (§8) is green.

7. Resident-token budget (steward RISK / contrarian R5 / devex m8 — accepted, re-scoped)

Budget the container by line count, keep gate wording intact. But CI cannot see user-generated SOUL.md/USER.md, and the resident set varies per harness tier (contrarian R5). So the control is split:

  • CI (package-side): a line-count ceiling over framework-owned resident files only (CONSTITUTION.md + dispatcher AGENTS.md + the resident RUNTIME.md slice). Real and enforceable.
  • mosaic doctor (runtime advisory): sums the actual composed prompt — including SOUL.md/ USER.md and the per-harness tier — and warns the operator. This is the only place the total resident budget is visible, and it is per-harness, not a single global number (devex m8: hook-less harnesses like Pi need more resident, so the advisory threshold is per-harness).

Gates keep full wording; procedure (wrapper paths, flags) moves to on-demand E2E-DELIVERY.md. Reject "exactly 500 words for L0" — gate #13 alone is ~110 words; a word cap forces paraphrasing law, the exact drift vector being killed.


8. Alpha Definition of Done (for the PRD)

Blocking, all CI-green: MIT LICENSE + package.json field; three credential-path sites + the hook URL fast-failed; verify-sanitized.sh (two-class, *.sh+*.md, self-tested) wired blocking; operator data purged from the full set (guides/tools/init-generator included); rails/tools/ in both template families; defaults/SOUL.md+jarvis-loop.json deleted; CONSTITUTION.md extracted (gates one place, capability-verb, §1.4 split, no false "already loaded"); AGENTS.md/STANDARDS.md out of PRESERVE_PATHS and seed-semantics switched to overwrite in both installers; snapshot/ migration v2→v3 moving user edits to .local/.bak; mosaic-init --non-interactive fail-closed persona; 5-fixture matrix (§3.4) green against both installers asserting injected bytes; compose-contract built + composer unit test (per-tier anchor, Tier-3 byte-equality); resident line-count ceiling enforced; CONTRIBUTING.md + compliance matrix; tag the alpha. PRD precedes implementation.

Deferred to v2 (explicit): constitution/ deploy directory; adapters/<h>.capabilities.json; 3-way merge; live-launch cross-harness smoke test; policy/*.md composition; per-layer version stamps as a migration driver; DCO CI.


9. Red-Team Disposition (every finding mitigated or accepted)

Finding Disposition
contrarian R1 / steward RISK-04 — "remove from PRESERVE_PATHS" doesn't update resident root file Mitigated §3.1: split seed lists, unconditional overwrite for framework-owned, in BOTH installers; test injected bytes
contrarian R2 — snapshot/restore described but unimplemented; cp-fallback can lose data Mitigated §3.3: atomic snapshot→sync→restore + interrupt fixture; user-edited AGENTS.md.pre-constitution.bak
contrarian R3 / steward RISK-11 — live-launch smoke test impractical Mitigated §5: re-scoped to composer unit test; live-launch → v2; hook-parity tracked in compliance matrix
contrarian R4 / devex B1 — deleting defaults/SOUL.md + interactive init bricks headless first-run Mitigated §3.3: checkSoul() self-heals mosaic launches; install.sh runs --non-interactive init; fixture 4
contrarian R5 / devex m8 — line budget can't see user files / varies per tier Mitigated §7: CI ceiling on framework files only; doctor per-harness runtime advisory
contrarian R6 — extracting gate #13 weakens a hard gate for non-adopters Mitigated §1.4: split #13 — disambiguation stays universal in L0; only the named delegation leaves
contrarian R7 / devex B3 / steward RISK-03 — denylist false-positives / misses the class Mitigated §4.1-4.2: two rule-classes (structural + labeled denylist); L0 prose firewall is the primary class-closer
contrarian R8 / steward RISK-06 / devex M6 — compose-contract is a new subsystem called "zero" Accepted + scoped §3.2: alpha-blocking work item with tests; policy/ composition deferred to v2 with rationale
contrarian R9 / steward RISK-07 — conditional self-load asks model to introspect Mitigated §1.6: Tier-3 read is unconditional; conditional only on Tier-1
contrarian R10 — two installers synced by a comment, TS path ignored Mitigated throughout: every mechanism "in both installers, one shared fixture suite"
devex B2 — non-interactive init ships "Assistant" + Jarvis role Mitigated §2b/§3.3: fail-closed persona; grep init defaults
devex B3 / steward RISK-01 — credential leak in 6+/3 files, grep misses tools/+*.sh Mitigated §2b/§4.1: all three *.sh sites + hook URL; grep scoped to tools/ and *.sh
devex M4 — Tier-3 paraphrase = two "Mosaics" Mitigated §5: Tier-3 anchor is a literal L0 substring; byte-equality asserted
devex M5 / steward RISK-05 — pulling from PRESERVE clobbers existing edits; non-TTY false-green Mitigated §3.3/§3.4: vendor v2 baseline, extract delta→.local before overwrite; fixtures pin MOSAIC_INSTALL_MODE
devex M7 — sequential-thinking contradiction in 4 files; L0 "else stop" halts Pi Mitigated §5/§4.1: rewrite all 4; L0 capability-verb only; structural CI rule + Pi fixture
devex m9doctor drift advisory absent on bare launches Accepted §3.5: documented limitation; self-load nudge
devex m10 / steward RISK-08CLAUDE.md.template siblings keep rails/ + gates Mitigated §2b: both template families; CI /rails/ rule over templates/
devex m11 — dead-path/legacy-term sanitization is one-off Mitigated §4.1: structural rules close the dead-path class
steward RISK-02verify-sanitized.sh doesn't exist / unwired Mitigated §2a/§4.1/Phase 1: built, self-tested, wired blocking
steward RISK-09 — "Master/slave" framing Mitigated §2b: → "Primary / satellite"
steward RISK-10 — no LICENSE Mitigated Phase 0

Accepted residual risks (stated in CONTRIBUTING.md): bare-launch overlay no-op (base-only) and bare-launch drift-undetected-by-doctor — both inherent to launches that bypass mosaic; mitigated by the unconditional Tier-3 self-load + nudge, not eliminated. Codex/OpenCode hook parity is a tracked v2 gap. Live-launch cross-harness verification is v2.