# Position Paper — The Contrarian Skeptic **Lens:** Distrust complexity and clever abstractions. Hunt failure modes, over-engineering, and rules that look good on a page but degrade real agent behavior. Every claim below is grounded in files actually read under `packages/mosaic/framework/`. --- ## TL;DR for the impatient The brief frames the problem as "we need *more* structure: introduce a Constitution layer, a precedence stack, version pinning, reconciliation." My position is the opposite of the framing: **the framework's biggest defect is not under-layering, it is over-volume and internal contradiction.** The contract is ~155 lines of always-resident hard gates in `defaults/AGENTS.md`, duplicated almost verbatim in `templates/agent/AGENTS.md.template`, re-stated again in `guides/E2E-DELIVERY.md`, and a fourth time in `guides/ORCHESTRATOR.md` — and the four copies *already disagree with each other* (path `tools/git` vs `rails/git`, gate counts, merge-authority nuance). Adding a fifth document called "Constitution" on top of this does not fix conflation; it adds a fifth place for the copies to drift. So: yes to a named Constitution **only if it is the single source and the duplicates are deleted**, not added to. The win is subtraction. The risk is that this debate produces a beautiful four-layer precedence model that ships with the same 55 personal-data references (`grep` count, see §2) still in the package. --- ## DQ1 — Layering: yes to a Constitution, but earn it by deletion ### What's actually there The brief says three things are conflated. Reading the files, that's true but understated. The real layering today is **implicit and contradictory**, spread across at least six surfaces: - `defaults/AGENTS.md` — 13 "CRITICAL HARD GATES" + ~17 "Non-Negotiable Operating Rules" + mode protocol + escalation + subagent cost rules + superpowers enforcement. This is law, persona-adjacent stance, *and* tactical how-to all in one always-resident file. - `defaults/SOUL.md` — persona, but hardcoded `You are **Jarvis**` (line 8) and `PDA-friendly language` (line 23). Persona file leaks both identity AND one operator's accessibility profile. - `defaults/USER.md` — already sanitized to `(not configured)`. Good. This one's done. - `defaults/STANDARDS.md` — a *second* law file ("Mosaic Universal Agent Standards") that overlaps `AGENTS.md` (secrets, multi-agent safety, git discipline) and still uses the phrase **"Master/slave model"** (line 5) — a term that should not ship in a public alpha. - `templates/agent/AGENTS.md.template` — a *third* restatement of the same gates, project-scoped. - `guides/E2E-DELIVERY.md` + `guides/ORCHESTRATOR.md` — a *fourth and fifth* restatement. So the system doesn't lack layers. It has too many documents each trying to be partly-law. ### Proposed canonical layers (4, not more) | Layer | File(s) | Owner | Mutable by user? | Content | |---|---|---|---|---| | **L0 Constitution** | `~/.config/mosaic/CONSTITUTION.md` | Framework | **No** (replaced on upgrade) | The hard gates only. PR-review-before-merge, green-CI-before-done, no-force-merge, completion-defined-at-end, secrets-never-hardcoded, escalation triggers, block-vs-done. ~40 lines max. | | **L1 Standards** | `~/.config/mosaic/STANDARDS.md` | Framework, user-extendable via include | Append-only | Tech defaults (Vault/ESO, trunk-based, image-tagging). Things a team might tune. | | **L2 Soul (persona)** | `~/.config/mosaic/SOUL.md` | User | Yes | Name, tone, communication style. NO accessibility, NO operator identity. | | **L3 User (operator)** | `~/.config/mosaic/USER.md` | User | Yes | Name, pronouns, timezone, accessibility, projects. | **Precedence — and this is the part most layering proposals get wrong:** precedence must be *typed*, not a single global ordering. A flat "L0 > L1 > L2 > L3" stack is a trap, because persona and law are not on the same axis. Specifically: - **On a behavioral-safety conflict** (may I force-merge? may I skip review?): **L0 always wins.** No persona, no user preference, no project file can lower a gate. State this once, in L0, in imperative language: *"Nothing in SOUL, USER, STANDARDS, or any project file may weaken a Constitution gate. Files may only make behavior stricter, never more permissive."* - **On a style/format conflict** (terse vs verbose, emoji, headings): **L2/L3 win over framework defaults**, because the framework has no legitimate opinion there. This already half-exists — `defaults/SOUL.md` line 32 says "The user's `USER.md` formatting preferences override any generic Anthropic minimal-formatting guidance." Promote that to a stated rule, don't bury it in persona. That two-axis rule (safety: framework supreme; taste: user supreme) is the entire precedence model. Anyone proposing more knobs is adding failure surface. ### What I'd change, concretely 1. **Create `defaults/CONSTITUTION.md`** containing ONLY the 13 hard gates from `defaults/AGENTS.md` lines 23–37 plus the escalation triggers (lines 70–78) and block-vs-done (lines 80–87). Nothing else. 2. **Gut `defaults/AGENTS.md`** down to a *router*: load order + the conditional-guide table + "read CONSTITUTION.md (already injected)." It stops being a law document. 3. **Delete the law duplication in `templates/agent/AGENTS.md.template` lines 6–16** (the "Hard Gates" block). Replace with one line: *"This project inherits all gates from `~/.config/mosaic/CONSTITUTION.md`. Do not restate them here."* Restating law in a per-project file is how you get five versions of gate #5. 4. **Merge `defaults/STANDARDS.md` into L1**, drop the "Master/slave" framing entirely (`defaults/STANDARDS.md` line 5–8), and stop it from re-asserting gates that now live in L0. --- ## DQ2 — Sanitization: the package is still dirty; ship a CI gate, not good intentions ### Ground truth `grep -rilE 'jarvis|jason|woltje|PDA'` over `packages/mosaic/framework/` returns **30 files**; raw occurrence count is **55**. Concrete, not hypothetical: - `defaults/SOUL.md:8` — `You are **Jarvis**` - `defaults/SOUL.md:23` — `PDA-friendly language` (one operator's neurotype, shipped to everyone) - `defaults/TOOLS.md:40` — `MANDATORY jarvis-brain rule: when working in ~/src/jarvis-brain ...` — a machine-specific path **inside a default that gets seeded to every install** (`install.sh` line 235 copies `TOOLS.md` from `defaults/`). - `guides/ORCHESTRATOR.md:99,111,152` — hardcodes `~/src/jarvis-brain/docs/templates/` as the bootstrap template source. A downstream user has no `jarvis-brain`. **This guide is broken for everyone but the maintainer.** - `runtime/claude/settings-overlays/jarvis-loop.json` — entire file is a Jarvis/`~/src/jarvis` preset with `projectConfigs.jarvis`, `presets.jarvis-loop`, `jarvis-review`. The `defaults/README.md` line 7 *promises* "No personal data ... should be committed." That promise is currently false. A promise in prose is not a control. ### The sanitization strategy: template-then-init for identity, generic-defaults for law, and a blocking CI grep The brief offers three options (generic-defaults / empty-defaults+examples / template-then-init). My answer: **stop treating it as one decision — it's per-layer.** - **L0 Constitution + L1 Standards → generic-defaults.** Law has no personal data by nature once you remove the leaks. Ship it populated and real. A user who runs nothing still gets a working, safe contract. (Empty-defaults here would be actively dangerous — an empty gate file = no gates.) - **L2 Soul + L3 User → template-then-init, and ship the *generic* default as the fallback.** `defaults/SOUL.md` must become the *generic* version (the template already exists at `templates/SOUL.md.template` with `{{AGENT_NAME}}`). The current `defaults/SOUL.md` with hardcoded "Jarvis" should be **deleted and replaced by a generic-rendered default** (e.g. name `Mosaic`, neutral stance, no PDA line). `install.sh` already does NOT seed SOUL/USER (lines 230–240 only seed `AGENTS.md STANDARDS.md TOOLS.md`) — so the dirty `defaults/SOUL.md` exists only to contaminate the public repo and the wizard's reference. Kill it. - **`TOOLS.md` → generic-defaults with NO project-specific rules.** Delete `defaults/TOOLS.md:40`'s jarvis-brain rule. That rule belongs in *that user's* `USER.md` or a project `AGENTS.md`, never in a shipped default. ### The mechanism that actually prevents regression Good intentions decayed into 55 leaks. The fix is mechanical and cheap: **Add a CI check** `tools/bootstrap/agent-lint.sh` (file already exists and already references jarvis per the grep — fix it too) or a new `tools/ci/no-personal-data.sh`: ```bash # fails the build if any shipped file under packages/mosaic/framework/ # matches a denylist of personal tokens or absolute home paths. grep -rinE 'jarvis|jason|woltje|\bPDA\b|/home/jwoltje|~/src/jarvis' \ packages/mosaic/framework/ \ --exclude-dir=.git \ && { echo "PERSONAL DATA IN SHIPPED FRAMEWORK"; exit 1; } || exit 0 ``` Wire it into the existing CI (`.woodpecker/`). This is ~10 lines and it is the *only* thing that will keep the package clean after this debate's enthusiasm fades. **A precedence model without this gate is theater.** --- ## DQ3 — Customization & upgrade safety: the real design already exists; the danger is over-engineering it ### What's actually there (and it's decent) `install.sh` already implements the upgrade-safe mechanism the brief asks for: - `PRESERVE_PATHS=("AGENTS.md" "SOUL.md" "USER.md" "TOOLS.md" "STANDARDS.md" "memory" ...)` (line 24) excluded from `rsync --delete` in `keep` mode (lines 118–124). - `FRAMEWORK_VERSION=2` + `.framework-version` stamp + a real `run_migrations()` with sequential version gating (lines 160–202). - Defaults live in `defaults/` and are *seeded* into the framework root only if absent (lines 230–241), so the user's edited copy is never clobbered. This is a working source-vs-deployed reconciliation model **already**. The brief calls drift "a real problem today" — but the machinery to solve it is present. The actual bug is narrower: **`STANDARDS.md` is in `PRESERVE_PATHS` (user-owned) yet is also framework law.** That's the conflation, in one line. If law and customization share a file, you cannot upgrade the law without either clobbering the user (overwrite) or freezing the law forever (keep). This is *exactly* why L0 must be a separate file. ### What I'd change 1. **Constitution is NOT in `PRESERVE_PATHS`.** `CONSTITUTION.md` must be overwritten on every upgrade — that is the point of law. Add it to the *overwrite-always* set, not the preserve set. 2. **`STANDARDS.md` (L1) stays preserved but switches to an include model.** Ship `STANDARDS.md` that ends with: `# Local overrides\n`. The user edits `STANDARDS.local.md` (preserved, never shipped); the framework owns `STANDARDS.md` (overwritten). This gives upgrade-safe customization *without* the merge-conflict reconciliation engine someone will inevitably propose. 3. **Reject version-pinning per-file.** The brief floats "version pinning." Resist it. Per-file pins create a combinatorial matrix of (framework vN, user pinned vM) states that no one will test. One `FRAMEWORK_VERSION` integer + linear migrations (already built) is sufficient and comprehensible. Pinning is the over-engineering this lens exists to kill. ### Failure mode I want on the record `install.sh` line 99: in non-interactive/non-TTY mode it defaults to `keep`. That means **a CI re-install silently keeps a user's stale law file.** Once L0 exists and is overwrite-always, this is fine. *Until* then, a downstream user who edited `AGENTS.md` (today's law file, which IS in `PRESERVE_PATHS`) **never receives a gate update.** That's the upgrade-drift bug, already live, today. Splitting out L0 is the fix; nothing else is. --- ## DQ4 — Cross-harness robustness: single source, dumb adapters, and stop pretending the runtimes are symmetric ### Ground truth The adapters are tiny and mostly consistent (`adapters/claude.md`, `codex.md`, `pi.md`, `generic.md` all say "load STANDARDS.md + repo AGENTS.md"). The runtime refs (`runtime/claude/RUNTIME.md`, `runtime/codex/RUNTIME.md`) correctly say "global rules win on conflict." That spine is sound. **Do not rebuild it.** The real cross-harness defects are concrete and small: 1. **Injection asymmetry is unmodeled.** `defaults/README.md` lines 127–135: `mosaic pi`/`claude` inject via `--append-system-prompt`; `codex`/`opencode` write to a file; direct launches use a thin pointer that the model must *choose* to read. So "the Constitution is always resident" is true for two harnesses and *aspirational* for the rest. `defaults/AGENTS.md` line 11 asserts "The core contract is ALREADY in your context (injected by `mosaic` launch). Do not re-read it." — **this is false for a direct `claude` launch**, where only the thin `~/.claude/CLAUDE.md` pointer exists. An agent that believes a false "it's already loaded" claim will skip loading the gates. That is a behavior-degrading rule. **Fix:** L0 must be injectable *by value*, not by reference, on every harness. The composed system prompt for ALL launchers must literally concatenate `CONSTITUTION.md`. For direct launches where injection isn't possible, the pointer must say "READ CONSTITUTION.md NOW" — never "it is already loaded." 2. **Codex memory override is a maintenance landmine.** `runtime/codex/RUNTIME.md:36` mandates durable memory to `~/.config/mosaic/memory/`, while `runtime/claude/RUNTIME.md:26–35` mandates OpenBrain and *write-blocks* `MEMORY.md` via a hook. Two harnesses, two contradictory memory truths. The Constitution should state the memory *principle* once (one cross-agent store, named) and let adapters bind the mechanism. Right now the principle lives in two runtime files saying different things. 3. **Path drift across harnesses/files.** `templates/agent/AGENTS.md.template` uses `~/.config/mosaic/rails/git/` (12 template files do); `defaults/AGENTS.md` and `guides/*` use `~/.config/mosaic/tools/git/` (20 refs). `install.sh:193` even removes a stale `rails` symlink. So half the shipped templates point at a path the installer deletes. **Any agent following the template's queue-guard command gets "no such file."** This is the single most concrete "rule that degrades real behavior" in the repo. **Fix:** one canonical path (`tools/git/`), enforced by the same CI grep as §2 (`grep -rn 'mosaic/rails/' packages/ && exit 1`). ### Design principle Single source (`CONSTITUTION.md`) → composed into every launcher's system prompt by value → adapters carry ONLY the harness-specific *binding* (how to declare a subagent model, where MCP config lives), never a restatement of law. The adapters today are already close to this. The job is to keep them dumb and delete the law that has crept into guides/templates. --- ## DQ5 — Minimalism vs completeness: the core is bloated, contradictory, and partly self-defeating This is the heart of my position. ### Evidence of bloat-induced degradation - **Duplication breeds contradiction.** `defaults/AGENTS.md` hard gate #13 (lines 37) adds a nuanced "Merge authority (coordinated work)" exception dated 2026-06-11. `templates/agent/AGENTS.md.template` gate list (lines 6–16) does **not** contain it. So a project-scoped agent reading the template has a *different, staler* merge policy than a global agent. Two copies, two policies. With four copies, you get four. - **The contract argues with itself about complexity.** `defaults/AGENTS.md:36` (gate #12) and `guides/E2E-DELIVERY.md:37` both contain a "COMPLEXITY TRAP" warning insisting intake is unconditional for "simple" tasks. The *existence* of a dedicated warning that agents keep skipping intake is itself evidence the contract is too heavy to internalize — agents shed it under load and the framework's response was to add *more* words telling them not to. That's a spiral. The fix for "agents skip the procedure because it's huge" is **a smaller procedure**, not a louder warning. - **Always-resident volume.** Between `AGENTS.md` (155 lines), `STANDARDS.md` (~71), `SOUL.md` (~54), and `USER.md`, the launcher injects several hundred lines of MUST/HARD-RULE before the agent reads the task. Past a threshold, more imperatives reduce adherence to *each* imperative. The conditional-guide table (`AGENTS.md` lines 89–110) is the right instinct — push depth on-demand — but the always-resident core didn't shrink to match. ### Concrete minimalism proposal 1. **L0 Constitution: hard cap ~40 lines, gates only, no how-to.** A gate states the invariant ("Completion requires merged PR + green CI + closed issue") not the procedure (which wrapper, which flag). Procedure goes to `guides/E2E-DELIVERY.md`, loaded on implementation. The line `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push|merge` does NOT belong in always-resident law (`AGENTS.md:30`); it belongs in the delivery guide. 2. **One law document, period.** After L0 exists, `AGENTS.md` keeps zero gates, the template keeps zero gates, the guides *reference* gates by number ("satisfies Constitution §C5") and never restate them. Single source or it rots — this repo is the proof. 3. **Kill the redundant second law file.** `STANDARDS.md`'s gate-like content (secrets HARD RULE, multi-agent safety, git discipline) is duplicated from `AGENTS.md`. Move the genuinely-standards parts to L1, delete the duplicated gates. 4. **Measure adherence, don't assume it.** The framework has no feedback loop proving the gates *work*. The hooks (`prevent-memory-write.sh`, `qa-hook-stdin.sh`, `typecheck-hook.sh` per `runtime/claude/RUNTIME.md:54–58`) are the right model: a gate enforced by a hook beats a gate written in prose ten times over. **Prefer mechanical enforcement (hooks/CI) over prose gates wherever the gate is checkable.** Each prose-only gate is a suggestion; each hook is a wall. The brief's "keep the hard gates intact" goal is best served by converting the checkable ones (no-force-merge, green-CI-before-done, no-hardcoded-secrets) into CI/hook checks, and trimming the prose. ### What completeness still requires I'm not arguing for anarchy. The escalation triggers, block-vs-done distinction, and PR/CI/issue completion gate are load-bearing and must stay resident — they govern *when the agent stops*, which prose is the only place to encode. Keep those. Cut the procedural how-to and the duplication. --- ## Summary of concrete changes (file-level) | # | Change | File(s) | Why | |---|---|---|---| | 1 | Create `CONSTITUTION.md`, gates only, ≤40 lines | new `defaults/CONSTITUTION.md` | Single source of law; separable from customization | | 2 | Gut to a router; remove gates | `defaults/AGENTS.md` | Stop being a 5th law copy | | 3 | Delete hard-gate block; reference Constitution | `templates/agent/AGENTS.md.template:6–16` | Kill per-project law drift (already stale re: merge-authority) | | 4 | Delete dirty SOUL; ship generic default | `defaults/SOUL.md` (Jarvis/PDA lines 8,15,23) | Sanitize persona + accessibility leak | | 5 | Delete jarvis-brain rule | `defaults/TOOLS.md:40` | Machine-specific path seeded to every install | | 6 | Parameterize bootstrap template path | `guides/ORCHESTRATOR.md:99,111,152` | Guide is broken for all non-maintainer users | | 7 | Delete or templatize the Jarvis preset | `runtime/claude/settings-overlays/jarvis-loop.json` | Pure personal contamination | | 8 | Unify `rails/git`→`tools/git` | 12 `templates/**/*.template` files | Templates point at a path `install.sh:193` deletes | | 9 | Fold STANDARDS into L1 + include model; drop "Master/slave" | `defaults/STANDARDS.md` | Resolve law/customization conflation + bad term | | 10 | Add blocking CI personal-data + path-drift grep | new `tools/ci/no-personal-data.sh` + `.woodpecker/` | The only durable anti-regression control | | 11 | Constitution = overwrite-always (not in PRESERVE_PATHS) | `install.sh:24` | Law must upgrade; today `AGENTS.md` is preserved → gate updates never reach edited installs | | 12 | Pointer says "READ NOW", not "already loaded" | `defaults/AGENTS.md:11`, direct-launch pointers | False "already injected" claim makes agents skip gates on direct launch | --- ## The one thing I'd die on **Subtraction before structure.** This debate will be tempted to design an elegant multi-layer Constitution with rich precedence and reconciliation. The repo's actual disease is *duplication and contradiction*, not missing layers. If we add `CONSTITUTION.md` without deleting the four existing restatements and wiring a CI grep, we will have five disagreeing law files instead of four, plus a prettier diagram. The layering is worth exactly as much as the deletions and the CI gate that accompany it — and not one line more.