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

473 lines
36 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.
# 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.md` → **always 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.
### Phase 0 — Legal & runnable-leak blockers (no behavior change)
- 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 m9** — `doctor` drift advisory absent on bare launches | **Accepted** §3.5: documented limitation; self-load nudge |
| **devex m10 / steward RISK-08** — `CLAUDE.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-02** — `verify-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.