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

22 KiB

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:19MOSAIC_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:23CRED_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:127jarvis-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-44MANDATORY jarvis-brain rule block verbatim
  • defaults/README.md:72mosaic 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:312mosaic-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

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

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:

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