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

260 lines
16 KiB
Markdown

# Rebuttal — OSS Steward & Security/Compliance Lens
**Author role:** OSS Steward & Security/Compliance
**Responding to:** position-architect.md, position-coder.md, position-contrarian.md,
position-devex.md, position-moonshot.md, position-aiml.md
**My original position:** position-steward.md
---
## Part 1 — Strongest Ideas from Other Personas Worth Keeping
### 1a. The DevEx lens on enforcement tiers is the most important cross-cutting insight
position-devex.md §DQ4 names something my position paper acknowledged but underweighted: **there
are two fundamentally different enforcement models in use today, and only one of them actually
enforces anything.** Pi gets the contract as a true system prompt via `--append-system-prompt`.
Claude, Codex, and OpenCode get a "please read these files" instruction in a user-editable memory
file. The DevEx paper (and the AIML paper independently) makes the enforcement-asymmetry concrete:
`runtime/claude/RUNTIME.md:30-32` already documents this lesson with respect to the memory-write
hook — "the rule alone proved insufficient — the hook is the hard gate." That sentence should be
promoted to Constitution doctrine, exactly as the DevEx paper proposes.
From a security posture: a hard gate enforced only by prose is not a hard gate. My position paper
proposed that "Constitution must be injection-resistant by position, not by instruction"
(position-steward.md §DQ4), but the DevEx paper gives this the operational teeth it needs —
specifically, that `mosaic claude` should inject L0 via `--append-system-prompt` and that
`~/.claude/CLAUDE.md` should be explicitly documented as a weaker fallback for bare `claude`
launches, not the primary enforcement path. This is strictly additive to my proposals and I
endorse it.
The capability-manifest idea (`adapters/<h>.capabilities.json`) is also worth keeping. My position
treated the adapter boundary as documentation; the DevEx formulation treats it as a machine-readable
contract that makes cross-harness gaps visible and auditable. This aligns directly with my S10
proposal (CI lint for deduplication) and extends it to per-gate enforcement coverage.
### 1b. The Contrarian on subtraction-first and the "rails" vs "tools" path drift
position-contrarian.md §DQ5 is right that adding a Constitution document without deleting the
four existing restatements produces five law files instead of four. The Contrarian is also the
only other paper that calls out the stale `rails/git/` path in `templates/agent/AGENTS.md.template`
as a concrete behavior-degrading bug — agents following the template's queue-guard command get
"no such file" on a live install because `install.sh:193` deletes the `rails` symlink. This is
exactly the kind of failure mode my lens exists to catch, and the Contrarian caught it more
explicitly than I did.
The Contrarian's hard cap of ~40 lines for L0 (versus my "~500 tokens target" in position-steward.md
§DQ5) is also the right order of magnitude and the more disciplined constraint. I accept it.
The "subtraction before structure" principle, while contrarian in framing, is security-consistent:
a shorter Constitution has fewer maintenance sites, fewer drift opportunities, and fewer lines
that can carry personal data under future commits. Deletion is a compliance control.
### 1c. The AIML lens on the `{{PLACEHOLDER}}` failure class
position-aiml.md §DQ2 introduces a failure class my sanitization section did not address:
a half-rendered template is *worse* than no file for an LLM. If `mosaic init` fails mid-render,
an agent that loads `You are **{{AGENT_NAME}}**` from `SOUL.md` may adopt the literal string
"{{AGENT_NAME}}" as a persona or treat the braces as an instruction artifact. The proposed
`mosaic-doctor` hard-fail on unrendered `{{...}}` or `${...}` tokens in any resident file is a
cheap, mechanical control that closes an entire failure class. I am adding it to my recommendation
set as S13 (below in §Part 3).
---
## Part 2 — Weakest or Riskiest Proposals
### 2a. The Moonshot's YAML front matter and hash-check launcher (position-moonshot.md §DQ1)
The Moonshot proposes adding `mosaic-layer:`, `mosaic-owner:`, and `mosaic-override:` YAML front
matter to each deployed file, with the launcher performing a content-hash check and refusing to
start if a layer-0 file has been structurally overridden.
This is the most dangerous-sounding "safety" proposal in the set, and my lens rejects it:
**Failure mode 1: the launcher is not the agent.** The hash-check-before-launch mechanism only
works if every agent session is launched through `mosaic <harness>`. A direct `claude` launch,
a Codex session launched through the platform's own tooling, or any future harness without a
wrapper binary bypasses the check entirely and silently. The DevEx paper already established that
three of four current harnesses enforce the contract only as a memory-file pointer — adding a
hash gate that those three harnesses cannot enforce is security theater that creates false
confidence.
**Failure mode 2: YAML front matter in an LLM context file is an injection surface.** A model
that reads front matter including `mosaic-override: forbidden` now has "forbidden" in its context
as a property of a rule. Adversarial prompt injection that adds `mosaic-override: allowed` to
a lower-layer file would read as a structural property to a naive parser, not as a contradiction
to check against the Constitution. The Constitution's injection-resistance guardrail
(currently `defaults/SOUL.md:48`, which I proposed promoting to L0) is the correct mitigation —
but it must not be undermined by teaching the model that override rules are expressed as parseable
properties.
**Failure mode 3: it blocks the alpha without adding OSS hygiene value.** A content-hash
check requires that the installed Constitution binary-match the shipped version. This breaks the
legitimate use case of a deployment that needs a localized version (translated docs, domain-specific
addendum to the gate list). My three-layer model already handles this by making L0 always-overwrite
on upgrade — that is the upgrade-safety mechanism, not hash enforcement. The Moonshot's mechanism
should be rejected for the alpha and reconsidered only after the simpler layer/directory boundary is
proven to work.
**Resolution:** keep the Moonshot's goal (detecting tampering with L0) but implement it via the
Contrarian's simpler mechanism: L0 is never in `PRESERVE_PATHS`, always overwritten, and
`mosaic doctor --check-constitution` compares checksums after-the-fact rather than blocking
launches. Advisory warnings are appropriate here; hard launch gates are not.
### 2b. The Architect's five-layer model and per-layer version stamps (position-architect.md §DQ1, §DQ3)
The Architect proposes five distinct layers (Constitution, Standards, Persona, Operator Policy,
Deployment/Runtime) with separate version stamps per layer (`constitution.version`,
`standards.version`, `user-schema.version`) and a three-way merge for user-seeded files.
The per-layer version stamp proposal has a concrete failure mode: **combinatorial migration matrix.**
If Constitution is at v5 and User schema is at v2, the installer must have tested and validated
the migration path for every `(constitution=N, user-schema=M)` combination where `N > M`. For a
project with a single maintainer shipping an alpha, this is a maintenance cliff. The Contrarian
named it: "Per-file pins create a combinatorial matrix of (framework vN, user pinned vM) states
that no one will test." The Architect's mechanism is correct in theory but wrong for an alpha
audience.
The five-layer model also introduces ambiguity about where the "operator policy" layer sits.
The Architect's smoking gun example — `defaults/AGENTS.md:37` ("Policy: Jason, 2026-06-11") —
is real and the fix is correct (move operator policy out of the Constitution), but creating a
dedicated `policy/*.md` layer for it adds a fourth always-resident file class when "USER.md has
a `## Operator Policy` section" is sufficient and simpler. The complexity should be justified by a
failure mode that a simpler design cannot handle. No one in this debate has named one.
**Resolution:** three layers (Constitution, Persona, Operator Profile) with a single
`FRAMEWORK_VERSION` integer plus the directory-boundary upgrade mechanism are sufficient for the
alpha. The Architect's per-layer stamps and three-way merge are good roadmap items for post-1.0.
### 2c. The DevEx on symlinks as the copy-on-link fix (position-devex.md §DQ3)
The DevEx paper proposes inverting the `mosaic-link-runtime-assets` policy to symlink
framework-owned runtime pointers and copy only user-editable surfaces. The principle is correct
(single source of truth, zero drift), but the concrete symlink proposal introduces a security
consideration that the paper acknowledges but does not fully resolve: Windows symlink support.
More critically for OSS hygiene: symlinks that point from `~/.claude/CLAUDE.md` into
`~/.config/mosaic/runtime/claude/RUNTIME.md` mean that the user's Claude harness now has a
persistent pointer into the mosaic config directory. If the mosaic config directory is mounted or
shared (e.g., in a container, in a dotfiles repo, in a shared dev environment), the symlink
exposes the entire `~/.config/mosaic/` tree to any process that can follow symlinks from the
Claude config location. The copy model, despite its drift risk, provides a natural isolation
boundary.
**Resolution:** keep the copy model as the default; add a `MOSAIC_SYMLINK_RUNTIME=1` opt-in
for users who understand the implications. This is already the DevEx paper's own caveat
(`MOSAIC_NO_SYMLINK=1` for Windows) — I am proposing it as the default rather than the
exception, because the privacy/isolation boundary matters more than the drift-elegance tradeoff
at alpha.
---
## Part 3 — Sharpened Key Disagreement: Who Bears Responsibility for PII Re-contamination
Every paper in this debate agrees on the diagnosis: personal data in shipped files, CI gate to
prevent it. There is no disagreement on the *what*. The disagreement my lens must sharpen is on
the *who bears ongoing responsibility and how the framework enforces it structurally, not procedurally.*
### The core disagreement
The Coder paper (position-coder.md §DQ2) describes the contamination as "surgical, not structural"
— approximately three files plus stray guide references. The DevEx paper counts 51 hits across 29
files. My position paper's evidence table lists 9 categories of violation. The Contrarian counts
55 raw occurrences across 30 files. **The disagreement about the contamination's extent reflects a
disagreement about the threat model:** is this a one-time cleanup or a structural re-contamination
risk?
The Moonshot names the ongoing risk most explicitly (position-moonshot.md §Biggest Risk): agents
running with the operator's SOUL.md and USER.md in context will generate framework-improvement PRs
that embed operator-specific terminology. The self-evolution rules in `defaults/AGENTS.md:136-139`
explicitly encourage this. Without a structural firewall, the framework re-contaminates itself
through its own best-practice enforcement.
### My lens's resolution
The re-contamination threat is structural, not one-time, because **the primary author of framework
improvements is an agent that always runs with operator-specific context.** This means:
1. **The CI grep is necessary but not sufficient.** Every paper agrees on the CI grep (denylist of
`jarvis|jason|woltje|PDA` over `packages/mosaic/framework/` excluding `examples/` and test
fixtures). That is S5 in my original proposals and it must be in the alpha DoD. But it only
catches the operator's *current* identity tokens. A future operator who also runs Mosaic daily
will contaminate the framework with *their* tokens, and no denylist written today will catch it.
2. **The structural fix is a "no operator context in framework PRs" rule enforced by the
framework's own scaffolding.** Concretely: the `defaults/AGENTS.md` self-evolution rules
(lines 136-139) must include a new hard constraint:
> When capturing a `framework-improvement` or `tooling-gap` pattern to OpenBrain or proposing
> a framework PR, you MUST NOT include content derived from SOUL.md, USER.md, or any
> operator-specific context. Framework proposals must be operator-agnostic by construction.
> If you cannot express the improvement without operator-specific language, that is a signal
> the improvement belongs in `policy/` or a project `AGENTS.md`, not in the Constitution.
This rule belongs in the Constitution (Layer 0) because it gates framework evolution, not
just current sessions.
3. **The denylist must include a structural-category check, not just known tokens.** The CI grep
should also fail on patterns like `~/src/<word>`, `/home/<word>/`, and any absolute home-dir
path — not just the current operator's identifiers. This closes the class of violation, not
just the current instances.
4. **The `CONTRIBUTING.md` I proposed (S12) must be written before the alpha tag, not deferred
to pre-stable.** Contribution guidelines are the only mechanism that governs PRs from community
contributors who are not running the CI grep locally. The BRIEF's backward-compatibility
constraint and the "solid alpha release" goal both imply external contributors are in scope.
A `CONTRIBUTING.md` without a section on operator-data hygiene is an invitation to
re-contaminate.
5. **The missing LICENSE (my S1/S2) remains a blocker for everything else.** No other hygiene
measure matters if the package has no legal open-source status. Under the Berne Convention,
a publicly accessible repository without a license is "all rights reserved." Community
contributors who submit PRs without a CLA or DCO have unclear IP status. This is the highest-
severity finding in my original position and no other paper disputes it. It must be resolved
before the alpha tag, because after the alpha tag there will be downstream users whose code
depends on this package, and retroactively adding a license creates ambiguity about the
pre-license period. **Ship with MIT + DCO on day zero.**
### Additional proposal from rebuttal review: S13
Based on the AIML paper's finding (position-aiml.md §DQ2), I am adding:
**S13 — mosaic-doctor hard-fail on unrendered tokens in resident files.** `mosaic-doctor` must
fail non-advisorily if any file in the resident set (`CONSTITUTION.md`/`AGENTS.md`, `SOUL.md`,
`USER.md`, `TOOLS.md`, any `RUNTIME.md`) contains a `{{...}}` or `${VAR}` token (excluding
documented `${VAR:-default}` safe-defaults tagged with `# safe-default:`). This closes the
half-rendered-template failure class, which is a security-adjacent concern: a mis-rendered SOUL
with placeholder tokens could cause an agent to adopt an arbitrary string as its governing
identity.
---
## Summary of Top Contentions
1. **LICENSE + DCO is a blocker for the alpha — no other proposal matters without it.** A
public repository without a license is not open source. Ship MIT + a DCO CI check before
the alpha tag; there is no valid reason to defer this.
2. **CI PII grep must close the structural contamination class, not just the current tokens.** The
denylist must include absolute home-dir patterns (`~/src/<word>`, `/home/<user>/`) in addition
to operator-specific identifiers. The self-evolution rules in AGENTS.md must prohibit operator
context from entering framework PRs by explicit Constitution rule, not just by convention.
3. **The Moonshot's hash-check launcher gate is security theater for three of four harnesses and
should be replaced by the simpler always-overwrite-L0 + post-hoc doctor check.** A launch-time
guard that only works when `mosaic <harness>` is the entry point provides false confidence about
direct launches, where the Constitution is weakest anyway.
4. **The Architect's five-layer model and per-layer version stamps are roadmap items, not alpha
requirements.** Three layers (Constitution, Persona, Operator) with directory-level ownership and
a single `FRAMEWORK_VERSION` integer are sufficient for the alpha and do not create a
combinatorial migration test matrix.
5. **CONTRIBUTING.md and the re-contamination rule in AGENTS.md must ship with the alpha.** The
self-evolution mechanism that allows agents to propose framework changes is a structural
re-contamination risk. Procedure (contributing guide) and rule (Constitution constraint on
framework-improvement proposals) are both required; neither alone is sufficient.