docs(design): mosaic framework constitution — expert conference output
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:
2026-06-15 23:47:49 -05:00
parent d481a74a86
commit c70b217a5c
22 changed files with 5822 additions and 0 deletions

View File

@@ -0,0 +1,472 @@
# 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.