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

282 lines
20 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.
# Rebuttal — Cross-Harness DevEx Lens
**Author lens:** Cross-Harness DevEx Expert — Claude Code / Codex / Pi / OpenCode injection + tool
differences; owns portability and the end-user customization experience.
**Method:** Read all seven position papers (`position-{aiml,architect,coder,contrarian,devex,moonshot,steward}.md`)
and re-verified every load-path / injection / install claim against the real tree under
`packages/mosaic/framework/`. This rebuttal does not restate my opening paper; it adjudicates the
*others* from the one seat that actually has to make the contract land identically on four harnesses
that inject context in three incompatible ways.
The conference has near-unanimous consensus on the easy 80% (split out an L0 Constitution by
ownership/mutability; delete `defaults/SOUL.md`; ship a CI PII gate; kill the `rails/``tools/` path
drift; budget the resident core). I will not relitigate that — it's settled, and I agree. The
remaining 20% is where the design actually lives or dies, and it is almost entirely **my lane**:
*how does L0 reach the model, and what happens when it doesn't.* Three of the six other papers get
that question subtly but dangerously wrong.
---
## 1. The 23 strongest ideas from other personas worth keeping
### 1a. Coder's self-bootstrapping Constitution — the single best idea in the room, because it is the only one that survives a harness we don't control
`position-coder.md` §"Biggest Risk" and §"Single Strongest Recommendation" name the failure mode the
governance-first papers all skate past:
> "If `mosaic claude` composes a `--append-system-prompt` that includes AGENTS.md but not
> `constitution/CORE.md`, the hard gates are silently absent... The Constitution must not rely on the
> launcher getting the injection order right; it must be a file the agent is instructed to read
> regardless."
This is correct and it is *load-bearing for my entire lens*. Ground truth: today
`defaults/AGENTS.md:11` literally asserts *"The core contract is ALREADY in your context (injected by
`mosaic` launch). Do not re-read it."* — and that claim is **false on a bare `claude` launch**, where
the only artifact is the thin `~/.claude/CLAUDE.md` pointer (`runtime/claude/CLAUDE.md:12-13` admits
it is "only a fallback for direct `claude` launches"). An agent that trusts a false "already loaded"
assertion skips the read and runs ungoverned. The contrarian (`position-contrarian.md` DQ4 point 1)
independently flags the same line as "a behavior-degrading rule." Two lenses converging on the same
concrete bug means it's real.
**Keep:** L0 must be *both* injected by value *and* self-loadable by file-read instruction, and the
pointer must never claim residency it can't guarantee. Belt and suspenders, because on the harnesses I
own, the suspenders (injection) are not always wearable.
### 1b. AI/ML's resident-token budget as a CI-enforced wall, and the "physics" framing that justifies it
`position-aiml.md` is the only paper that treats *what the model can actually weight* as a first-class
constraint rather than an afterthought. Its DQ5 diagnosis — ~300+ resident lines / ~34K tokens of
"dense, imperative, partially-redundant, partially-contradictory law... including for `list the files
in this dir`" — is exactly right, and its mechanism (a non-advisory line-count assertion in
`mosaic-doctor` + framework CI) is the only proposal that stops the new `CONSTITUTION.md` from
re-bloating into the old 155-line `AGENTS.md`. Its closing line — *"Ship the budget gate in the same
alpha as the Constitution, or don't ship the Constitution"* — should be adopted verbatim as alpha DoD.
From the DevEx seat this matters doubly: the **weakest-context harness sets the ceiling for
everyone**. A budget that fits Pi's `--append-system-prompt` and Claude's window must also survive
Codex/OpenCode writing the same bytes to an instructions file the model may only skim. Budget
discipline is portability discipline.
### 1c. Steward's license + `credentials.sh` findings — the only papers-killing-shipping-blockers nobody else surfaced
`position-steward.md` §"The Missing License" and finding S6 (`tools/_lib/credentials.sh:19` hardcodes
`$HOME/src/jarvis-brain/credentials.json` as a default) are the two findings that, if missed, make the
*first public push itself* a hygiene incident regardless of how clean the layering is. No LICENSE = not
legally open source (Berne default: all rights reserved). A hardcoded private credential path shipped
as a default is worse than the SOUL contamination everyone fixated on, because it's executable and it's
in the *tooling* layer, not the persona layer. These are unglamorous and correct. Keep both as alpha
blockers.
---
## 2. The 23 weakest / riskiest proposals, with concrete failure modes
### 2a. Moonshot's machine-readable front-matter + "launcher refuses to start on hash mismatch" — over-engineered enforcement that breaks exactly the harnesses I own
`position-moonshot.md` DQ1 proposes YAML front-matter (`mosaic-layer: 0`, `mosaic-override: forbidden`)
on each deployed file, and a launcher that "reads these headers and refuses to start if a layer-0 file
has been structurally overridden (content-hash check against installed version)." `position-steward.md`
S11 proposes the cousin: `mosaic doctor --check-constitution` treating any deployed-file diff as "an
error, not a warning, because it means the hard gates may be compromised."
Concrete failure modes from the cross-harness seat:
1. **YAML front-matter is not free in resident context — it's noise injected into the prompt.** These
files are concatenated into the system prompt (`--append-system-prompt` on Pi/Claude; an instructions
file on Codex/OpenCode). A `---\nmosaic-layer: 0\nmosaic-owner: framework\nmosaic-override:
forbidden\n---` block at the top of the *highest-primacy position in the whole stack* spends the most
valuable attention real estate (aiml behavior #1, primacy) on metadata the model must parse and then
ignore. The framework's own best instinct is the opposite: `defaults/USER.md` leads with human-readable
prose, not machine front-matter. Machine-readable layer tags belong in a *manifest the launcher reads*,
never in the *text the model reads*. This is precisely the adapter-capability-manifest split I argued
for — keep machine metadata out of the model's eyes.
2. **"Launcher refuses to start on hash mismatch" makes the framework hostile and is trivially bypassed
on 3 of 4 harnesses anyway.** A user who adds one clarifying line to their deployed contract now
cannot launch. Worse: the hash check only runs inside `mosaic <harness>`. A bare `claude`, `codex`,
or `opencode` launch — which the framework explicitly supports via thin pointers — *never invokes the
launcher*, so the "refuse to start" gate is absent on every direct launch. You get a control that
punishes compliant `mosaic`-launch users and is invisible to exactly the unmanaged launches where
drift is most likely. That is enforcement theater with a usability tax.
3. **Treating any constitution-file diff as a violation collides with the upgrade model.** During a
v2→v3 migration the deployed file legitimately differs from "installed version" for a window. A
checksum-as-violation check will false-positive every mid-upgrade state and every legitimate
`MOSAIC_NO_SYMLINK` copy that differs by a trailing newline.
**Resolution:** enforce L0 immutability *structurally* (it lives in a framework-owned dir that is
overwritten wholesale on upgrade — architect/coder/steward all converge here) and *socially* (CI PII +
dead-path grep on the source repo). Drop the runtime hash-refusal. Detection (`mosaic doctor` reporting
drift as an *advisory*) is fine; *refusing to launch* is not.
### 2b. The proliferation of three mutually-incompatible "user overlay" schemes — a portability landmine if any one ships as-is
Three papers invented three *different* customization-survival mechanisms, and nobody noticed they
conflict:
- `position-coder.md` DQ3: per-*guide* `E2E-DELIVERY.local.md` siblings, with `AGENTS.md` instructing
"after loading any guide, check for a `.local.md` variant and merge-read it."
- `position-aiml.md` DQ3 + `position-devex.md` (mine): per-*layer* `SOUL.local.md` / `USER.local.md`,
loaded last-within-layer.
- `position-contrarian.md` DQ3: an `<!-- mosaic:include STANDARDS.local.md -->` directive embedded in
the shipped file.
These are not interchangeable and the difference is *my* problem, because each implies a different
*injection-time composition step* and the four harnesses compose differently:
- The coder's "agent checks for a `.local.md` after each guide load" assumes the **agent** does the
merge at read time. That works on a file-reading harness but is redundant/confusing when the launcher
already injected a pre-composed blob (Pi, `mosaic claude`) — now the agent is told to go re-read and
merge files that are *already in its system prompt*, doubling tokens and risking contradiction between
the injected copy and the freshly-read copy.
- The contrarian's `<!-- mosaic:include -->` directive assumes a **composer** that understands the
directive. Markdown comments are inert; nothing in the current tree processes them. Ship that directive
without building the processor (`mosaic compose-contract`, which only the architect actually specs) and
the "override" is a no-op comment the model ignores — silent failure of the user's customization.
**Concrete failure mode:** a user reads the contrarian's docs, adds `STANDARDS.local.md`, and it is
*never loaded* because the Claude path injects `STANDARDS.md` verbatim with the comment treated as text.
The user believes their tightened secret-handling rule is active; it isn't. That's a security regression
dressed as a customization feature.
**Resolution (my lane to call):** pick **one** overlay mechanism, and make the **launcher/composer**
own it, not the agent. Exactly one composition step (`mosaic compose-contract <harness>`, per
`position-architect.md` DQ4) resolves base + `.local` overlays *before* injection, on every harness, so
the model receives one already-merged blob and never runs a read-merge ritual that's redundant on
injected harnesses and the only path on pointer harnesses. Overlay granularity = per-layer
(`SOUL.local.md`, `USER.local.md`, `STANDARDS.local.md`), not per-guide — guides are L1 framework-owned
and should be referenced, not forked.
### 2c. Architect's + my own 3-way-merge reconciliation engine — the contrarian's attack lands, and I concede part of it
`position-architect.md` DQ3 and my own opening paper both propose per-file template versioning plus a
`git merge-file`-style 3-way merge (`mosaic-reconcile`) for user-seeded files on upgrade.
`position-contrarian.md` DQ3 attacks this directly: *"Reject version-pinning per-file. Per-file pins
create a combinatorial matrix of (framework vN, user pinned vM) states that no one will test."*
He's right about the **test matrix**, and from a DevEx standpoint an *interactive merge-conflict
resolution flow* is a terrible first-run/upgrade experience — it drops a non-expert user into
`<<<<<<< theirs` markers in a config file they didn't know they were editing. For an alpha, that is too
much machinery for too little payoff.
**Resolution / concession:** for the alpha, adopt the **overlay model (2b) instead of 3-way merge**.
Overlays sidestep merge entirely: framework files are overwritten wholesale (no merge needed), user
deltas live in never-touched `.local.md` files (no merge needed). 3-way merge is only required for the
one genuinely-hand-tuned-generated file, `TOOLS.md` — and even there, the alpha can ship "we regenerate
`TOOLS.md` from template; your old one is backed up to `TOOLS.md.bak.<ts>`" (machinery `install.sh`
already has) rather than a conflict UI. Defer real reconciliation to post-alpha. The contrarian's
"subtraction before structure" applies to the *upgrade mechanism* too.
---
## 3. The key disagreement most relevant to my lens, sharpened — and how to resolve it
### The fault line: **inject-by-value (byte-for-byte, launcher-composed) vs. self-load-by-instruction (agent reads files).** Both camps are half-right, and the framework needs both *with a defined boundary* — which no paper draws.
- **Inject-by-value camp** (`position-aiml.md` DQ4: *"L0 must be injected as system-prompt text on
every harness, identically, byte-for-byte"*; `position-moonshot.md` DQ4; `position-steward.md` DQ4):
correct that injection at primacy position is strictly stronger than a deferred "go read this." A
system-prompt-resident gate is non-removable for the turn; a "please read `AGENTS.md`" pointer is a
request the model can skip under load.
- **Self-load camp** (`position-coder.md`): correct that the launcher cannot be trusted as the *sole*
delivery path, because bare `claude`/`codex`/`opencode` launches bypass `mosaic compose-contract`
entirely and get only the thin pointer.
Here is the fact both camps under-weight, and it is the central fact of my lens: **the four harnesses
do not offer the same injection channel, so "byte-for-byte identical injection everywhere" is not
currently achievable as stated.** Ground truth:
- **Pi:** full contract via `--append-system-prompt` + `--skill` + `--extension`
(`adapters/pi.md:14-16`). Tier-1 injection, strongest. *And* Pi has **no permission backstop**
(`runtime/pi/RUNTIME.md:20`), so resident-text fidelity is the *only* enforcement — aiml's point 4
is right: keep L0 tiny precisely because Pi has no hook wall behind it.
- **Claude:** `mosaic claude` *can* `--append-system-prompt` (Tier 1), but bare `claude` gets only
`~/.claude/CLAUDE.md` (Tier 3 pointer). **And Claude's own harness injects competing
`<system-reminder>` mandatory-read blocks** — this very session's reminder demonstrates the harness
will inject *its own* "read these files first" instructions that compete with ours for primacy.
- **Codex / OpenCode:** write to an instructions file (`~/.codex/instructions.md`,
`~/.config/opencode/AGENTS.md`) — between Tier 1 and Tier 3; resident-ish but the model may skim.
So "byte-for-byte everywhere" is an *aspiration*, not a switch you flip. The honest design is a
**tiered injection contract that names the strength per harness and degrades safely**, which is exactly
the per-harness capability manifest I proposed (`position-devex.md` DQ4) — and which the inject-by-value
papers asserted *as if all four harnesses were symmetric*. They are not. `position-aiml.md` even
half-concedes this in its own point 4 (Pi special case) without following the thread to its conclusion:
if Pi is special, the contract is *not* byte-for-byte uniform, it's capability-resolved.
### Proposed resolution — a single, testable injection contract that both camps can sign
1. **L0 is delivered by the strongest channel each harness offers (manifest-declared), AND is
self-loadable as a fallback. The two are not alternatives — they are tiered.**
- Tier 1 (system-prompt append: Pi, `mosaic claude`, `mosaic codex` where supported): launcher
injects the composed L0 by value at primacy. The pointer/AGENTS index then says: *"The Constitution
is resident above. If it is NOT in your context, read `~/.config/mosaic/CONSTITUTION.md` now."* —
conditional, not the false unconditional "already loaded; do not re-read" of `defaults/AGENTS.md:11`.
- Tier 3 (bare-launch pointer: direct `claude`/`codex`/`opencode`): the pointer carries the
**5-bullet irreducible-gate summary inline** (aiml DQ4 point 3) *and* the instruction to read the
full `CONSTITUTION.md`. Even a model that skips the read has the irreducible law resident.
2. **Per-harness capability manifest (`adapters/<h>.capabilities.json`) is the single source for: which
injection tier this harness gets, and how abstract capability-verbs in L0 map to concrete tools.**
This is what collapses the four near-duplicate "sequential-thinking required (except Pi)" stanzas
(`runtime/{claude,codex,opencode}/RUNTIME.md` require it; `runtime/pi/RUNTIME.md:59-61` exempts it).
The Constitution says *"use structured multi-step reasoning before planning"* (capability verb); the
manifest resolves it to `mcp:sequential-thinking` (gate=true) on Claude/Codex/OpenCode and
`native-thinking` (gate=false) on Pi. `position-moonshot.md` DQ4 reached the same "behavior
requirement, not tool requirement" conclusion for sequential-thinking — generalize it to *all*
capability references via the manifest rather than prose carve-outs scattered across runtime files.
3. **Back every hookable gate with a hook where the harness has hooks; track parity as an open gap, not
a silent inconsistency.** This is repo-proven doctrine, not theory: `runtime/claude/RUNTIME.md:30-32`
says the prose memory rule "proved insufficient — the hook is the hard gate." Promote that to
Constitution doctrine. The contrarian (DQ5 point 4) and I agree here. The manifest is also where
"Codex/OpenCode have no `prevent-memory-write` equivalent yet" gets recorded as a tracked gap — which
is the *honest* version of moonshot's COMPLIANCE matrix, minus the launch-refusal enforcement I
rejected in 2a.
4. **Resolve the inject-vs-self-load tension by making the launcher own composition and the agent own
verification.** Launcher composes + injects (`mosaic compose-contract`, architect DQ4). Agent runs a
one-line self-check: *"if CONSTITUTION not resident, read it."* This satisfies the inject-by-value
camp (strongest channel used) and the self-load camp (never trusts the launcher blindly) with a
single defined seam, and it is **testable**: a CI smoke test launches each harness path (Pi append,
`mosaic claude` append, bare-`claude` pointer, Codex instructions-file) and asserts the 7 irreducible
gates are present in the effective context. That smoke test — not a hash-refusal, not front-matter —
is the mechanical control that makes "the Constitution is enforced across harnesses" a *true*
statement instead of an aspirational one.
The disagreement dissolves once you stop pretending the four harnesses are symmetric. They aren't; the
manifest names the asymmetry; the tiered contract degrades safely across it; the smoke test proves it.
---
## Top contentions (return value)
1. **Keep coder's self-bootstrapping Constitution**`defaults/AGENTS.md:11`'s "already loaded; do not
re-read" is *false* on bare `claude`/`codex`/`opencode` launches and makes agents skip the gates. L0
must be injected by value AND self-loadable by instruction; the pointer must never claim residency it
can't guarantee.
2. **Keep aiml's CI-enforced resident-token budget and steward's two shipping blockers** (LICENSE file;
`tools/_lib/credentials.sh:19` hardcoded private credential path). The weakest-context harness sets
the budget ceiling for all four — budget discipline IS portability discipline.
3. **Reject moonshot/steward's "launcher refuses to start on hash mismatch" + YAML front-matter on
resident files.** The hash gate is invisible on the very direct-launch paths where drift happens, and
punishes compliant users; front-matter spends primacy-position attention on metadata the model must
parse and ignore. Enforce L0 immutability structurally (overwritten dir) + socially (CI grep); machine
metadata goes in a launcher manifest, never in the text the model reads.
4. **Three papers invented three incompatible user-overlay schemes** (coder per-guide `.local.md`; aiml/me
per-layer `.local.md`; contrarian `<!-- mosaic:include -->`). Pick ONE, owned by the
launcher/composer, not the agent — the contrarian's inert-comment directive would silently no-op a
user's tightened security rule on the Claude path. Per-layer overlays, composed before injection.
5. **Concede the 3-way-merge attack.** For alpha, overlays replace reconciliation: framework files
overwritten wholesale, user deltas in never-touched `.local.md`. Defer real merge to post-alpha.
6. **The core disagreement is inject-by-value vs. self-load — and it's a false binary** rooted in the
wrong assumption that the four harnesses inject symmetrically (Pi system-prompt + no hook backstop;
Claude append-or-pointer + competing harness `<system-reminder>`s; Codex/OpenCode instructions-file).
Resolve with a **per-harness capability manifest** (injection tier + capability-verb→tool mapping,
collapsing the four "sequential-thinking except Pi" stanzas), a **tiered injection contract** that
degrades safely (Tier-1 append + Tier-3 pointer carrying the 5-bullet gate summary inline), and a
**CI smoke test asserting the 7 irreducible gates are resident on every harness path** — the only
control that makes "enforced across harnesses" true rather than aspirational.