docs(design): mosaic framework constitution — expert conference output
Some checks failed
ci/woodpecker/push/ci Pipeline failed
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:
281
docs/design/framework-constitution/debate/rebuttal-devex.md
Normal file
281
docs/design/framework-constitution/debate/rebuttal-devex.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# 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 2–3 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 / ~3–4K 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 2–3 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.
|
||||
Reference in New Issue
Block a user