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

422 lines
22 KiB
Markdown

# Red-Team Report: OSS Steward & Security/Compliance Lens
**Author:** OSS Steward & Security/Compliance (red-team pass against synthesis-v1.md)
**Date:** 2026-06-15
**Scope:** Attempt to break the synthesis design. Every claim is grounded in actual files
under `packages/mosaic/framework/` — line references are real.
---
## Executive Summary
The synthesis resolves the right architectural problems but ships with at least five
conditions that could cause the alpha to fail on its own stated constraints: one that
leaks credentials into every downstream fork on day one, two that re-contaminate the
public package within the first framework PR authored by an agent, one that bricks the
migration on legacy installs with interactive prompts, and one that leaves the cross-
harness gate unenforceable for the alpha window. Each is ranked below.
---
## RISK-01 — BLOCKER: Three `$HOME/src/jarvis-brain/credentials.json` defaults are executable, publicly shipped, and run without `MOSAIC_CREDENTIALS_FILE` being set
**Severity:** Blocker
**Files:**
- `tools/_lib/credentials.sh:19``MOSAIC_CREDENTIALS_FILE="${MOSAIC_CREDENTIALS_FILE:-$HOME/src/jarvis-brain/credentials.json}"`
- `tools/git/detect-platform.sh:89` — same pattern, duplicated independently
- `tools/health/stack-health.sh:23``CRED_FILE="${MOSAIC_CREDENTIALS_FILE:-$HOME/src/jarvis-brain/credentials.json}"`
The synthesis (D8, §2b) correctly names two of these for repair but the grep found **three**
locations. `stack-health.sh` is missed. Each script is `chmod +x` by `install.sh:244` and
invocable by any user who runs `mosaic-quality-verify` or `stack-health`.
**Why this is a blocker and not just major:** A public OSS package that ships executable
scripts with a hardcoded absolute private home path (`$HOME/src/jarvis-brain/...`) is not
a style issue — it is a correctness failure. A downstream user's install will silently
default to a non-existent path, causing every credential-dependent tool to fail with a
misleading error. The error message will reference a path (`jarvis-brain`) that is
meaningless to any user who is not the original author. This leaks the primary maintainer's
directory layout into every fork and install. It also violates `STANDARDS.md:35` (the
framework's own rule: `${VAR:-default}` for required values is forbidden; use `${VAR:?}`
to fast-fail).
**The synthesis fix is correct but incomplete:** D8 says fix `credentials.sh` and
`detect-platform.sh`. It does not mention `stack-health.sh`. The `verify-sanitized.sh`
CI gate (synthesis §2a) will catch pattern `~/src/<word>` / `/home/<word>/` in `*.md`
files but the grep pattern as specified in the synthesis targets text files — it must
also cover `*.sh` to catch the three shell-script instances.
**Mitigation:**
1. Fix all three files: replace `${VAR:-$HOME/src/jarvis-brain/...}` with
`${MOSAIC_CREDENTIALS_FILE:?MOSAIC_CREDENTIALS_FILE must be set}` per `STANDARDS.md:35`.
2. Extend `verify-sanitized.sh` to cover `*.sh` files, not only `*.md`.
3. Add a fixture to the migration test matrix (synthesis §5.5): `MOSAIC_CREDENTIALS_FILE`
unset should produce a clear error, not a path-not-found on a private directory.
---
## RISK-02 — BLOCKER: The CI sanitization gate (`verify-sanitized.sh`) does not yet exist; the synthesis treats it as done, but the actual file is a TypeScript quality-gates test (`tools/quality/scripts/verify.sh`) that checks lint/type/gitleaks — not PII
**Severity:** Blocker
**Files:**
- `tools/quality/scripts/verify.sh` — exists, tests TypeScript/lint/gitleaks
- `tools/quality/scripts/verify-sanitized.sh` — does not exist (synthesis §2a names it as new)
- No `.woodpecker.yml` at framework root wires the gate to CI (only project-template
woodpecker files exist under `tools/quality/templates/`)
The synthesis declares `verify-sanitized.sh` a blocking CI gate (§2a, §4, D6). It does
not exist. This is the single most critical anti-regression control in the entire design —
without it, the "personal data / dead paths / unrendered tokens" contamination can re-enter
on the first framework PR authored by an agent running with someone's SOUL.md in context.
The synthesis notes correctly that an agent's own operator identity is the primary re-
contamination vector ("the primary author of future framework PRs is an agent running with
some operator's SOUL/USER in context" — §4). Without the gate being real and wired, the
entire sanitization guarantee is prose.
**Mitigation:**
1. The alpha cannot tag until `verify-sanitized.sh` exists and `.woodpecker.yml` at
`packages/mosaic/framework/` (or monorepo root) wires it as a blocking CI step.
2. The gate must cover `*.sh` files (see RISK-01) in addition to `*.md`.
3. Test coverage for the gate itself: the gate must be able to detect a planted
`jarvis-brain` token and fail. Without a self-test, the gate can silently no-op
on a grep syntax error.
---
## RISK-03 — MAJOR: Personal operator data still live in four shipped guide files and the `TOOLS.md` default; the synthesis plan misses them
**Severity:** Major
**Files with surviving contamination:**
- `guides/ORCHESTRATOR.md:99,111,152` — three references to `jarvis-brain/docs/templates/`
(synthesis §2b explicitly calls for these to be fixed, but they still exist in the working
copy at time of review)
- `guides/ORCHESTRATOR-LEARNINGS.md:127``jarvis-brain/data/orchestrator-metrics.json`
(not in the synthesis fix list)
- `guides/ORCHESTRATOR-PROTOCOL.md:4` — "Distilled from `jarvis-brain/docs/protocols/ORCHESTRATOR-PROTOCOL.md`"
(not in the synthesis fix list)
- `guides/TOOLS-REFERENCE.md:149,182,226` — three jarvis-brain references including a
`MANDATORY jarvis-brain rule` block and `$HOME/src/jarvis-brain/tools/excalidraw_export/`
(not in the synthesis fix list)
- `defaults/TOOLS.md:40-44``MANDATORY jarvis-brain rule` block verbatim
- `defaults/README.md:72``mosaic init --non-interactive --name Jarvis --user-name Jason --timezone America/Chicago`
(a named example using private personal data)
- `defaults/AGENTS.md:37``(Policy: Jason, 2026-06-11.)` at end of Gate 13
- `tools/qa/prevent-memory-write.sh:29` — hardcoded `https://brain.woltje.com/v1/thoughts`
(a private domain; this hook ships executable in every install)
- `tools/_scripts/mosaic-doctor:312``mosaic-jarvis` in the shipped skill list
**Why this is major and not blocker:** None of these individually break the framework's
functionality for a downstream user. But collectively, they mean a new adopter's first
`mosaic-doctor` run, first OpenBrain error, or first guide read will surface private data.
More critically, the `prevent-memory-write.sh` hook prints `https://brain.woltje.com/v1/thoughts`
in the agent's face every time it blocks a memory write — which happens constantly. Every
user who installs the hook gets an error message pointing to a private individual's domain.
The `verify-sanitized.sh` gate as specified in synthesis D6 excludes `examples/` but must
also catch the guide and tool files listed here. The grep pattern `jarvis|jason|woltje|\bPDA\b`
will catch these, but only if the gate actually runs against `guides/`, `defaults/`, and
`tools/` — confirm the exclusion list does not inadvertently omit these directories.
**Mitigation:**
1. Replace `brain.woltje.com` in `prevent-memory-write.sh` with
`${OPENBRAIN_URL:-https://brain.your-mosaic-instance.dev}/v1/thoughts` and document
the env var in the generated `TOOLS.md`.
2. Purge the four guide-level `jarvis-brain` references in ORCHESTRATOR.md, ORCHESTRATOR-
PROTOCOL.md, ORCHESTRATOR-LEARNINGS.md, and TOOLS-REFERENCE.md.
3. Remove `MANDATORY jarvis-brain rule` block from `defaults/TOOLS.md` — this is
operator-specific memory protocol that belongs in the operator's generated `TOOLS.md`
or project `AGENTS.md`.
4. Fix `defaults/README.md:72` to use placeholder names.
5. Remove `(Policy: Jason, 2026-06-11.)` from `defaults/AGENTS.md:37` gate 13 —
the synthesis identifies this as operator policy that must leave L0 (D1 rationale).
6. Remove `mosaic-jarvis` from the `mosaic-doctor` skill list or replace with
`mosaic-agent` (a framework-generic skill name).
---
## RISK-04 — MAJOR: The `PRESERVE_PATHS` list in `install.sh:24` includes `AGENTS.md` and `STANDARDS.md`; removing them is the literal drift bug fix, but `install.sh` is not updated
**Severity:** Major
**File:** `packages/mosaic/framework/install.sh:24`
```bash
PRESERVE_PATHS=("AGENTS.md" "SOUL.md" "USER.md" "TOOLS.md" "STANDARDS.md" "memory" "sources" "credentials")
```
The synthesis calls removing `AGENTS.md` and `STANDARDS.md` from this list "the single
change that makes gate updates reach every existing install" (§5.1, D4). The v3 migration
stub in `install.sh` is a comment: `# ── Future migrations go here ──` at line 198. The
actual change has not been applied.
**Consequence:** Until this line is changed, every `keep`-mode upgrade (`INSTALL_MODE=keep`,
the default for existing installs at `install.sh:99`) silently skips overwriting
`AGENTS.md` and `STANDARDS.md`. A user who installed v1 and runs upgrade will get
framework updates to everything except the two files carrying the hard gates. The bugs
the architecture is designed to fix will not reach existing deployments.
**Secondary issue:** The seeding logic at `install.sh:235-241` seeds `AGENTS.md`,
`STANDARDS.md`, and `TOOLS.md` from `defaults/` only when they do not yet exist. If
`CONSTITUTION.md` is introduced as a new file (synthesis §2a), it needs to be added to
this seeding block — otherwise the first upgrade will skip seeding it for fresh installs
that happen before `CONSTITUTION.md` is in `PRESERVE_PATHS`.
**Mitigation:**
1. Change `PRESERVE_PATHS` line to remove `"AGENTS.md"` and `"STANDARDS.md"`.
2. Add v3 migration block that (a) snapshots `~/.config/mosaic/` to
`~/.config/mosaic/.backup-v2/` (synthesis §5.4), (b) seeds `CONSTITUTION.md`
as a new file, (c) removes `AGENTS.md`/`STANDARDS.md` from any PRESERVE record.
3. Add `CONSTITUTION.md` to the seeding block at line 235 alongside `AGENTS.md`.
4. Run the three-fixture migration test matrix before tagging alpha (synthesis §5.5):
fresh install, legacy-flat user-edited install, user-tuned-standard install —
with no interactive prompt and no hang.
---
## RISK-05 — MAJOR: `install.sh` blocks on interactive prompt in non-TTY environments; the three-fixture migration test cannot pass criterion 3 of "no hang"
**Severity:** Major
**File:** `packages/mosaic/framework/install.sh:84-101`
```bash
case "$INSTALL_MODE" in
keep|overwrite) ;;
prompt)
if [[ -t 0 ]]; then # <-- only interactive if TTY
...
read -r selection # BLOCKS
else
INSTALL_MODE="keep" # silently defaults to keep
fi
;;
esac
```
When running in non-TTY (CI, headless, piped installs) the installer silently defaults
to `keep`. This means a CI smoke test that upgrades from v2 to v3 will silently not
overwrite `AGENTS.md` and `STANDARDS.md` unless `MOSAIC_INSTALL_MODE=overwrite` is
explicitly passed. The synthesis migration plan (§5.5) requires that fixture 2
(legacy-flat user-edited install) proves "law moves, user files survive" — but the
default non-TTY behavior will quietly preserve the old `AGENTS.md`, and the test will
pass even though the gate update did not reach the install. The test matrix will produce
a false green.
**Similarly, `mosaic-init`** has the same pattern (`tools/_scripts/mosaic-init:100-107`):
when `NON_INTERACTIVE=0` and a value is missing, it prompts and reads from stdin, which
hangs in CI unless `--non-interactive` is passed.
**Mitigation:**
1. The alpha CI smoke test MUST pass `MOSAIC_INSTALL_MODE=overwrite` or `keep`
explicitly — never rely on the `prompt` default.
2. Document the required env vars for headless upgrade in `CONTRIBUTING.md`.
3. For the three-fixture test matrix, pin fixture 2 to `MOSAIC_INSTALL_MODE=keep` to
exercise the preserve/overwrite split under the exact conditions a user upgrade uses.
---
## RISK-06 — MAJOR: The `.local.md` overlay compose mechanism is entirely absent; the upgrade-safety guarantee is unimplementable until it exists
**Severity:** Major
The synthesis resolves DQ3 (upgrade-safe customization) by specifying `SOUL.local.md`,
`USER.local.md`, and `STANDARDS.local.md` as additive overlays composed by
`mosaic compose-contract <harness>` before injection (§5.2, D4). No such script or
mechanism exists in `tools/_scripts/`. No `*.local.md` file handling appears anywhere
in the framework codebase:
```
grep -rn "\.local\.md|local_overlay|local-overlay" packages/mosaic/framework/ -- (zero results)
```
The synthesis explicitly defers 3-way merge and relies on `.local` overlays as the
*only* upgrade-safe customization path for L1 (`STANDARDS.md`). Without the overlay
composer, a user who wants to tighten `STANDARDS.md` has two options: (a) edit
`STANDARDS.md` directly and lose the change on the next upgrade (the bug the whole
architecture is meant to fix), or (b) do nothing. The alpha ships with no working
customization path for L1.
This is not blocked by the `CONSTITUTION.md` extraction — overlays are a separate
mechanism — but it must exist before the alpha tags or the upgrade-safety promise is
marketing copy, not engineering.
**Mitigation:**
1. Add `mosaic compose-contract` (or equivalent) to `tools/_scripts/` before alpha tag.
Minimum viable: a script that concatenates `$MOSAIC_HOME/STANDARDS.md` +
`$MOSAIC_HOME/STANDARDS.local.md` (if present) into a temp file and injects it.
2. Update `install.sh` to document the `.local.md` convention and create empty
`STANDARDS.local.md.example` so users know the escape hatch exists.
3. The LAYER-MODEL.md governance spec should explicitly enumerate which files are
overlay-eligible and which are not (to prevent users from creating
`CONSTITUTION.local.md` and expecting it to work).
---
## RISK-07 — MAJOR: `defaults/AGENTS.md:11` contains the false claim the synthesis explicitly flags as a known bug — it is still present and still teaches agents to skip the gates
**Severity:** Major
**File:** `packages/mosaic/framework/defaults/AGENTS.md:11`
> "The core contract is ALREADY in your context (injected by `mosaic` launch). Do not re-read it."
The synthesis (§0, settled point 9) names fixing this false unconditional claim as settled
and required. The file still contains it verbatim. On a bare `claude` launch (Tier-3,
synthesis §6), `AGENTS.md` is the self-load fallback — the agent reads it, hits line 11,
and is told the contract is already in context when it demonstrably is not. The agent
skips the self-load of `CONSTITUTION.md` (once extracted) because the file it just read
told it not to. This is the exact failure mode the self-bootstrap fallback exists to prevent.
The synthesis fix is precise (synthesis §1, "If `CONSTITUTION.md` is not already in your
context, READ IT NOW" — conditional, not unconditional). The implementation has not
happened.
**Mitigation:**
Replace `defaults/AGENTS.md:10-11`:
```
The core contract is ALREADY in your context (injected by `mosaic` launch). Do not re-read it.
```
with the conditional self-bootstrap line:
```
If `~/.config/mosaic/CONSTITUTION.md` is not already in your context, READ IT NOW before proceeding.
```
This is a one-line change that closes a meaningful gate-skip path.
---
## RISK-08 — MAJOR: The `rails/` dead path appears in 60 template occurrences; templates are user-facing, and bootstrapped repos inherit broken wrapper commands
**Severity:** Major
**Count:** 60 lines across template files (confirmed by grep):
- `templates/agent/AGENTS.md.template` (6 occurrences)
- `templates/agent/projects/typescript/CLAUDE.md.template` (5 occurrences)
- `templates/agent/projects/django/CLAUDE.md.template` (5 occurrences)
- `templates/agent/projects/nestjs-nextjs/AGENTS.md.template` (multiple)
- Plus `templates/agent/projects/python-fastapi/`, `python-library/`
The installer (`install.sh:192-194`) removes `rails/` from the deployed config:
```bash
if [[ -L "$TARGET_DIR/rails" ]]; then
rm -f "$TARGET_DIR/rails"
fi
```
But every project bootstrapped via `mosaic-bootstrap-repo` using these templates will
receive the dead path `~/.config/mosaic/rails/git/ci-queue-wait.sh` baked into its
`AGENTS.md` or `CLAUDE.md`. When the agent tries to run the queue guard — a HARD GATE
(synthesis hard gate #6) — it fails. Gate #8 says: "if any required wrapper command fails,
status is blocked; stop." The agent stops and reports a failure on a dead path that
ships in the framework.
The synthesis identifies this (§0, verified live fact; §2b fix). The implementation has
not happened.
**Mitigation:**
A global sed/find-replace of `rails/git/``tools/git/` and `rails/codex/``tools/codex/`
across all template files. This is a mechanical change, low risk, and must be in the alpha.
The CI gate (`verify-sanitized.sh`) should include `/rails/` in its dead-path grep.
---
## RISK-09 — MINOR: The `defaults/STANDARDS.md:5` "Master/slave model" framing ships to public package and conflicts with OSS community norms
**Severity:** Minor
**File:** `packages/mosaic/framework/defaults/STANDARDS.md:5`
> "Master/slave model:
> - Master: `~/.config/mosaic` (this framework)
> - Slave: each repo bootstrapped via `mosaic-bootstrap-repo`"
The synthesis (§2b) explicitly calls for dropping this framing ("drop the 'Master/slave
model' framing (line 5)"). The implementation has not happened. For a public OSS package,
this is a contribution-chilling issue that will surface in the first community PR review.
It is not a security issue but it is a hygiene issue that the synthesis already resolved
and that costs one line to fix.
**Mitigation:** Replace with "Primary / satellite model" or "Framework / project model".
---
## RISK-10 — MINOR: No LICENSE file exists anywhere in the monorepo; every contribution is all-rights-reserved under Berne until this is fixed
**Severity:** Minor (but legally time-sensitive)
**Confirmed:** `find /home/jwoltje/src/_ms_stack/ -maxdepth 3 -name "LICENSE"` — zero results.
`packages/mosaic/package.json` has no `"license"` field.
The synthesis (D8) makes this a blocking release requirement with correct rationale:
"An unlicensed public repo is all-rights-reserved under Berne; retroactively licensing
after the alpha creates ambiguity about the pre-license period." The synthesis chose MIT.
This is ranked minor only because it does not break runtime behavior, but the legal
window to fix it cleanly closes at the alpha tag. Post-tag contribution history will have
unclear IP status. This is the easiest fix on this list: two files and a `package.json`
field.
**Mitigation:** Add `LICENSE` (MIT) at monorepo root, `packages/mosaic/framework/LICENSE`,
and `"license": "MIT"` in `package.json` before any alpha tag. Ship `CONTRIBUTING.md`
with the operator-data-hygiene section.
---
## RISK-11 — MINOR: Cross-harness smoke test required by synthesis (§6) does not exist; the "enforced across harnesses" claim is aspirational, not testable
**Severity:** Minor
The synthesis (D5) requires "a CI smoke test [that] launches each harness path and asserts
the irreducible gates are present in the effective context." No such test exists in
`tools/quality/` or anywhere in the framework tree. The existing `tools/quality/scripts/verify.sh`
tests TypeScript lint/type/gitleaks — not gate residency.
Without this test, the cross-harness claim is documentation. An agent running on OpenCode
or bare `claude` with a stale pointer can operate without the gates and no CI check will
catch it. The synthesis correctly ranks this as necessary for the alpha claim to be true.
**Mitigation:** This is a legitimate post-alpha-tag risk for the alpha window. A minimal
smoke test that reads the deployed `AGENTS.md`, executes the conditional self-load line,
and asserts that the gate keywords (`PR-review-before-merge`, `green CI`, `no forced
merges`, `completion-defined-at-end`, `block-vs-done`) appear in the resolved context would
close this. Mark as a tracked gap if not achievable before alpha, but the gap must be
explicit in the compliance matrix (synthesis D5).
---
## Interaction Effects
Two risks compound: RISK-02 (no `verify-sanitized.sh`) + RISK-03 (surviving contamination
in guides/tools) means the sanitization story is wrong at two levels simultaneously —
the surviving tokens will not be caught even after the gate is built, unless the gate's
grep scope covers `tools/*.sh` and `guides/*.md`. Fix RISK-02 and RISK-03 together.
RISK-04 (PRESERVE_PATHS not updated) + RISK-06 (no overlay composer) means that even
after the v3 migration runs, users cannot safely customize L1 (STANDARDS) without
losing changes on the next upgrade. These must ship together.
RISK-01 (credential path in three scripts) + RISK-02 (gate scope misses `*.sh`) means
the CI gate will not catch the credential path leak even once the gate exists. The gate
scope fix and the credential path fix are co-dependent.
---
## Summary Table
| Risk | Severity | One-liner |
|------|----------|-----------|
| RISK-01 | Blocker | Three shipped scripts default to `$HOME/src/jarvis-brain/credentials.json`; synthesis misses `stack-health.sh` |
| RISK-02 | Blocker | `verify-sanitized.sh` does not exist; no CI gate wires it; the sanitization guarantee is prose |
| RISK-03 | Major | Surviving personal data in 9+ shipped files; synthesis fix list is incomplete |
| RISK-04 | Major | `PRESERVE_PATHS` still includes `AGENTS.md`/`STANDARDS.md`; drift bug not fixed |
| RISK-05 | Major | Non-TTY install silently defaults to `keep`; migration test matrix will false-green |
| RISK-06 | Major | `.local.md` overlay compose mechanism does not exist; upgrade-safety guarantee unimplementable |
| RISK-07 | Major | `AGENTS.md:11` still says "ALREADY in context — do not re-read"; gates are skippable on bare launch |
| RISK-08 | Major | 60 template lines still emit dead `rails/git/` paths; bootstrapped repos hit blocked gate on first run |
| RISK-09 | Minor | "Master/slave model" framing at `STANDARDS.md:5` ships to public |
| RISK-10 | Minor | No LICENSE file exists; legal window to fix cleanly closes at alpha tag |
| RISK-11 | Minor | Cross-harness smoke test does not exist; "enforced across harnesses" is aspirational |