chore: consolidate new foundation and archive v1 (#1495)

This commit is contained in:
2026-09-07 12:32:57 -05:00
3511 changed files with 727899 additions and 10 deletions
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# check-resident-budget.sh — resident line-count ceiling (R9 / DESIGN §7).
#
# Budgets the *container* (line count) of the framework-owned files that are
# injected into every agent's context by value — the Constitution (L0), the
# AGENTS dispatcher, and each runtime RUNTIME.md slice. Gate *wording* is never
# capped (a word cap forces paraphrasing law — the exact drift vector P3 killed);
# only the file's line count is bounded, so prose creep is caught in review.
#
# This is the CI-enforceable half of the budget. The per-harness *total* resident
# prompt (which also includes user-generated SOUL.md/USER.md and the per-tier
# slice) is summed by `mosaic doctor` as a runtime advisory — CI cannot see user
# files, so it is deliberately out of scope here (DESIGN §7).
#
# Usage: check-resident-budget.sh [--self-test]
# Exit: 0 = all within budget · 1 = a file exceeds its ceiling · 2 = self-test failed
set -uo pipefail
FW="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" # packages/mosaic/framework
# Per-file ceilings (lines). Headroom above current counts; tighten as files settle.
# Format: "<relative-path>:<max-lines>"
CEILINGS=(
"defaults/CONSTITUTION.md:120"
"defaults/AGENTS.md:120"
"runtime/claude/RUNTIME.md:90"
"runtime/codex/RUNTIME.md:90"
"runtime/opencode/RUNTIME.md:90"
"runtime/pi/RUNTIME.md:90"
)
# check_file <abs-path> <max> → echoes "<n>"; returns 0 if n<=max, 1 otherwise.
check_file() {
local path="$1" max="$2" n
n=$(wc -l <"$path" 2>/dev/null || echo 0)
n=$((n + 0))
echo "$n"
[ "$n" -le "$max" ]
}
run_budget() {
local fail=0 rel max abs n
printf '%-32s %8s %8s %s\n' "FILE" "LINES" "CEILING" "STATUS"
for entry in "${CEILINGS[@]}"; do
rel="${entry%%:*}"
max="${entry##*:}"
abs="$FW/$rel"
if [ ! -f "$abs" ]; then
printf '%-32s %8s %8s %s\n' "$rel" "-" "$max" "MISSING"
fail=1
continue
fi
n=$(check_file "$abs" "$max")
if [ "$n" -le "$max" ]; then
printf '%-32s %8s %8s %s\n' "$rel" "$n" "$max" "ok"
else
printf '%-32s %8s %8s %s\n' "$rel" "$n" "$max" "OVER BUDGET"
fail=1
fi
done
return "$fail"
}
self_test() {
local tmp rc
tmp=$(mktemp)
# 3 lines, ceiling 5 → within budget (rc 0)
printf 'a\nb\nc\n' >"$tmp"
check_file "$tmp" 5 >/dev/null
rc=$?
if [ "$rc" -ne 0 ]; then echo "self-test FAIL: under-budget file flagged"; rm -f "$tmp"; return 2; fi
# 6 lines, ceiling 5 → over budget (rc 1)
printf 'a\nb\nc\nd\ne\nf\n' >"$tmp"
check_file "$tmp" 5 >/dev/null
rc=$?
if [ "$rc" -ne 1 ]; then echo "self-test FAIL: over-budget file not flagged"; rm -f "$tmp"; return 2; fi
rm -f "$tmp"
echo "self-test OK"
return 0
}
if [ "${1:-}" = "--self-test" ]; then
self_test
exit $?
fi
if run_budget; then
echo "Resident budget: all framework-owned resident files within ceiling."
exit 0
else
echo "Resident budget EXCEEDED — trim prose or raise the ceiling deliberately (see DESIGN §7)." >&2
exit 1
fi
@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# check-test-enumeration.sh — CI test-membership guard (#1017).
#
# CI reaches shell suites through two hand-enumerated surfaces:
# S1 packages/mosaic/package.json scripts."test:framework-shell"
# S2 .woodpecker/ci.yml direct `bash packages/mosaic/framework/tools/...` commands
#
# A hand-enumerated allowlist re-arms its own gap: a new suite never auto-joins,
# so the list silently under-runs the disk (17 of 39 suites were invisible when
# #1017 was filed). This guard makes that under-run impossible to do silently:
#
# FAIL when a suite-shaped file exists on disk and is neither enumerated on
# the UNION of both surfaces nor listed in the exclusions file.
# ("Enumerated", deliberately — F1/F2 on PR #1018 proved this guard sees
# NAMING, not reachability, and its words must not claim otherwise.)
# FAIL when either surface names a path that does not exist on disk
# (a rename manufactures a stale entry silently — checked BOTH directions).
# FAIL when an exclusion entry has no reason, names a path that is gone,
# names a path that is also enumerated (contradiction), or names a path
# outside the population (dead weight that looks like coverage).
#
# POPULATION PATTERN — a deliberate decision, stated per #1017's record:
# basename matches *test*.sh (contains "test", ends ".sh"). Deliberately BROAD:
# the strict `test-*.sh` prefix cannot even name three real boundary files
# (tmux/agent-send.test.sh — CI-run; orchestrator/smoke-test.sh;
# wake/validate-973/microtest-wake-assert.sh), and three independent censuses
# handled that last file three different ways with no trace of the judgement.
# The broad pattern makes such files MEMBERS, so their disposition must be a
# signed exclusion, not an accident of the glob. The SAME pattern is applied to
# both sides of the comparison (disk and enumeration) — a comparison globbed two
# ways runs on two different populations. Scripts outside the pattern on both
# sides symmetrically (e.g. check-resident-budget.sh, verify-sanitized.sh) are
# check-scripts, not suites; their existence is still verified via the
# both-directions rule because every surface-named path must exist on disk.
#
# The surfaces are PARSED, never line-ranged: three seats independently
# mis-scoped hand-written line ranges against these files (#1017 thread). S1 is
# read via JSON + command-chain tokenization; S2 by extracting every
# packages/mosaic/framework/tools/ token wherever it appears in the file.
#
# Exclusions file format (framework/tools/quality/test-enumeration-exclusions.txt):
# <repo-relative-path> | <non-empty reason>
# Lines starting with # and blank lines are ignored. An exclusion is a recorded
# decision someone signed, not an omission nobody made.
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/../../../../../.." && pwd)"
while (( $# )); do
case "$1" in
--root) ROOT="$(cd "$2" && pwd)"; shift 2 ;;
*) echo "usage: check-test-enumeration.sh [--root <repo-root>]" >&2; exit 2 ;;
esac
done
PKG_JSON="$ROOT/packages/mosaic/package.json"
CI_YML="$ROOT/.woodpecker/ci.yml"
TOOLS_DIR="$ROOT/packages/mosaic/framework/tools"
EXCLUSIONS="$TOOLS_DIR/quality/test-enumeration-exclusions.txt"
for f in "$PKG_JSON" "$CI_YML"; do
[[ -f "$f" ]] || { echo "FAIL: required surface file missing: $f" >&2; exit 2; }
done
[[ -d "$TOOLS_DIR" ]] || { echo "FAIL: tools dir missing: $TOOLS_DIR" >&2; exit 2; }
fail_count=0
fail() { printf 'FAIL %s\n' "$1"; fail_count=$(( fail_count + 1 )); }
# in_population <repo-relative path> — the single pattern, used for BOTH sides.
in_population() {
local base; base="$(basename "$1")"
[[ "$base" == *test*.sh ]]
}
# --- Surface 1: package.json test:framework-shell, parsed, repo-relative -----
# Tokens are script paths iff they contain "/" and end .sh/.py; interpreter
# names and flags are skipped. Paths are relative to packages/mosaic/.
mapfile -t S1 < <(python3 - "$PKG_JSON" <<'PY'
import json, shlex, sys
cmd = json.load(open(sys.argv[1]))["scripts"].get("test:framework-shell", "")
seen = []
for seg in cmd.split("&&"):
for tok in shlex.split(seg):
if "/" in tok and (tok.endswith(".sh") or tok.endswith(".py")):
path = "packages/mosaic/" + tok
if path not in seen:
seen.append(path)
print("\n".join(seen))
PY
)
# --- Surface 2: ci.yml, every framework/tools token wherever it appears ------
# Comment lines (first non-whitespace char is #) are skipped BEFORE matching:
# commenting an invocation out is the most common way a suite actually gets
# disabled, and a raw-text regex would keep calling it enumerated (F1, 20155 on
# PR #1018 — demonstrated, not argued). Known residual limit: a path named only
# in a TRAILING comment on a live line still matches; no such line exists today
# and full fidelity would need a YAML parser the CI image does not ship.
mapfile -t S2 < <(grep -vE '^[[:space:]]*#' "$CI_YML" \
| grep -oE 'packages/mosaic/framework/tools/[A-Za-z0-9_./-]+\.(sh|py)' | sort -u)
# --- Union, and its population-restricted view -------------------------------
declare -A ENUM=() ENUM_POP=()
for p in "${S1[@]:-}" "${S2[@]:-}"; do
[[ -n "$p" ]] || continue
ENUM["$p"]=1
in_population "$p" && ENUM_POP["$p"]=1
done
# --- Direction B: every surface-named path must exist on disk ----------------
for p in "${!ENUM[@]}"; do
[[ -f "$ROOT/$p" ]] || fail "STALE ENUMERATION: surfaces name '$p' but it does not exist on disk"
done
# --- Exclusions: parsed with the same rigor the enumeration gets -------------
declare -A EXCLUDED=()
if [[ -f "$EXCLUSIONS" ]]; then
lineno=0
while IFS= read -r line; do
lineno=$(( lineno + 1 ))
[[ "$line" =~ ^[[:space:]]*(#|$) ]] && continue
path="${line%%|*}"; reason="${line#*|}"
path="$(echo "$path" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
reason="$(echo "$reason" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
if [[ "$line" != *"|"* || -z "$reason" ]]; then
fail "EXCLUSION MISSING REASON: line $lineno ('$path') — an exclusion is a recorded decision someone signed"
continue
fi
if [[ ! -f "$ROOT/$path" ]]; then
fail "STALE EXCLUSION: line $lineno excludes '$path' which does not exist on disk"
continue
fi
if ! in_population "$path"; then
fail "EXCLUSION OUTSIDE POPULATION: line $lineno excludes '$path' which the population pattern does not name — dead weight that reads as coverage"
continue
fi
if [[ -n "${ENUM[$path]:-}" ]]; then
fail "CONTRADICTORY EXCLUSION: line $lineno excludes '$path' which the surfaces already enumerate"
continue
fi
EXCLUDED["$path"]=1
done < "$EXCLUSIONS"
fi
# --- Direction A: disk population must be enumerated or signed-excluded ------
disk_total=0
unlisted=0
while IFS= read -r f; do
rel="${f#"$ROOT"/}"
in_population "$rel" || continue
disk_total=$(( disk_total + 1 ))
if [[ -z "${ENUM_POP[$rel]:-}" && -z "${EXCLUDED[$rel]:-}" ]]; then
fail "UNENUMERATED: '$rel' exists on disk but is neither enumerated on any CI surface nor signed in the exclusions file"
unlisted=$(( unlisted + 1 ))
fi
done < <(find "$TOOLS_DIR" -type f -name '*.sh' | sort)
if (( fail_count > 0 )); then
printf 'enumeration guard: %d failure(s) — population %d, enumerated (in-population) %d, excluded %d\n' \
"$fail_count" "$disk_total" "${#ENUM_POP[@]}" "${#EXCLUDED[@]}"
exit 1
fi
printf 'enumeration guard: OK — population %d, enumerated (in-population) %d, excluded (signed) %d, surfaces name %d path(s), all present on disk\n' \
"$disk_total" "${#ENUM_POP[@]}" "${#EXCLUDED[@]}" "${#ENUM[@]}"
@@ -0,0 +1,293 @@
#!/usr/bin/env bash
# check-tools-index.sh — assert every shipped tool is discoverable from the
# resident documentation an agent actually has in context.
#
# WHY THIS GATE EXISTS
# --------------------
# The framework ships 26 git wrappers. Before this gate, 20 of them were named
# in neither `defaults/TOOLS.md` nor `guides/TOOLS-REFERENCE.md`. One of the
# undocumented ones was `pr-review.sh` — the wrapper that carries the
# APPROVED/APPROVE provider-dialect split.
#
# The observable consequence, on a live fleet host: an agent needing to place a
# review verdict reached for raw `curl`, sent GitHub's `APPROVE` to a Gitea
# host, and got HTTP 200 with the review silently filed PENDING — three times,
# because nothing about the failure pointed at the wrapper that already handled
# it correctly. The agent was not ignoring Constitution gate 7. It was obeying
# an index that said the tool did not exist.
#
# That is not a discipline problem and no amount of prose fixes it. A wrapper
# that is not in the resident index is, from inside a session, indistinguishable
# from a wrapper that was never written. So the invariant is mechanical:
#
# shipping a tool and documenting it are the same commit, or CI fails.
#
# WHAT IT CHECKS
# --------------
# forward every non-excluded tool in an ENFORCED suite is named in at least
# one index document (missing tool -> undiscoverable -> FAIL)
# reverse every `<name>.sh` an index document attributes to an enforced
# suite exists on disk (stale reference -> agent runs a ghost -> FAIL)
#
# Suites outside the enforced set are reported with a coverage percentage but do
# not fail the build, so the ratchet can be tightened one suite per PR instead of
# landing as one unreviewable sweep. `--strict` fails on those too.
#
# WHY THE ENFORCED LIST LIVES HERE AND NOT IN A MARKER INSIDE THE DOC
# -------------------------------------------------------------------
# `TOOLS.md` is operator-owned (see framework-manifest.txt). A marker inside it
# would let an operator silence this gate by editing their own copy — the gate
# would then be strongest exactly where it is least needed and absent where it
# is needed most. The list is framework-owned and changes only through a
# reviewed PR.
#
# Usage:
# check-tools-index.sh [--tools-dir DIR] [--doc FILE]... [--strict] [--self-test]
#
# Exit: 0 = every enforced suite fully discoverable · 1 = drift · 2 = bad usage
set -euo pipefail
# Suites whose coverage is a HARD requirement. Add a suite here only together
# with the doc changes that make it pass.
#
# `git` is first because it is the suite Constitution gates 6-8 make mandatory:
# an undiscoverable git wrapper converts a hard gate into a coin flip.
ENFORCED_SUITES=(git)
# Files that are not agent-callable tools and must not be required in an index.
EXCLUDE_GLOBS=(
'test-*' # hermetic regression scripts, invoked by CI not by agents
'_*' # private helpers (_lib, _scripts internals)
'*.bak' # editor/installer debris
'*.pre-*' # pre-change backups (e.g. ci-queue-wait.sh.pre-404fix-bak)
'README.md'
)
STRICT=0
SELF_TEST=0
TOOLS_DIR=""
DOCS=()
die() { printf 'check-tools-index: %s\n' "$*" >&2; exit 2; }
while [ $# -gt 0 ]; do
case "$1" in
--tools-dir) TOOLS_DIR="${2:-}"; shift 2 ;;
--doc) DOCS+=("${2:-}"); shift 2 ;;
--strict) STRICT=1; shift ;;
--self-test) SELF_TEST=1; shift ;;
-h|--help) sed -n '2,48p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) die "unknown argument: $1" ;;
esac
done
# ---- location resolution ---------------------------------------------------
# Runs from two places with different layouts, and must not silently check the
# wrong tree: a CI checkout (repo-relative) and an installed host ($MOSAIC_HOME).
resolve_locations() {
local here framework
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
# .../framework/tools/quality/scripts -> .../framework
framework="$(cd -- "$here/../../.." && pwd)"
if [ -z "$TOOLS_DIR" ]; then
if [ -d "$framework/tools" ]; then
TOOLS_DIR="$framework/tools"
else
TOOLS_DIR="${MOSAIC_HOME:-$HOME/.config/mosaic}/tools"
fi
fi
if [ ${#DOCS[@]} -eq 0 ]; then
# The two layouts are mutually exclusive on purpose. Unioning them would let
# a well-maintained operator TOOLS.md on the developer's own machine mask a
# gap in the shipped defaults — the check would pass locally and the defect
# would still install on every other host. Repo layout wins when present.
if [ -f "$framework/defaults/TOOLS.md" ]; then
DOCS+=("$framework/defaults/TOOLS.md")
[ -f "$framework/guides/TOOLS-REFERENCE.md" ] && DOCS+=("$framework/guides/TOOLS-REFERENCE.md")
else
local mosaic_home="${MOSAIC_HOME:-$HOME/.config/mosaic}"
[ -f "$mosaic_home/TOOLS.md" ] && DOCS+=("$mosaic_home/TOOLS.md")
[ -f "$mosaic_home/guides/TOOLS-REFERENCE.md" ] && DOCS+=("$mosaic_home/guides/TOOLS-REFERENCE.md")
fi
fi
[ -d "$TOOLS_DIR" ] || die "tools dir not found: $TOOLS_DIR"
[ ${#DOCS[@]} -gt 0 ] || die "no index documents found (pass --doc FILE)"
}
is_excluded() {
local name="$1" glob
for glob in "${EXCLUDE_GLOBS[@]}"; do
# shellcheck disable=SC2254 # glob is intentionally a pattern
case "$name" in $glob) return 0 ;; esac
done
return 1
}
# A tool counts as documented when its basename appears anywhere in the corpus.
# Deliberately permissive about *form* (table cell, code fence, prose) and strict
# about *presence*: the gate's job is "an agent can find it", not house style.
documented() { grep -qF -- "$1" "$CORPUS"; }
# ---- the check -------------------------------------------------------------
run_check() {
local rc=0 suite dir tool base enforced
CORPUS="$(mktemp)"; trap 'rm -f "$CORPUS"' RETURN
cat "${DOCS[@]}" > "$CORPUS"
printf 'tools: %s\n' "$TOOLS_DIR"
for d in "${DOCS[@]}"; do printf 'index: %s\n' "$d"; done
printf '\n'
for dir in "$TOOLS_DIR"/*/; do
[ -d "$dir" ] || continue
suite="$(basename -- "$dir")"
case " ${ENFORCED_SUITES[*]} " in *" $suite "*) enforced=1 ;; *) enforced=0 ;; esac
[ "$STRICT" -eq 1 ] && enforced=1
case "$suite" in _*) continue ;; esac
local total=0 found=0
local -a suite_missing=() suite_noexec=()
for tool in "$dir"*.sh; do
[ -e "$tool" ] || continue
base="$(basename -- "$tool")"
is_excluded "$base" && continue
total=$((total + 1))
if documented "$base"; then
found=$((found + 1))
# Documented AND present is not enough. The index presents these as
# commands to run, and every caller — the wrapper guard included —
# decides "is this tool here?" with `[ -x ]`. A 0644 wrapper is
# documented, present, and dead: it reads as absent to every check that
# matters while scoring 100% here. That is a false green, which is worse
# than a red, so it fails rather than warns.
[ -x "$tool" ] || suite_noexec+=("$base")
else
suite_missing+=("$base")
fi
done
[ "$total" -eq 0 ] && continue
local pct=$(( found * 100 / total ))
if [ "$enforced" -eq 1 ] && [ ${#suite_noexec[@]} -gt 0 ]; then
printf 'FAIL %-12s %3d%% (%d/%d) documented but not executable: %s\n' \
"$suite" "$pct" "$found" "$total" "${suite_noexec[*]}"
rc=1
fi
if [ "$enforced" -eq 1 ] && [ ${#suite_missing[@]} -gt 0 ]; then
printf 'FAIL %-12s %3d%% (%d/%d) undocumented: %s\n' \
"$suite" "$pct" "$found" "$total" "${suite_missing[*]}"
rc=1
elif [ "$enforced" -eq 1 ] && [ ${#suite_noexec[@]} -eq 0 ]; then
printf 'ok %-12s %3d%% (%d/%d) [enforced]\n' "$suite" "$pct" "$found" "$total"
else
printf 'info %-12s %3d%% (%d/%d) not yet enforced\n' "$suite" "$pct" "$found" "$total"
fi
# Reverse: an index that names a tool this suite does not have sends agents
# after something that cannot run. Only checked for enforced suites, where
# the naming is unambiguous enough to attribute.
if [ "$enforced" -eq 1 ]; then
local -a stale=()
local ref
while read -r ref; do
[ -n "$ref" ] || continue
is_excluded "$ref" && continue
[ -e "$dir$ref" ] || stale+=("$ref")
done < <(grep -oE "$suite/[a-z0-9][a-z0-9._-]*\.sh" "$CORPUS" \
| sed "s|^$suite/||" | sort -u)
if [ ${#stale[@]} -gt 0 ]; then
printf 'FAIL %-12s stale index references (no such file): %s\n' \
"$suite" "${stale[*]}"
rc=1
fi
fi
done
printf '\n'
if [ "$rc" -ne 0 ]; then
cat <<EOF
Undocumented tools are undiscoverable. An agent cannot obey a hard gate that
tells it to use a wrapper it has no way to learn exists — it will reach for raw
curl/gh/tea instead, and the wrapper's provider-dialect handling will be lost.
Fix by naming each tool above in one of the index documents listed at the top,
in the same commit that ships it.
EOF
else
printf 'every enforced suite is fully discoverable.\n'
fi
return "$rc"
}
# ---- self-test -------------------------------------------------------------
# Proves the gate can actually fail. A checker that only ever passes is
# indistinguishable from one that is not running, which is the failure mode this
# whole file exists to prevent — so it must demonstrate a red on demand.
self_test() {
local tmp rc
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' RETURN
mkdir -p "$tmp/tools/git"
printf '#!/bin/sh\n' > "$tmp/tools/git/documented-tool.sh"
printf '#!/bin/sh\n' > "$tmp/tools/git/test-ignored.sh"
chmod +x "$tmp/tools/git/documented-tool.sh" "$tmp/tools/git/test-ignored.sh"
# run_check reads the TOOLS_DIR / DOCS globals; an array cannot ride in a
# command-prefix assignment, so point the globals at the fixture directly.
TOOLS_DIR="$tmp/tools"
DOCS=("$tmp/doc.md")
# Case 1: fully documented -> pass.
printf 'see tools/git/documented-tool.sh for details\n' > "$tmp/doc.md"
if run_check >/dev/null; then
printf 'self-test 1/4 ok (complete index passes)\n'
else
printf 'self-test 1/4 FAIL (complete index should pass)\n'; return 1
fi
# Case 2: an undocumented tool -> fail.
printf '#!/bin/sh\n' > "$tmp/tools/git/undocumented-tool.sh"
rc=0; run_check >/dev/null || rc=$?
if [ "$rc" -eq 1 ]; then
printf 'self-test 2/4 ok (undocumented tool fails the gate)\n'
else
printf 'self-test 2/4 FAIL (undocumented tool should fail, got rc=%s)\n' "$rc"; return 1
fi
# Case 3: a stale index reference -> fail.
rm "$tmp/tools/git/undocumented-tool.sh"
printf 'also tools/git/deleted-tool.sh\n' >> "$tmp/doc.md"
rc=0; run_check >/dev/null || rc=$?
if [ "$rc" -eq 1 ]; then
printf 'self-test 3/4 ok (stale index reference fails the gate)\n'
else
printf 'self-test 3/4 FAIL (stale reference should fail, got rc=%s)\n' "$rc"; return 1
fi
# Case 4: documented, present, and NOT executable -> fail. Found by an
# independent reviewer: a 0644 wrapper scored 100% here while reading as
# absent to every `[ -x ]` in the fleet, including the wrapper guard's.
sed -i '/deleted-tool/d' "$tmp/doc.md"
printf '#!/bin/sh\n' > "$tmp/tools/git/noexec-tool.sh"
chmod 0644 "$tmp/tools/git/noexec-tool.sh"
printf 'and tools/git/noexec-tool.sh\n' >> "$tmp/doc.md"
rc=0; run_check >/dev/null || rc=$?
if [ "$rc" -eq 1 ]; then
printf 'self-test 4/4 ok (documented but non-executable tool fails the gate)\n'
else
printf 'self-test 4/4 FAIL (non-executable tool should fail, got rc=%s)\n' "$rc"; return 1
fi
printf '\nself-test passed: the gate demonstrably reds on every drift direction.\n'
}
if [ "$SELF_TEST" -eq 1 ]; then
self_test
else
resolve_locations
run_check
fi
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""Fail-closed comparison of deployed Mosaic tools to manifest-owned shipped tools."""
from __future__ import annotations
import argparse
import hashlib
import os
from pathlib import Path
import stat
import subprocess
import sys
def digest(path: Path) -> str:
value = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
value.update(chunk)
return value.hexdigest()
def default_source_tools() -> Path:
return Path(__file__).resolve().parents[2]
def normalize_source(path: Path) -> Path:
candidate = path.resolve()
return candidate / "tools" if (candidate / "tools").is_dir() else candidate
def assert_traversable_directory(path: Path) -> None:
mode = stat.S_IMODE(path.stat(follow_symlinks=False).st_mode)
# At least one principal class must have both read and search. This catches
# mode-000 even for privileged reviewers for whom os.access() would lie.
if not any(mode & read and mode & execute for read, execute in ((0o400, 0o100), (0o040, 0o010), (0o004, 0o001))):
raise PermissionError(f"directory has no readable/searchable mode: {path}")
def census(root: Path, *, reject_symlinks: bool) -> dict[str, Path]:
result: dict[str, Path] = {}
def onerror(error: OSError) -> None:
raise error
for current, directories, filenames in os.walk(root, topdown=True, followlinks=False, onerror=onerror):
current_path = Path(current)
assert_traversable_directory(current_path)
for name in directories:
entry = current_path / name
if entry.is_symlink() and reject_symlinks:
# A source symlink makes the shipped census incomplete. Deployed
# aliases are assessed later only when they occupy a required
# framework path; installed-only aliases remain operator state.
raise OSError(f"symlinked directory is not an independent census entry: {entry}")
for name in filenames:
entry = current_path / name
if entry.is_symlink():
if reject_symlinks:
raise OSError(f"symlinked file is not an independent census entry: {entry}")
result[entry.relative_to(root).as_posix()] = entry
continue
mode = entry.stat(follow_symlinks=False).st_mode
if not stat.S_ISREG(mode):
raise OSError(f"non-regular census entry: {entry}")
if stat.S_IMODE(mode) & 0o444 == 0:
raise PermissionError(f"file has no readable mode: {entry}")
result[entry.relative_to(root).as_posix()] = entry
return result
def classify_with_manifest(source: Path, relatives: list[str]) -> dict[str, str]:
framework = source.parent
manifest = framework / "framework-manifest.txt"
resolver = source / "_lib" / "manifest.sh"
if not manifest.is_file() or not os.access(manifest, os.R_OK):
raise OSError(f"ownership manifest is missing or unreadable: {manifest}")
if not resolver.is_file() or not os.access(resolver, os.R_OK):
raise OSError(f"canonical manifest resolver is missing or unreadable: {resolver}")
payload = "".join(f"tools/{relative}\n" for relative in relatives)
completed = subprocess.run(
["bash", str(resolver), "classify"],
input=payload,
text=True,
capture_output=True,
check=False,
env={**os.environ, "MANIFEST_FILE": str(manifest)},
)
if completed.returncode != 0:
detail = completed.stderr.strip() or f"resolver rc={completed.returncode}"
raise OSError(f"ownership manifest failed canonical resolution: {detail}")
classified: dict[str, str] = {}
for line in completed.stdout.splitlines():
ownership, separator, manifest_path = line.partition("\t")
if not separator or not manifest_path.startswith("tools/") or ownership not in {"framework", "operator"}:
raise OSError(f"invalid canonical ownership output: {line!r}")
relative = manifest_path.removeprefix("tools/")
if relative in classified:
raise OSError(f"duplicate canonical ownership output: {relative}")
classified[relative] = ownership
if set(classified) != set(relatives):
raise OSError("canonical ownership output did not classify the complete source census")
return classified
def has_symlinked_component(root: Path, relative: str) -> bool:
current = root
for component in Path(relative).parts:
current = current / component
if current.is_symlink():
return True
return False
def main() -> int:
parser = argparse.ArgumentParser(description="Detect deployed Mosaic framework-tool drift")
parser.add_argument("--source-root", type=Path, default=Path(os.environ["MOSAIC_FRAMEWORK_SOURCE_ROOT"]) if os.environ.get("MOSAIC_FRAMEWORK_SOURCE_ROOT") else default_source_tools())
parser.add_argument("--installed-root", type=Path, default=Path(os.environ.get("MOSAIC_HOME", Path.home() / ".config/mosaic")) / "tools")
parser.add_argument("--verbose", action="store_true")
args = parser.parse_args()
source = normalize_source(args.source_root)
installed = args.installed_root.resolve()
try:
if not source.is_dir():
raise OSError(f"source tools missing: {source}")
if not installed.is_dir():
raise OSError(f"installed tools missing: {installed}")
if source.samefile(installed):
raise OSError("source and installed roots identify the same filesystem object")
source_files = census(source, reject_symlinks=True)
if not source_files:
raise OSError("source tools census is empty")
ownership = classify_with_manifest(source, sorted(source_files))
required = sorted(relative for relative, owner in ownership.items() if owner == "framework")
if not required:
raise OSError("ownership manifest classifies zero shipped tools as framework-owned")
installed_files = census(installed, reject_symlinks=False)
except (OSError, PermissionError) as error:
print(f"[framework-drift] CANNOT_ASSERT {error}", file=sys.stderr)
return 2
in_sync: list[str] = []
stale: list[str] = []
not_installed: list[str] = []
unsafe_alias: list[str] = []
for relative in required:
deployed = installed / relative
if not deployed.is_file():
not_installed.append(relative)
continue
if has_symlinked_component(installed, relative):
unsafe_alias.append(relative)
continue
try:
if source_files[relative].samefile(deployed):
unsafe_alias.append(relative)
elif digest(source_files[relative]) == digest(deployed):
in_sync.append(relative)
else:
stale.append(relative)
except OSError as error:
print(f"[framework-drift] CANNOT_ASSERT cannot compare {relative}: {error}", file=sys.stderr)
return 2
source_relative = set(source_files)
installed_only = sorted(set(installed_files) - source_relative)
if args.verbose:
for relative in in_sync:
print(f"[framework-drift] IN_SYNC {relative}")
for relative in stale:
print(f"[framework-drift] STALE {relative}")
for relative in not_installed:
print(f"[framework-drift] NOT_INSTALLED {relative}")
for relative in unsafe_alias:
print(f"[framework-drift] UNSAFE_ALIAS {relative}")
if args.verbose:
for relative in installed_only:
print(f"[framework-drift] INSTALLED_ONLY operator-or-unknown {relative}")
print(
"[framework-drift] summary "
f"in-sync={len(in_sync)} stale={len(stale)} not-installed={len(not_installed)} "
f"unsafe-alias={len(unsafe_alias)} installed-only={len(installed_only)}"
)
print("[framework-drift] classification canonical framework-manifest ownership; installed-only=operator-or-unknown-preserved")
if stale or not_installed or unsafe_alias:
print("[framework-drift] FAIL deployed framework tools do not match independent shipped source; schedule a reviewed framework reseed", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,59 @@
# Quality Rails Installation Script (Windows)
param(
[Parameter(Mandatory=$true)]
[string]$Template,
[Parameter(Mandatory=$false)]
[string]$TargetDir = "."
)
$ErrorActionPreference = "Stop"
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$RepoRoot = Split-Path -Parent $ScriptDir
$TemplateDir = Join-Path $RepoRoot "templates\$Template"
if (-not (Test-Path $TemplateDir)) {
Write-Error "Template '$Template' not found at $TemplateDir"
Write-Host "Available templates: typescript-node, typescript-nextjs, python, monorepo"
exit 1
}
Write-Host "Installing Quality Rails: $Template"
Write-Host "Target directory: $TargetDir"
Write-Host ""
# Copy template files
Write-Host "Copying template files..."
if (Test-Path "$TemplateDir\.husky") {
Copy-Item -Path "$TemplateDir\.husky" -Destination $TargetDir -Recurse -Force
}
Copy-Item -Path "$TemplateDir\.lintstagedrc.js" -Destination $TargetDir -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$TemplateDir\.eslintrc.strict.js" -Destination "$TargetDir\.eslintrc.js" -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$TemplateDir\tsconfig.strict.json" -Destination "$TargetDir\tsconfig.json" -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$TemplateDir\.woodpecker.yml" -Destination $TargetDir -Force -ErrorAction SilentlyContinue
# Copy shared gitleaks config from templates root
$SharedTemplates = Split-Path -Parent $TemplateDir
Copy-Item -Path "$SharedTemplates\.gitleaks.toml" -Destination $TargetDir -Force -ErrorAction SilentlyContinue
Write-Host "✓ Files copied"
if (Test-Path "$TargetDir\package.json") {
Write-Host ""
Write-Host "⚠ package.json exists. Please manually merge dependencies from:"
Write-Host " $TemplateDir\package.json.snippet"
} else {
Write-Host "⚠ No package.json found. Create one and add dependencies from:"
Write-Host " $TemplateDir\package.json.snippet"
}
Write-Host ""
Write-Host "✓ Quality Rails installed successfully!"
Write-Host ""
Write-Host "Next steps:"
Write-Host "1. Install dependencies: npm install"
Write-Host "2. Initialize husky: npx husky install"
Write-Host "3. Install gitleaks: winget install gitleaks"
Write-Host "4. Run verification: ..\quality-rails\scripts\verify.ps1"
Write-Host "5. (Optional) Scan full history: gitleaks git --redact --verbose"
@@ -0,0 +1,81 @@
#!/bin/bash
set -e
# Quality Rails Installation Script
# Usage: ./install.sh --template typescript-node [--target /path/to/project]
TEMPLATE=""
TARGET_DIR="."
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--template)
TEMPLATE="$2"
shift 2
;;
--target)
TARGET_DIR="$2"
shift 2
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 --template <template-name> [--target <directory>]"
exit 1
;;
esac
done
if [ -z "$TEMPLATE" ]; then
echo "Error: --template is required"
echo "Available templates: typescript-node, typescript-nextjs, python, monorepo"
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
TEMPLATE_DIR="$REPO_ROOT/templates/$TEMPLATE"
if [ ! -d "$TEMPLATE_DIR" ]; then
echo "Error: Template '$TEMPLATE' not found at $TEMPLATE_DIR"
exit 1
fi
echo "Installing Quality Rails: $TEMPLATE"
echo "Target directory: $TARGET_DIR"
echo ""
# Copy template files
echo "Copying template files..."
cp -r "$TEMPLATE_DIR/.husky" "$TARGET_DIR/" 2>/dev/null || true
cp "$TEMPLATE_DIR/.lintstagedrc.js" "$TARGET_DIR/" 2>/dev/null || true
cp "$TEMPLATE_DIR/.eslintrc.strict.js" "$TARGET_DIR/.eslintrc.js" 2>/dev/null || true
cp "$TEMPLATE_DIR/tsconfig.strict.json" "$TARGET_DIR/tsconfig.json" 2>/dev/null || true
cp "$TEMPLATE_DIR/.woodpecker.yml" "$TARGET_DIR/" 2>/dev/null || true
# Copy shared gitleaks config from templates root
SHARED_TEMPLATES="$(dirname "$TEMPLATE_DIR")"
cp "$SHARED_TEMPLATES/.gitleaks.toml" "$TARGET_DIR/" 2>/dev/null || true
echo "✓ Files copied"
# Check if package.json exists
if [ -f "$TARGET_DIR/package.json" ]; then
echo ""
echo "⚠ package.json exists. Please manually merge dependencies from:"
echo " $TEMPLATE_DIR/package.json.snippet"
else
echo "⚠ No package.json found. Create one and add dependencies from:"
echo " $TEMPLATE_DIR/package.json.snippet"
fi
echo ""
echo "✓ Quality Rails installed successfully!"
echo ""
echo "Next steps:"
echo "1. Install dependencies: npm install"
echo "2. Initialize husky: npx husky install"
echo "3. Install gitleaks: https://github.com/gitleaks/gitleaks#installing"
echo "4. Run verification: ~/.config/mosaic/bin/mosaic-quality-verify --target $TARGET_DIR"
echo "5. (Optional) Scan full history: gitleaks git --redact --verbose"
echo ""
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
# test-check-test-enumeration.sh — needles for the enumeration guard (#1017).
#
# Every failure mode the guard promises gets BOTH polarities:
# NEEDLE a fixture that MUST trip the guard, asserted on the guard's OWN
# words (--out) — exit 1 alone cannot distinguish "caught the rogue
# file" from "choked on the fixture".
# CONTROL a fixture that MUST pass. A guard that failed unconditionally
# would satisfy every needle here — the null-case defect the guard's
# own subject matter (#1017) exists to make impossible.
#
# The needles encode the specific errors that produced #1017's thread:
# n6 is the 20124 boundary file (a suite the strict prefix cannot name);
# n2b proves surface 2 is PARSED, not line-ranged (three seats mis-scoped
# hand-written ranges against ci.yml);
# n5/n7 keep the exclusions file honest so it cannot become the next silent cap;
# n8/c4 are F1 (20155): a commented-out ci.yml line is NOT enumeration —
# commenting-out is the most common way a suite actually gets disabled,
# and it must fail loud in one direction without false-staling the other.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GUARD="$HERE/check-test-enumeration.sh"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
# fixture <name> — a minimal repo root the guard accepts via --root:
# one suite enumerated on S1 (plus a naming-outlier suite, so the S1 parser's
# handling of non-prefix names is always exercised), one on S2, one check-script
# named on S2 that is outside the population, and an empty exclusions file.
fixture() {
local r="$TMP/$1"
mkdir -p "$r/packages/mosaic/framework/tools/git" \
"$r/packages/mosaic/framework/tools/tmux" \
"$r/packages/mosaic/framework/tools/quality/scripts" \
"$r/.woodpecker"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/git/test-a.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/tmux/outlier.test.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/quality/scripts/test-ci.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/quality/scripts/verify-thing.sh"
cat > "$r/packages/mosaic/package.json" <<'JSON'
{"scripts": {"test:framework-shell": "bash framework/tools/git/test-a.sh && bash framework/tools/tmux/outlier.test.sh"}}
JSON
cat > "$r/.woodpecker/ci.yml" <<'YML'
steps:
sanitize:
commands:
- bash packages/mosaic/framework/tools/quality/scripts/verify-thing.sh
guard:
commands:
- bash packages/mosaic/framework/tools/quality/scripts/test-ci.sh
YML
: > "$r/packages/mosaic/framework/tools/quality/test-enumeration-exclusions.txt"
printf '%s' "$r"
}
# expect <kind> <want-exit> <desc> [--out <substring>] -- <root>
expect() {
local kind="$1" want="$2" desc="$3"; shift 3
local need_out=""
while (( $# )); do
case "$1" in
--out) need_out="$2"; shift 2 ;;
--) shift; break ;;
esac
done
local root="$1" got=0 out
out="$(bash "$GUARD" --root "$root" 2>&1)" || got=$?
local why=""
[[ "$got" == "$want" ]] || why="wanted exit $want, got $got"
if [[ -z "$why" && -n "$need_out" && "$out" != *"$need_out"* ]]; then
why="exit $got as expected, but output never said: $need_out"
fi
if [[ -z "$why" ]]; then
printf ' PASS [%-7s] %s (exit %s)\n' "$kind" "$desc" "$got"
PASS=$(( PASS + 1 ))
else
printf ' FAIL [%-7s] %s — %s\n' "$kind" "$desc" "$why"
printf '%s\n' "$out" | sed 's/^/ | /'
FAIL=$(( FAIL + 1 ))
fi
}
excl() { printf '%s\n' "$2" >> "$1/packages/mosaic/framework/tools/quality/test-enumeration-exclusions.txt"; }
echo "=== c1: a fully consistent fixture passes ==="
R="$(fixture c1)"
expect CONTROL 0 "consistent tree: both surfaces enumerated, nothing unlisted" \
--out "enumeration guard: OK" -- "$R"
echo "=== n1: an on-disk suite reachable from no surface must fail ==="
R="$(fixture n1)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
expect NEEDLE 1 "unlisted suite is named in the failure" \
--out "UNENUMERATED: 'packages/mosaic/framework/tools/git/test-rogue.sh'" -- "$R"
echo "=== n6: the 20124 boundary file — a suite the strict prefix cannot name ==="
R="$(fixture n6)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/rogue.test.sh"
expect NEEDLE 1 "naming-outlier suite (*.test.sh) is a population member, not invisible" \
--out "UNENUMERATED: 'packages/mosaic/framework/tools/git/rogue.test.sh'" -- "$R"
echo "=== c3: a non-suite script outside the pattern is outside it on BOTH sides ==="
R="$(fixture c3)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/check-unrelated.sh"
expect CONTROL 0 "check-script on disk, unlisted, outside population: not the guard's business" \
--out "enumeration guard: OK" -- "$R"
echo "=== n2/n2b: a surface naming a path absent from disk must fail — both surfaces ==="
R="$(fixture n2)"
rm "$R/packages/mosaic/framework/tools/git/test-a.sh"
expect NEEDLE 1 "S1 (package.json) stale entry" \
--out "STALE ENUMERATION: surfaces name 'packages/mosaic/framework/tools/git/test-a.sh'" -- "$R"
R="$(fixture n2b)"
rm "$R/packages/mosaic/framework/tools/quality/scripts/test-ci.sh"
expect NEEDLE 1 "S2 (ci.yml) stale entry — proves ci.yml is parsed, not line-ranged" \
--out "STALE ENUMERATION: surfaces name 'packages/mosaic/framework/tools/quality/scripts/test-ci.sh'" -- "$R"
echo "=== c2: a rogue suite with a SIGNED exclusion passes, and is counted ==="
R="$(fixture c2)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
excl "$R" "packages/mosaic/framework/tools/git/test-rogue.sh | non-hermetic pending fixture work (needle-suite specimen)"
expect CONTROL 0 "signed exclusion is honoured and visible in the summary" \
--out "excluded (signed) 1" -- "$R"
echo "=== n3: an exclusion with no reason is not a decision ==="
R="$(fixture n3)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
excl "$R" "packages/mosaic/framework/tools/git/test-rogue.sh | "
expect NEEDLE 1 "empty reason rejected" --out "EXCLUSION MISSING REASON" -- "$R"
R="$(fixture n3b)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
excl "$R" "packages/mosaic/framework/tools/git/test-rogue.sh"
expect NEEDLE 1 "missing separator rejected (the path alone is not a signature)" \
--out "EXCLUSION MISSING REASON" -- "$R"
echo "=== n4: an exclusion whose path is gone is stale, not satisfied ==="
R="$(fixture n4)"
excl "$R" "packages/mosaic/framework/tools/git/test-vanished.sh | was excluded once, then deleted"
expect NEEDLE 1 "stale exclusion rejected" --out "STALE EXCLUSION" -- "$R"
echo "=== n5: excluding an enumerated suite is a contradiction, not belt-and-braces ==="
R="$(fixture n5)"
excl "$R" "packages/mosaic/framework/tools/git/test-a.sh | already in CI but excluded anyway"
expect NEEDLE 1 "contradictory exclusion rejected" --out "CONTRADICTORY EXCLUSION" -- "$R"
echo "=== n8/c4: a commented-out ci.yml line is not enumeration (F1, 20155) ==="
R="$(fixture n8)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-disabled.sh"
printf ' # - bash packages/mosaic/framework/tools/git/test-disabled.sh\n' >> "$R/.woodpecker/ci.yml"
expect NEEDLE 1 "suite named only in a commented-out invocation is UNENUMERATED" \
--out "UNENUMERATED: 'packages/mosaic/framework/tools/git/test-disabled.sh'" -- "$R"
R="$(fixture c4)"
printf ' # - bash packages/mosaic/framework/tools/git/test-vanished.sh\n' >> "$R/.woodpecker/ci.yml"
expect CONTROL 0 "comment naming an absent path raises no false stale-enumeration" \
--out "enumeration guard: OK" -- "$R"
echo "=== n7: excluding a file outside the population is dead weight, not coverage ==="
R="$(fixture n7)"
excl "$R" "packages/mosaic/framework/tools/quality/scripts/verify-thing.sh | not a suite but signing it anyway"
expect NEEDLE 1 "out-of-population exclusion rejected" --out "EXCLUSION OUTSIDE POPULATION" -- "$R"
echo
printf 'enumeration-guard needles: %d passed, %d failed\n' "$PASS" "$FAIL"
(( FAIL == 0 ))
@@ -0,0 +1,123 @@
#!/usr/bin/env python3
from __future__ import annotations
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest
CHECKER = Path(__file__).with_name("framework-drift-check.py")
REAL_RESOLVER = CHECKER.parents[2] / "_lib" / "manifest.sh"
class FrameworkDriftCheckTests(unittest.TestCase):
def setUp(self) -> None:
self.temp = tempfile.TemporaryDirectory()
root = Path(self.temp.name)
self.framework = root / "framework"
self.source = self.framework / "tools"
self.installed = root / "home" / "tools"
for directory in (self.source / "git", self.source / "_lib", self.installed / "git", self.installed / "_lib"):
directory.mkdir(parents=True, exist_ok=True)
shutil.copy2(REAL_RESOLVER, self.source / "_lib" / "manifest.sh")
(self.source / "git" / "guard.sh").write_text("fixed\n")
(self.source / "git" / "new-wrapper.sh").write_text("new\n")
(self.source / "_lib" / "credentials.json").write_text("source-placeholder\n")
self.write_manifest()
def tearDown(self) -> None:
self.temp.cleanup()
def write_manifest(self, operator_extra: str = "") -> None:
(self.framework / "framework-manifest.txt").write_text(
"[framework]\ntools/**\n[operator]\ntools/_lib/credentials.json\n" + operator_extra
)
def run_check(self, *extra: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, str(CHECKER), "--source-root", str(self.framework), "--installed-root", str(self.installed), *extra],
text=True, capture_output=True, check=False,
env={**os.environ, "PYTHONDONTWRITEBYTECODE": "1"},
)
def install_matching(self) -> None:
for relative in ("git/guard.sh", "git/new-wrapper.sh", "_lib/manifest.sh"):
shutil.copy2(self.source / relative, self.installed / relative)
(self.installed / "_lib" / "credentials.json").write_text("different-operator-secret\n")
def test_fails_loudly_and_classifies_stale_missing_and_installed_only(self) -> None:
(self.installed / "git" / "guard.sh").write_text("broken\n")
shutil.copy2(self.source / "_lib" / "manifest.sh", self.installed / "_lib" / "manifest.sh")
(self.installed / "local-helper.sh").write_text("operator\n")
result = self.run_check("--verbose")
self.assertEqual(result.returncode, 1)
self.assertIn("STALE git/guard.sh", result.stdout)
self.assertIn("NOT_INSTALLED git/new-wrapper.sh", result.stdout)
self.assertIn("INSTALLED_ONLY operator-or-unknown local-helper.sh", result.stdout)
self.assertIn("FAIL deployed framework tools", result.stderr)
def test_passes_only_when_every_manifest_owned_source_file_matches(self) -> None:
self.install_matching()
result = self.run_check()
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("stale=0 not-installed=0 unsafe-alias=0", result.stdout)
def test_exact_operator_directory_does_not_hide_framework_drift_beneath_it(self) -> None:
self.install_matching()
(self.installed / "git" / "guard.sh").write_text("drift-hidden-by-directory-entry\n")
self.write_manifest("tools/git\n")
result = self.run_check()
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
self.assertIn("STALE git/guard.sh", result.stdout)
def test_manifest_is_required_and_policy_changes_take_effect(self) -> None:
self.install_matching()
(self.installed / "git" / "guard.sh").write_text("operator-divergence\n")
self.write_manifest("tools/git/guard.sh\n")
self.assertEqual(self.run_check().returncode, 0)
(self.framework / "framework-manifest.txt").unlink()
result = self.run_check()
self.assertEqual(result.returncode, 2)
self.assertIn("CANNOT_ASSERT ownership manifest is missing", result.stderr)
def test_empty_and_unreadable_source_census_cannot_assert(self) -> None:
empty_framework = Path(self.temp.name) / "empty-framework"
empty_source = empty_framework / "tools"
empty_source.mkdir(parents=True)
shutil.copy2(self.framework / "framework-manifest.txt", empty_framework / "framework-manifest.txt")
# The canonical resolver is supplied outside the empty census solely so
# this probe reaches the explicit minimum-population guard.
result = subprocess.run([sys.executable, str(CHECKER), "--source-root", str(empty_framework), "--installed-root", str(self.installed)], text=True, capture_output=True)
self.assertEqual(result.returncode, 2)
self.assertIn("CANNOT_ASSERT", result.stderr)
blocked = self.source / "blocked"
blocked.mkdir(); (blocked / "hidden.sh").write_text("hidden\n"); blocked.chmod(0)
try:
result = self.run_check()
finally:
blocked.chmod(0o700)
self.assertEqual(result.returncode, 2)
self.assertIn("CANNOT_ASSERT", result.stderr)
self.assertTrue("Permission denied" in result.stderr or "no readable/searchable mode" in result.stderr)
def test_root_and_descendant_aliases_cannot_report_clean(self) -> None:
result = subprocess.run([sys.executable, str(CHECKER), "--source-root", str(self.framework), "--installed-root", str(self.source)], text=True, capture_output=True)
self.assertEqual(result.returncode, 2)
self.assertIn("same filesystem object", result.stderr)
shutil.copy2(self.source / "_lib" / "manifest.sh", self.installed / "_lib" / "manifest.sh")
shutil.rmtree(self.installed / "git")
(self.installed / "git").symlink_to(self.source / "git", target_is_directory=True)
result = self.run_check()
self.assertNotEqual(result.returncode, 0)
self.assertTrue("symlinked directory" in result.stderr or "UNSAFE_ALIAS" in result.stdout)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# Doctor must contain a stalled drift checker and continue its remaining audit.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCTOR="$SCRIPT_DIR/../../_scripts/mosaic-doctor"
WORK="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/framework-drift-doctor}"
rm -rf "$WORK"
mkdir -p "$WORK/source/tools/quality/scripts" "$WORK/source/tools/_scripts" "$WORK/home/tools"
cp "$DOCTOR" "$WORK/source/tools/_scripts/mosaic-doctor"
cat > "$WORK/source/tools/quality/scripts/framework-drift-check.py" <<'PY'
import time
time.sleep(30)
PY
start=$(date +%s)
set +e
output=$(MOSAIC_HOME="$WORK/home" MOSAIC_DOCTOR_DRIFT_TIMEOUT_SEC=1 \
bash "$WORK/source/tools/_scripts/mosaic-doctor" --fail-on-warn 2>&1)
rc=$?
set -e
elapsed=$(( $(date +%s) - start ))
[[ "$rc" -ne 0 ]] || { echo "FAIL: checker timeout became doctor success" >&2; exit 1; }
[[ "$elapsed" -lt 10 ]] || { echo "FAIL: checker hang escaped watchdog (${elapsed}s)" >&2; exit 1; }
[[ "$output" == *"CANNOT_ASSERT framework drift checker timed out"* ]] || {
echo "FAIL: missing timeout CANNOT_ASSERT diagnostic" >&2; printf '%s\n' "$output" >&2; exit 1;
}
[[ "$output" == *"[mosaic-doctor] warnings="* ]] || {
echo "FAIL: doctor did not continue after checker timeout" >&2; printf '%s\n' "$output" >&2; exit 1;
}
echo "framework drift doctor watchdog regression passed"
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# test-install-migration.sh — fixture matrix for the v2→v3 (Constitution) upgrade
# migration in install.sh. Runs the installer against throwaway MOSAIC_HOME dirs
# with MOSAIC_SYNC_ONLY=1 (file phase only — no environment-touching post-install)
# and asserts the framework-owned-overwrite + user-preserve + backup semantics.
#
# Mirrors the TS fixture suite in packages/mosaic/src/config/file-adapter.test.ts;
# both installers MUST behave identically.
#
# Usage: bash test-install-migration.sh
set -uo pipefail
FW="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" # packages/mosaic/framework
INSTALL="$FW/install.sh"
DEFA="$FW/defaults"
pass=0; fail=0
chk() { if eval "$2"; then echo "$1"; pass=$((pass + 1)); else echo "$1"; fail=$((fail + 1)); fi; }
run() { MOSAIC_HOME="$1" MOSAIC_INSTALL_MODE="$2" MOSAIC_SYNC_ONLY=1 bash "$INSTALL" >/dev/null 2>&1; }
echo "install.sh v2→v3 migration fixture matrix:"
# F1 — fresh install
T1=$(mktemp -d); run "$T1" overwrite
chk "F1 fresh: CONSTITUTION/AGENTS/STANDARDS/TOOLS seeded" \
"[ -f '$T1/CONSTITUTION.md' ] && [ -f '$T1/AGENTS.md' ] && [ -f '$T1/STANDARDS.md' ] && [ -f '$T1/TOOLS.md' ]"
chk "F1 fresh: AGENTS == shipped default" "cmp -s '$T1/AGENTS.md' '$DEFA/AGENTS.md'"
chk "F1 fresh: framework-version stamped 3" "[ \"\$(cat '$T1/.framework-version' 2>/dev/null)\" = 3 ]"
chk "F1 fresh: Pi goal extension deploys under Mosaic runtime" \
"cmp -s '$T1/runtime/pi/goal-extension.ts' '$FW/runtime/pi/goal-extension.ts'"
chk "F1 fresh: installer creates no nested main Pi config" "[ ! -e '$T1/.pi' ]"
# F2 — legacy install with a user-edited AGENTS.md (the sanctioned pre-constitution customization)
T2=$(mktemp -d); mkdir -p "$T2/credentials"
printf '# user-edited AGENTS pre-constitution\n' > "$T2/AGENTS.md"
printf '# my persona\n' > "$T2/SOUL.md"
printf 'token\n' > "$T2/credentials/c.json"
echo 2 > "$T2/.framework-version"
run "$T2" keep
chk "F2 legacy-edited: AGENTS overwritten to framework version" "cmp -s '$T2/AGENTS.md' '$DEFA/AGENTS.md'"
chk "F2 legacy-edited: prior AGENTS saved to .pre-constitution.bak" \
"grep -q 'user-edited AGENTS pre-constitution' '$T2/AGENTS.md.pre-constitution.bak'"
chk "F2 legacy-edited: SOUL.md preserved" "grep -q 'my persona' '$T2/SOUL.md'"
chk "F2 legacy-edited: credentials preserved" "grep -q token '$T2/credentials/c.json'"
chk "F2 legacy-edited: CONSTITUTION.md installed" "[ -f '$T2/CONSTITUTION.md' ]"
run "$T2" keep
chk "F2 idempotent: .pre-constitution.bak preserved across a 2nd upgrade" \
"grep -q 'user-edited AGENTS pre-constitution' '$T2/AGENTS.md.pre-constitution.bak'"
# F3 — user-tuned STANDARDS.md
T3=$(mktemp -d); printf '# tuned standards\n' > "$T3/STANDARDS.md"; printf '# persona\n' > "$T3/SOUL.md"; echo 2 > "$T3/.framework-version"
run "$T3" keep
chk "F3 tuned-standard: STANDARDS overwritten" "cmp -s '$T3/STANDARDS.md' '$DEFA/STANDARDS.md'"
chk "F3 tuned-standard: tuned copy backed up" "grep -q 'tuned standards' '$T3/STANDARDS.md.pre-constitution.bak'"
# F4 — unattended / no TTY (stdin closed): must complete without hanging, default to keep
T4=$(mktemp -d); printf '# persona\n' > "$T4/SOUL.md"; printf '# old\n' > "$T4/AGENTS.md"; echo 2 > "$T4/.framework-version"
MOSAIC_HOME="$T4" MOSAIC_SYNC_ONLY=1 bash "$INSTALL" </dev/null >/dev/null 2>&1
chk "F4 no-TTY: completed, AGENTS updated" "cmp -s '$T4/AGENTS.md' '$DEFA/AGENTS.md'"
# F5 — failure path must not corrupt existing data (invalid mode rejected before any file op)
T5=$(mktemp -d); mkdir -p "$T5/credentials"; printf '# orig\n' > "$T5/SOUL.md"; printf 'keepme\n' > "$T5/credentials/c.json"; echo 2 > "$T5/.framework-version"
MOSAIC_HOME="$T5" MOSAIC_INSTALL_MODE=bogus MOSAIC_SYNC_ONLY=1 bash "$INSTALL" >/dev/null 2>&1; rc=$?
chk "F5 failure: invalid mode rejected (nonzero exit)" "[ $rc -ne 0 ]"
chk "F5 failure: SOUL + credentials intact" "grep -q orig '$T5/SOUL.md' && grep -q keepme '$T5/credentials/c.json'"
# F6 — keep-mode re-seed (the `mosaic update` path) MUST preserve ALL user-owned
# fleet state — including an unanticipated file the manifest never names, which
# resolves to operator-owned by the #791 fail-safe — while refreshing the
# framework-owned schema/examples.
T6=$(mktemp -d); mkdir -p "$T6/fleet/examples" "$T6/fleet/run" "$T6/fleet/agents"
printf '# persona\n' > "$T6/SOUL.md" # makes it a recognized existing install (→ keep mode)
printf 'version: 1\nagents:\n - name: coder0\n' > "$T6/fleet/roster.yaml"
printf '{"version":1,"agents":[{"name":"json-user"}]}\n' > "$T6/fleet/roster.json"
printf 'version: 1\nagents:\n - name: not-active-roster\n' > "$T6/fleet/my-fleet.yaml"
printf 'ts=x\n' > "$T6/fleet/run/coder0.hb"
printf 'MOSAIC_AGENT_NAME=coder0\n' > "$T6/fleet/agents/coder0.env"
printf '# stale preset\n' > "$T6/fleet/examples/general.yaml"
printf '{"stale":true}\n' > "$T6/fleet/roster.schema.json"
E6=$(mktemp -d)
cp "$T6/fleet/roster.yaml" "$E6/roster-yaml.expected"
cp "$T6/fleet/roster.json" "$E6/roster-json.expected"
cp "$T6/fleet/my-fleet.yaml" "$E6/my-fleet.expected"
cp "$T6/fleet/run/coder0.hb" "$E6/run.expected"
cp "$T6/fleet/agents/coder0.env" "$E6/agent.expected"
echo 3 > "$T6/.framework-version"
run "$T6" keep
chk "F6 reseed: exact roster.yaml bytes survive keep-mode sync" "cmp -s '$T6/fleet/roster.yaml' '$E6/roster-yaml.expected'"
chk "F6 reseed: exact roster.json bytes survive keep-mode sync" "cmp -s '$T6/fleet/roster.json' '$E6/roster-json.expected'"
chk "F6 reseed: unanticipated operator fleet file survives (fail-safe, #791)" "cmp -s '$T6/fleet/my-fleet.yaml' '$E6/my-fleet.expected'"
chk "F6 reseed: per-agent env bytes survive" "cmp -s '$T6/fleet/agents/coder0.env' '$E6/agent.expected'"
chk "F6 reseed: heartbeat bytes survive" "cmp -s '$T6/fleet/run/coder0.hb' '$E6/run.expected'"
chk "F6 reseed: framework examples are refreshed" "grep -q orchestrator '$T6/fleet/examples/general.yaml'"
chk "F6 reseed: framework roster schema is refreshed" "cmp -s '$T6/fleet/roster.schema.json' '$FW/fleet/roster.schema.json'"
chk "F6 reseed: Pi goal extension is refreshed from framework source" \
"cmp -s '$T6/runtime/pi/goal-extension.ts' '$FW/runtime/pi/goal-extension.ts'"
rm -rf "$T1" "$T2" "$T3" "$T4" "$T5" "$T6" "$E6"
echo
echo "RESULT: $pass passed, $fail failed"
[ "$fail" -eq 0 ]
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""Regression checks for connector-kind-conditional fleet roster schema."""
import json
import sys
from pathlib import Path
from jsonschema import Draft202012Validator
schema_path = Path(__file__).resolve().parents[3] / "fleet" / "roster.schema.json"
schema = json.loads(schema_path.read_text(encoding="utf-8"))
validator = Draft202012Validator(schema)
base = {
"version": 1,
"transport": "tmux",
"agents": [{"name": "orchestrator", "runtime": "pi"}],
}
valid = [
{"kind": "tmux"},
{"kind": "discord", "discord": {"channel_id": "123"}},
{
"kind": "matrix",
"matrix": {
"homeserver_url": "https://matrix.example",
"user_id": "@mosaic:example",
"room_id": "!room:example",
},
},
]
invalid = [
{"kind": "tmux", "discord": {"channel_id": "123"}},
{"kind": "tmux", "matrix": {}},
{"kind": "discord"},
{"kind": "discord", "matrix": {}},
{
"kind": "discord",
"discord": {"channel_id": "123"},
"matrix": {
"homeserver_url": "https://matrix.example",
"user_id": "@mosaic:example",
"room_id": "!room:example",
},
},
{"kind": "matrix"},
{"kind": "matrix", "discord": {"channel_id": "123"}},
{"kind": "discord", "discord": {"channel_id": ""}},
{"kind": "discord", "discord": {"channel_id": " "}},
{
"kind": "matrix",
"matrix": {
"homeserver_url": "",
"user_id": "@mosaic:example",
"room_id": "!room:example",
},
},
{
"kind": "matrix",
"matrix": {
"homeserver_url": "\t",
"user_id": "@mosaic:example",
"room_id": "!room:example",
},
},
{
"kind": "matrix",
"matrix": {
"homeserver_url": "https://matrix.example",
"user_id": "",
"room_id": "!room:example",
},
},
{
"kind": "matrix",
"matrix": {
"homeserver_url": "https://matrix.example",
"user_id": " ",
"room_id": "!room:example",
},
},
{
"kind": "matrix",
"matrix": {
"homeserver_url": "https://matrix.example",
"user_id": "@mosaic:example",
"room_id": "",
},
},
{
"kind": "matrix",
"matrix": {
"homeserver_url": "https://matrix.example",
"user_id": "@mosaic:example",
"room_id": "\n",
},
},
]
for connector in valid:
errors = list(validator.iter_errors({**base, "connector": connector}))
if errors:
print(f"expected valid connector {connector}: {errors}", file=sys.stderr)
raise SystemExit(1)
for connector in invalid:
if not list(validator.iter_errors({**base, "connector": connector})):
print(f"expected invalid connector: {connector}", file=sys.stderr)
raise SystemExit(1)
print("connector schema regression: PASS")
@@ -0,0 +1,317 @@
#!/usr/bin/env bash
# test-upgrade-durable-snapshot.sh — the #791 PR2 regression gate.
#
# PR1 gave keep-mode upgrades two protections: the manifest (a keep-sync only
# ever writes framework-owned paths — operator config is structurally untouched)
# and an EPHEMERAL /tmp snapshot that rolls the whole target back if the sync
# CRASHES mid-write. PR2 adds a third, independent layer for the case neither
# covers: a "successful" upgrade that a manifest/logic bug silently let touch an
# operator file. That layer is a DURABLE, operator-scoped pre-update snapshot:
#
# Part 1 (scope): before any mutation, the installer copies exactly the
# operator-owned files that exist into a retained backup
# under $XDG_STATE_HOME/mosaic/backups/pre-update-<ts>/ —
# framework files are NOT captured.
# Part 2 (perms): the backup root, snapshot dir and every nested dir are
# 0700; every backed-up file is 0600 (never world-readable,
# even though operator config may hold secrets).
# Part 3 (no leak): a secret seeded into credentials.json is copied into the
# snapshot (proving coverage) but its value never appears
# on stdout/stderr — the snapshot reports counts/paths only.
# Part 4 (retention): only the newest MOSAIC_BACKUP_RETENTION snapshots survive;
# older ones are pruned.
# Part 5 (verify net): if the upgrade DID modify an operator file (injected here
# with a cp shim that scribbles on SOUL.md while a framework
# file is copied), the post-sync verify restores that file
# from the durable snapshot and warns loudly. The control —
# the same installer with the verify call stripped — leaves
# the corruption in place, proving the net is load-bearing.
#
# Usage: bash test-upgrade-durable-snapshot.sh
set -uo pipefail
FW="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" # packages/mosaic/framework
INSTALL="$FW/install.sh"
ORIG_PATH="$PATH"
FRAMEWORK_VERSION="$(grep -m1 '^FRAMEWORK_VERSION=' "$INSTALL" | cut -d= -f2)"
# Control installers must live INSIDE $FW: install.sh derives SOURCE_DIR from its
# own path and sources tools/_lib/manifest.sh relative to it, so a copy anywhere
# else aborts before the sync. Each control is a shipped installer with one guard
# line stripped (keyed off a `# <MARKER>` anchor), proving that guard load-bearing.
# All controls share the .install-*.tmp.sh glob so one trap sweeps them on exit.
VERIFYCTRL="$FW/.install-verifynet-control.tmp.sh"
rm -f "$FW"/.install-*.tmp.sh
trap 'rm -f "$FW"/.install-*.tmp.sh' EXIT
# mk_control <marker-regex> <name> — echo a control installer path ($FW-local) that
# is $INSTALL with every line matching /<marker-regex>/ deleted.
mk_control() {
local path="$FW/.install-$2.tmp.sh"
sed "/$1/d" "$INSTALL" > "$path"
printf '%s' "$path"
}
pass=0; fail=0
chk() { if eval "$2"; then echo "$1"; pass=$((pass + 1)); else echo "$1"; fail=$((fail + 1)); fi; }
SECRET='SUPER-SECRET-TOKEN-do-not-log-pr2'
SOUL_ORIG='# persona'
# A framework file the sync copies (source ships it; the seeded target omits it,
# so the bytes differ and cp is attempted). The Part-5 shim keys off this path.
POISON_REL='guides/E2E-DELIVERY.md'
# Seed a recognized keep-mode install holding four operator-owned files across
# the identity file, an operator subtree, memory, and the credentials carve-out.
seed_home() {
local H="$1"
mkdir -p "$H/agents" "$H/tools/_lib" "$H/memory"
printf '%s\n' "$SOUL_ORIG" > "$H/SOUL.md" # recognized install → keep mode
printf 'MODEL=opus\n' > "$H/agents/coder0.conf"
printf '# operator memory\n' > "$H/memory/note.md"
printf 'TOKEN=%s\n' "$SECRET" > "$H/tools/_lib/credentials.json"
echo 3 > "$H/.framework-version"
# Deliberately NO guides/E2E-DELIVERY.md so the sync copies it (framework file,
# bytes differ) — that copy is where the Part-5 corruption shim fires.
}
# A pre-v2 (legacy) keep-mode install: SOUL.md marks it recognized, and a bin/
# tree with NO .framework-version makes installed_framework_version() report 1, so
# the v1→v2 migration (which deletes bin/) runs. bin/ is unknown⇒operator, so the
# durable snapshot captures it — the verify net must NOT heal the intended removal.
seed_home_v1() {
local H="$1"
mkdir -p "$H/agents" "$H/tools/_lib" "$H/memory" "$H/bin"
printf '%s\n' "$SOUL_ORIG" > "$H/SOUL.md"
printf 'TOKEN=%s\n' "$SECRET" > "$H/tools/_lib/credentials.json"
printf '#!/bin/sh\necho legacy\n' > "$H/bin/tool.sh"; chmod +x "$H/bin/tool.sh"
# Deliberately NO .framework-version and NO guides/E2E-DELIVERY.md (see seed_home).
}
# A cp shim that, while the framework POISON file is being copied during sync,
# swaps the operator credentials file for a symlink pointing at an attacker-
# readable file OUTSIDE the target — simulating post-snapshot tampering (CWE-59).
# The durable snapshot already holds the real credentials (it is taken before any
# sync), so the verify net must restore a REAL file in place WITHOUT following the
# link (which would write the snapshot's secret out through it). $EXFIL_TARGET is
# expanded at shim-write time from the caller's environment.
#
# PORTABILITY (why this shim, not the real `cp`): the CWE-59 leak this exercises is
# `cp` writing THROUGH a symlinked destination. GNU/BSD cp — what a real operator
# runs `mosaic update` under — follows the dest symlink and leaks. busybox cp (the
# Alpine CI image) REPLACES a symlinked dest instead of following it, so under the
# CI harness the leak vector simply does not exist and the negative control could
# never reproduce it. This shim therefore emulates the real-target GNU cp behavior
# PORTABLY: when the destination is a symlink it writes the source bytes through the
# link via redirection (which follows symlinks on every coreutils, busybox included);
# otherwise it delegates to the host's real cp unchanged. Both the shipped-case and
# the negative control run through this identical shim, so the ONLY difference
# between them remains the SYMLINK-LEAF-GUARD — the control stays load-bearing and
# non-tautological. It does NOT touch install.sh (approved) or the real assertions:
# with the guard present the symlinked leaf is dropped BEFORE this cp runs, so the
# dest is a real file and the delegate path is taken exactly as on a GNU host.
make_symlink_leaf_shim() {
local dir="$1" home="$2"
cat > "$dir/cp" <<SHIM
#!/usr/bin/env bash
dest="\${@: -1}"
src="\${@:(-2):1}"
case "\$dest" in
*/$POISON_REL)
rm -f "$home/tools/_lib/credentials.json"
ln -s "$EXFIL_TARGET" "$home/tools/_lib/credentials.json"
;;
esac
# Coreutils-agnostic emulation of GNU cp's follow-through-dest-symlink behavior.
if [[ -L "\$dest" && -f "\$src" ]]; then
cat "\$src" > "\$dest"
exit \$?
fi
exec env PATH="$ORIG_PATH" cp "\$@"
SHIM
chmod +x "$dir/cp"
}
# A cp shim that, while the framework POISON file is being copied during sync,
# also appends garbage to the operator SOUL.md — simulating a manifest bug that
# writes outside the framework lane. The framework copy itself still succeeds
# (real cp runs), so the sync completes 0 and the post-sync verify is what must
# catch and undo the operator-file damage. The snapshot's own cp only ever
# targets operator files (never guides/…), so it is never corrupted by this shim.
make_corrupt_shim() {
local dir="$1" home="$2"
cat > "$dir/cp" <<SHIM
#!/usr/bin/env bash
dest="\${@: -1}"
case "\$dest" in
*/$POISON_REL) printf 'CORRUPTION-mid-sync\n' >> "$home/SOUL.md" 2>/dev/null || true ;;
esac
exec env PATH="$ORIG_PATH" cp "\$@"
SHIM
chmod +x "$dir/cp"
}
# Run one keep-mode, sync-only upgrade with $XDG_STATE_HOME redirected to a
# throwaway dir (so the real ~/.local/state is never touched). Optional args:
# $2 shim-maker (default none), $3 MOSAIC_BACKUP_RETENTION (default unset).
# Echoes: "<exit>\t<out>\t<state-dir>\t<home>".
# $4 seeder (default seed_home) — swap in seed_home_v1 for the migration case.
run_snap() {
local installer="$1" shim_maker="${2:-}" retention="${3:-}" seeder="${4:-seed_home}" H STATE OUT SHIM rc pathpre
H=$(mktemp -d); STATE=$(mktemp -d); OUT=$(mktemp); pathpre="$ORIG_PATH"
"$seeder" "$H"
if [[ -n "$shim_maker" ]]; then
SHIM=$(mktemp -d); "$shim_maker" "$SHIM" "$H"; pathpre="$SHIM:$ORIG_PATH"
fi
set +e
env PATH="$pathpre" XDG_STATE_HOME="$STATE" \
${retention:+MOSAIC_BACKUP_RETENTION="$retention"} \
MOSAIC_HOME="$H" MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1 \
bash "$installer" >"$OUT" 2>&1
rc=$?
set -e 2>/dev/null || true
[[ -n "$shim_maker" ]] && rm -rf "$SHIM"
printf '%s\t%s\t%s\t%s\n' "$rc" "$OUT" "$STATE" "$H"
}
# Resolve the single pre-update-* snapshot dir under a state dir (newest if many).
snap_dir() {
local -a snapshots=()
mapfile -t snapshots < <(
find "$1/mosaic/backups" -maxdepth 1 -type d -name 'pre-update-*' 2>/dev/null \
| LC_ALL=C sort -r
)
printf '%s\n' "${snapshots[0]:-}"
}
echo "── Part 1/2/3: durable snapshot scope, perms, no-leak ──────────────────"
IFS=$'\t' read -r rc OUT STATE H < <(run_snap "$INSTALL")
SNAP="$(snap_dir "$STATE")"
chk "upgrade succeeds" "[ '$rc' -eq 0 ]"
chk "exactly one pre-update snapshot created" "[ \$(find '$STATE/mosaic/backups' -maxdepth 1 -type d -name 'pre-update-*' | wc -l) -eq 1 ]"
chk "snapshot: SOUL.md captured" "[ -f '$SNAP/SOUL.md' ]"
chk "snapshot: operator subtree captured" "[ -f '$SNAP/agents/coder0.conf' ]"
chk "snapshot: memory captured" "[ -f '$SNAP/memory/note.md' ]"
chk "snapshot: credentials carve-out captured" "[ -f '$SNAP/tools/_lib/credentials.json' ]"
chk "snapshot: SOUL.md bytes preserved" "[ \"\$(cat '$SNAP/SOUL.md')\" = '$SOUL_ORIG' ]"
chk "snapshot: framework file NOT captured" "[ ! -e '$SNAP/CONSTITUTION.md' ] && [ ! -e '$SNAP/$POISON_REL' ]"
# Part 2 — permissions (0700 dirs, 0600 files); never world-readable.
chk "perms: backup root is 0700" "[ \$(stat -c '%a' '$STATE/mosaic/backups') -eq 700 ]"
chk "perms: snapshot dir is 0700" "[ \$(stat -c '%a' '$SNAP') -eq 700 ]"
chk "perms: nested dir is 0700" "[ \$(stat -c '%a' '$SNAP/agents') -eq 700 ]"
chk "perms: credentials backup is 0600" "[ \$(stat -c '%a' '$SNAP/tools/_lib/credentials.json') -eq 600 ]"
chk "perms: SOUL.md backup is 0600" "[ \$(stat -c '%a' '$SNAP/SOUL.md') -eq 600 ]"
# Part 3 — the secret is backed up but never emitted to stdout/stderr.
chk "no-leak: secret IS in the backup file" "grep -q '$SECRET' '$SNAP/tools/_lib/credentials.json'"
chk "no-leak: secret NOT on stdout/stderr" "! grep -q '$SECRET' '$OUT'"
rm -rf "$STATE" "$H"; rm -f "$OUT"
echo "── Part 4: retention prune (MOSAIC_BACKUP_RETENTION) ───────────────────"
# Pre-seed four dated snapshots, then take one real snapshot with retention=2:
# only the two newest (the fresh real one + the newest pre-seeded) must survive.
IFS=$'\t' read -r rc OUT STATE H < <(
H=$(mktemp -d); STATE=$(mktemp -d); OUT=$(mktemp)
seed_home "$H"
mkdir -p "$STATE/mosaic/backups"
for ts in 20200101T000000Z 20210101T000000Z 20220101T000000Z 20230101T000000Z; do
mkdir -p "$STATE/mosaic/backups/pre-update-$ts"
done
set +e
env PATH="$ORIG_PATH" XDG_STATE_HOME="$STATE" MOSAIC_BACKUP_RETENTION=2 \
MOSAIC_HOME="$H" MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1 \
bash "$INSTALL" >"$OUT" 2>&1
rc=$?
set -e 2>/dev/null || true
printf '%s\t%s\t%s\t%s\n' "$rc" "$OUT" "$STATE" "$H"
)
chk "retention: upgrade succeeds" "[ '$rc' -eq 0 ]"
chk "retention: pruned to exactly 2 snapshots" "[ \$(find '$STATE/mosaic/backups' -maxdepth 1 -type d -name 'pre-update-*' | wc -l) -eq 2 ]"
chk "retention: newest pre-seeded survives" "[ -d '$STATE/mosaic/backups/pre-update-20230101T000000Z' ]"
chk "retention: oldest pre-seeded pruned" "[ ! -d '$STATE/mosaic/backups/pre-update-20200101T000000Z' ]"
rm -rf "$STATE" "$H"; rm -f "$OUT"
echo "── Part 5: post-sync verify restores an operator file (+ control) ──────"
# Shipped installer: the cp shim corrupts SOUL.md mid-sync; verify must restore it.
IFS=$'\t' read -r rc OUT STATE H < <(run_snap "$INSTALL" make_corrupt_shim)
chk "verify: upgrade still succeeds" "[ '$rc' -eq 0 ]"
chk "verify: SOUL.md restored to original" "[ \"\$(cat '$H/SOUL.md')\" = '$SOUL_ORIG' ]"
chk "verify: no corruption remains in SOUL.md" "! grep -q 'CORRUPTION-mid-sync' '$H/SOUL.md'"
chk "verify: loud restore warning emitted" "grep -qi 'restored from the pre-update snapshot' '$OUT'"
chk "verify: secret still not leaked" "! grep -q '$SECRET' '$OUT'"
rm -rf "$STATE" "$H"; rm -f "$OUT"
# Control: strip the verify call → the corruption must SURVIVE (net is load-bearing).
sed '/# VERIFY-NET/d' "$INSTALL" > "$VERIFYCTRL"
IFS=$'\t' read -r rc OUT STATE H < <(run_snap "$VERIFYCTRL" make_corrupt_shim)
chk "control: SOUL.md corruption survives" "grep -q 'CORRUPTION-mid-sync' '$H/SOUL.md'"
chk "control: no restore warning emitted" "! grep -qi 'restored from the pre-update snapshot' '$OUT'"
rm -rf "$STATE" "$H"; rm -f "$OUT"
echo "── Part 6: verify net honors an intentional migration removal (+ control) ─"
# BLOCKER regression: on a pre-v2 install, bin/ is operator-classified so the durable
# snapshot captures it — but the v1→v2 migration deletes bin/ ON PURPOSE. The verify
# net must SKIP that removal (is_migration_removed), or it heals bin/ back and the
# migration is silently undone forever once the version is stamped.
IFS=$'\t' read -r rc OUT STATE H < <(run_snap "$INSTALL" "" "" seed_home_v1)
chk "migration: upgrade succeeds" "[ '$rc' -eq 0 ]"
chk "migration: legacy bin/ stays removed" "[ ! -e '$H/bin' ]"
chk "migration: operator SOUL.md untouched" "[ \"\$(cat '$H/SOUL.md')\" = '$SOUL_ORIG' ]"
chk "migration: version stamped to $FRAMEWORK_VERSION" "[ \"\$(cat '$H/.framework-version')\" = '$FRAMEWORK_VERSION' ]"
rm -rf "$STATE" "$H"; rm -f "$OUT"
# Control: strip the MIGRATION-SKIP-GUARD → the verify net restores bin/ from the
# snapshot, silently undoing the migration (proves the guard is load-bearing).
MIGCTRL="$(mk_control 'MIGRATION-SKIP-GUARD' migration-control)"
IFS=$'\t' read -r rc OUT STATE H < <(run_snap "$MIGCTRL" "" "" seed_home_v1)
chk "control: bin/ wrongly restored by verify" "[ -e '$H/bin/tool.sh' ]"
rm -rf "$STATE" "$H"; rm -f "$OUT"
echo "── Part 7: verify net never restores a secret through a symlink (+ control) ─"
# HIGH (CWE-59) regression: an attacker who swaps an operator file for a symlink
# AFTER the durable snapshot must not cause the verify net's restore to write the
# snapshot's secret out THROUGH that link. The shipped net drops a symlinked leaf and
# writes a real file in its place, leaving the external target untouched.
EXFIL_DIR=$(mktemp -d); EXFIL_TARGET="$EXFIL_DIR/stolen"
printf 'ATTACKER-PLACEHOLDER\n' > "$EXFIL_TARGET"
IFS=$'\t' read -r rc OUT STATE H < <(run_snap "$INSTALL" make_symlink_leaf_shim)
chk "symlink-leaf: upgrade succeeds" "[ '$rc' -eq 0 ]"
chk "symlink-leaf: secret NOT written through link" "! grep -q '$SECRET' '$EXFIL_TARGET'"
chk "symlink-leaf: credentials.json is a real file" "[ -f '$H/tools/_lib/credentials.json' ] && [ ! -L '$H/tools/_lib/credentials.json' ]"
chk "symlink-leaf: credentials.json restored intact" "grep -q '$SECRET' '$H/tools/_lib/credentials.json'"
chk "symlink-leaf: secret not leaked to stdout/stderr" "! grep -q '$SECRET' '$OUT'"
rm -rf "$STATE" "$H" "$EXFIL_DIR"; rm -f "$OUT"
# Control: strip the SYMLINK-LEAF-GUARD → cp follows the swapped-in link and writes
# the snapshot secret out through it (proves the guard is load-bearing).
EXFIL_DIR=$(mktemp -d); EXFIL_TARGET="$EXFIL_DIR/stolen"
printf 'ATTACKER-PLACEHOLDER\n' > "$EXFIL_TARGET"
LEAFCTRL="$(mk_control 'SYMLINK-LEAF-GUARD' symlinkleaf-control)"
IFS=$'\t' read -r rc OUT STATE H < <(run_snap "$LEAFCTRL" make_symlink_leaf_shim)
chk "control: secret leaked through the symlink" "grep -q '$SECRET' '$EXFIL_TARGET'"
rm -rf "$STATE" "$H" "$EXFIL_DIR"; rm -f "$OUT"
echo "── Part 8: snapshot umask 077 does not leak into synced files (+ control) ──"
# SHOULD-FIX regression: umask 077 is process-global. Scoped to the snapshot it keeps
# backups 0600; leaked past it, every later cp/mkdir inherits 0600/0700. A freshly-
# synced framework file must be 0644 (per the ambient 022 umask) while the backup of
# a secret stays 0600.
IFS=$'\t' read -r rc OUT STATE H < <(umask 022; run_snap "$INSTALL")
SNAP="$(snap_dir "$STATE")"
chk "umask: upgrade succeeds" "[ '$rc' -eq 0 ]"
chk "umask: synced framework file is 0644" "[ \$(stat -c '%a' '$H/$POISON_REL') -eq 644 ]"
chk "umask: backup of a secret stays 0600" "[ \$(stat -c '%a' '$SNAP/tools/_lib/credentials.json') -eq 600 ]"
rm -rf "$STATE" "$H"; rm -f "$OUT"
# Control: strip the UMASK-RESTORE-NORMAL line → umask 077 leaks past the snapshot,
# so the newly-synced framework file is created 0600 (proves the restore matters).
UMASKCTRL="$(mk_control 'UMASK-RESTORE-NORMAL' umask-control)"
IFS=$'\t' read -r rc OUT STATE H < <(umask 022; run_snap "$UMASKCTRL")
chk "control: leaked umask makes synced file 0600" "[ \$(stat -c '%a' '$H/$POISON_REL') -eq 600 ]"
rm -rf "$STATE" "$H"; rm -f "$OUT"
echo ""
echo "RESULT: $pass passed, $fail failed"
[ "$fail" -eq 0 ]
@@ -0,0 +1,231 @@
#!/usr/bin/env bash
# test-upgrade-manifest-guard.sh — the #791 HARD GATE.
#
# Proves that a keep-mode framework upgrade (the `mosaic update` path:
# install.sh with MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1) touches NO path
# outside the framework-owned manifest. Every operator-owned sentinel — including
# a deliberately UNANTICIPATED one the manifest never names — must survive
# byte-identical with an unchanged mtime (not even rewritten). Framework files
# must still update, and a retired framework file inside a shipped subtree must
# still be pruned. No operator secret value may appear in installer output.
#
# Keep mode is a SINGLE code path (sync_framework_keep, a manifest-driven cp
# overlay + scoped prune — no rsync). The matrix still runs twice, once with
# rsync on PATH and once with it hidden, to prove the keep path is genuinely
# rsync-independent: it must obey the manifest identically whether or not rsync
# happens to be installed (rsync --delete is only ever used by overwrite mode,
# which has no operator state to protect).
#
# It also runs a fail-closed matrix (#791 B2/B3): an empty, operator-only,
# malformed, or missing manifest must ABORT the upgrade loudly and leave every
# operator path untouched — never silently no-op to "complete".
#
# Usage: bash test-upgrade-manifest-guard.sh
set -uo pipefail
FW="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" # packages/mosaic/framework
INSTALL="$FW/install.sh"
pass=0; fail=0
chk() { if eval "$2"; then echo "$1"; pass=$((pass + 1)); else echo "$1"; fail=$((fail + 1)); fi; }
# Redirect the #791 PR2 durable pre-update snapshot ($XDG_STATE_HOME/mosaic/backups)
# into a throwaway so a keep-mode upgrade under test never writes into the real
# ~/.local/state. This test asserts operator-surface fidelity, not backup content.
export XDG_STATE_HOME
XDG_STATE_HOME="$(mktemp -d)"
trap 'rm -rf "$XDG_STATE_HOME"' EXIT
SECRET='SUPER-SECRET-TOKEN-do-not-log-3f9a'
# Seed a throwaway MOSAIC_HOME with an operator sentinel per ownership class.
seed_home() {
local H="$1"
mkdir -p "$H/agents" "$H/policy" "$H/memory" "$H/tools/_lib" \
"$H/fleet/agents" "$H/fleet/run/sessions" "$H/harvester" \
"$H/unknown-operator-dir" "$H/guides"
printf '# persona\n' > "$H/SOUL.md" # marks a recognized existing install → keep mode
printf 'MODEL=opus\n' > "$H/agents/coder0.conf"
printf '# operator policy\n' > "$H/policy/custom.md"
printf '# soul overlay\n' > "$H/SOUL.local.md"
printf '# operator memory\n' > "$H/memory/note.md"
printf 'TOKEN=%s\n' "$SECRET" > "$H/tools/_lib/credentials.json"
printf 'MOSAIC_AGENT_NAME=coder0\n' > "$H/fleet/agents/coder0.env"
printf 'version: 2\nagents:\n - name: coder0\n' > "$H/fleet/roster.yaml"
printf '# harvester SOP\n' > "$H/harvester/sop.md"
printf 'operator data the manifest never anticipated\n' > "$H/unknown-operator-dir/x"
printf 'version: 1\nagents:\n - name: mine\n' > "$H/fleet/my-fleet.yaml"
# #797 Runtime Session Ledger (Mos-elevated to a #791 PR1 merge-blocker): a
# populated ledger under fleet/run/sessions/ must survive the upgrade — a
# runtime ledger an upgrade rsync can wipe is worthless. Seed it exactly as
# #797 writes it: a non-empty append journal + a non-empty compacted
# projection, files 0600 under a 0700 dir.
printf '%s\n%s\n%s\n' \
'{"seq":1,"kind":"session.spawn","node":"sess-42","generation":7}' \
'{"seq":2,"kind":"lease.grant","node":"sess-42","lease":"web1"}' \
'{"seq":3,"kind":"dispatch.create","from":"sess-42","to":"disp-9"}' \
> "$H/fleet/run/sessions/events.ndjson"
printf '%s\n' \
'{"generation":7,"nodes":[{"id":"sess-42","kind":"session"}],"edges":[{"from":"sess-42","to":"disp-9","kind":"dispatch"}]}' \
> "$H/fleet/run/sessions/ledger.json"
chmod 0700 "$H/fleet/run" "$H/fleet/run/sessions"
chmod 0600 "$H/fleet/run/sessions/events.ndjson" "$H/fleet/run/sessions/ledger.json"
# A retired framework file inside a shipped subtree (absent from source) — must be pruned.
printf '# retired guide\n' > "$H/guides/RETIRED-OLD-GUIDE.md"
echo 3 > "$H/.framework-version"
}
OPERATOR_SENTINELS=(
"agents/coder0.conf"
"policy/custom.md"
"SOUL.local.md"
"memory/note.md"
"tools/_lib/credentials.json"
"fleet/agents/coder0.env"
"fleet/roster.yaml"
"harvester/sop.md"
"unknown-operator-dir/x"
"fleet/my-fleet.yaml"
# #797 Runtime Session Ledger — populated journal + projection must survive.
"fleet/run/sessions/events.ndjson"
"fleet/run/sessions/ledger.json"
)
run_matrix() {
local label="$1"; shift # extra env / PATH override applied to the run
local H E OUT rel before_hash after_hash before_mt after_mt
H=$(mktemp -d); E=$(mktemp -d); OUT=$(mktemp)
seed_home "$H"
# Snapshot hash + mtime of every operator sentinel before the upgrade.
for rel in "${OPERATOR_SENTINELS[@]}"; do
sha256sum "$H/$rel" | awk '{print $1}' > "$E/$(echo "$rel" | tr / _).hash"
stat -c %Y "$H/$rel" > "$E/$(echo "$rel" | tr / _).mt"
done
# Snapshot the ledger directory permission bits (#797 assert: perms unchanged).
local before_dirperm after_dirperm
before_dirperm=$(stat -c %a "$H/fleet/run/sessions")
# The upgrade under test (keep + sync-only = the `mosaic update` reseed path).
MOSAIC_HOME="$H" MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1 "$@" bash "$INSTALL" >"$OUT" 2>&1
# HARD GATE: every operator sentinel survives byte-identical AND mtime-unchanged.
for rel in "${OPERATOR_SENTINELS[@]}"; do
before_hash=$(cat "$E/$(echo "$rel" | tr / _).hash")
before_mt=$(cat "$E/$(echo "$rel" | tr / _).mt")
after_hash=$(sha256sum "$H/$rel" 2>/dev/null | awk '{print $1}')
after_mt=$(stat -c %Y "$H/$rel" 2>/dev/null || echo MISSING)
chk "[$label] operator sentinel survives byte-identical: $rel" \
"[ -n '$after_hash' ] && [ '$before_hash' = '$after_hash' ]"
chk "[$label] operator sentinel not rewritten (mtime unchanged): $rel" \
"[ '$before_mt' = '$after_mt' ]"
done
# #797 assert 7: the ledger directory's permission bits are unchanged.
after_dirperm=$(stat -c %a "$H/fleet/run/sessions" 2>/dev/null || echo MISSING)
chk "[$label] ledger dir perms unchanged (#797): $before_dirperm" \
"[ '$before_dirperm' = '$after_dirperm' ]"
# Positive controls / negative controls — prove the test discriminates: the
# upgrade DOES write and prune framework-owned paths, so the operator sentinels
# (incl. the #797 ledger) survive because of the manifest, not because the
# upgrade is a no-op.
chk "[$label] positive control: framework file present after upgrade (guides synced)" \
"[ -f '$H/guides/E2E-DELIVERY.md' ]"
chk "[$label] negative control: retired framework file inside a subtree IS pruned" \
"[ ! -f '$H/guides/RETIRED-OLD-GUIDE.md' ]"
chk "[$label] manifest itself is installed" "[ -f '$H/framework-manifest.txt' ]"
# Secret-safety: the operator secret value never appears in installer output.
chk "[$label] operator secret value absent from installer stdout/stderr" \
"! grep -q '$SECRET' '$OUT'"
rm -rf "$H" "$E" "$OUT"
}
# Fail-closed matrix (#791 B2/B3 + blocker-1): run install.sh from a COPY of the
# framework so the shipped manifest can be corrupted. Every corruption must abort
# the upgrade non-zero with a manifest error, leaving all operator sentinels
# byte-identical AND on the SAME inode. The inode check is the load-bearing part:
# manifest validation is hoisted BEFORE make_snapshot/the restore trap, so a bad
# manifest must abort without ever snapshotting, deleting, and restoring the
# target. Were validation still armed under the ERR trap, restore_snapshot would
# rm -rf + rebuild the target — same bytes but a NEW inode (broken hard links,
# changed ctime), which a content-only hash would miss (#791 blocker-1).
run_failclosed() {
local label="$1" mutate="$2"
local SRC H E OUT rc rel before_hash after_hash before_ino after_ino key
SRC=$(mktemp -d); H=$(mktemp -d); E=$(mktemp -d); OUT=$(mktemp)
cp -a "$FW/." "$SRC/"
case "$mutate" in
empty) : > "$SRC/framework-manifest.txt" ;;
operator-only) printf '[operator]\nSOUL.md\n*.local.md\n' > "$SRC/framework-manifest.txt" ;;
malformed) printf 'stray.md\n[framework]\nguides/**\n' > "$SRC/framework-manifest.txt" ;;
degenerate) printf '[framework]\n/\n./\n[operator]\nSOUL.md\n' > "$SRC/framework-manifest.txt" ;;
missing) rm -f "$SRC/framework-manifest.txt" ;;
esac
seed_home "$H"
for rel in "${OPERATOR_SENTINELS[@]}"; do
key=$(echo "$rel" | tr / _)
sha256sum "$H/$rel" | awk '{print $1}' > "$E/$key.hash"
stat -c '%i' "$H/$rel" > "$E/$key.ino"
done
MOSAIC_HOME="$H" MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1 bash "$SRC/install.sh" >"$OUT" 2>&1
rc=$?
chk "[fail-closed:$label] upgrade aborts non-zero" "[ '$rc' -ne 0 ]"
chk "[fail-closed:$label] refuses loudly with a manifest error" \
"grep -qi 'manifest' '$OUT'"
for rel in "${OPERATOR_SENTINELS[@]}"; do
key=$(echo "$rel" | tr / _)
before_hash=$(cat "$E/$key.hash")
after_hash=$(sha256sum "$H/$rel" 2>/dev/null | awk '{print $1}')
chk "[fail-closed:$label] operator sentinel untouched: $rel" \
"[ -n '$after_hash' ] && [ '$before_hash' = '$after_hash' ]"
before_ino=$(cat "$E/$key.ino")
after_ino=$(stat -c '%i' "$H/$rel" 2>/dev/null)
chk "[fail-closed:$label] operator sentinel not deleted/recreated (inode stable): $rel" \
"[ -n '$after_ino' ] && [ '$before_ino' = '$after_ino' ]"
done
chk "[fail-closed:$label] operator secret value absent from output" \
"! grep -q '$SECRET' '$OUT'"
chmod -R u+w "$SRC" "$H" 2>/dev/null || true
rm -rf "$SRC" "$H" "$E" "$OUT"
}
echo "#791 upgrade manifest guard (HARD GATE):"
# 1) rsync path (if available on this host).
if command -v rsync >/dev/null 2>&1; then
run_matrix "rsync"
else
echo " · rsync not installed — skipping rsync-path matrix"
fi
# 2) rsync-absent path — hide rsync behind a scratch PATH. Keep mode never calls
# rsync, so this must resolve identically to run (1); it proves the keep path
# does not silently depend on rsync being installed. (Provide the coreutils the
# installer needs on the stripped PATH.)
FBIN=$(mktemp -d)
for t in bash cp find mktemp rm mkdir chmod cmp sed grep cat dirname basename stat sha256sum awk tr date sort; do
p=$(command -v "$t" 2>/dev/null) && ln -s "$p" "$FBIN/$t"
done
run_matrix "rsync-absent" env "PATH=$FBIN"
rm -rf "$FBIN"
# 3) fail-closed matrix (#791 B2/B3) — corrupt the shipped manifest four ways.
run_failclosed "empty-manifest" empty
run_failclosed "operator-only" operator-only
run_failclosed "malformed-manifest" malformed
run_failclosed "degenerate-framework" degenerate
run_failclosed "missing-manifest" missing
echo
echo "RESULT: $pass passed, $fail failed"
[ "$fail" -eq 0 ]
@@ -0,0 +1,366 @@
#!/usr/bin/env bash
# test-upgrade-rollback.sh — the #791 B1 regression gate.
#
# A keep-mode upgrade takes a pre-update snapshot and installs an ERR/INT/TERM
# trap that restores it if the sync aborts midway (install.sh: make_snapshot +
# `trap restore_snapshot`). That trap is only reached if `set -E` (errtrace) is
# active — otherwise a failure INSIDE sync_framework_keep() (which runs entirely
# in a function) never fires the trap, and the upgrade aborts leaving a
# half-written target with NO rollback. This test proves:
#
# Part A (the gate): the shipped installer rolls back a mid-sync failure —
# the restore message fires, the corrupted file is put
# back, AND the whole target is byte-identical to its
# pre-upgrade state.
# Part B (the control): the SAME installer with `-E` stripped does NOT roll back
# (dead trap) — the mid-sync corruption survives, proving
# errtrace is load-bearing. If anyone removes `set -E`,
# Part A goes red.
#
# The mid-sync failure is injected with a PATH-shadowing `cp` shim rather than
# file permissions. The earlier 0400/EACCES approach was NOT portable: Woodpecker
# runs steps as root (node:24-alpine has no USER directive), and root overwrites a
# 0400 file, so the failure never fired and this gate silently passed (#791
# blocker-3). The shim fails deterministically for one framework-owned
# destination regardless of uid, and — like a real interrupted cp (disk-full
# mid-write) — leaves a partially-written target behind, so rollback has real
# damage to undo and the control has real damage to expose.
#
# Usage: bash test-upgrade-rollback.sh
set -uo pipefail
FW="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" # packages/mosaic/framework
INSTALL="$FW/install.sh"
ORIG_PATH="$PATH"
# The `-E`-stripped control installer must live INSIDE $FW: install.sh derives
# SOURCE_DIR from its own path and `source`s $SOURCE_DIR/tools/_lib/manifest.sh,
# so a copy anywhere else aborts at the source line before ever reaching the sync
# loop — which would make the control a false negative. A root dotfile is
# operator-owned (unknown→operator), so the sync loop skips it. Clean up on exit.
STRIPPED="$FW/.install-rollback-control.tmp.sh"
SIGNALED="$FW/.install-signal-control.tmp.sh"
NOEXIT="$FW/.install-noexit-control.tmp.sh"
D1CTRL="$FW/.install-d1guard-control.tmp.sh"
D2CTRL="$FW/.install-d2guard-control.tmp.sh"
rm -f "$STRIPPED" "$SIGNALED" "$NOEXIT" "$D1CTRL" "$D2CTRL"
trap 'rm -f "$STRIPPED" "$SIGNALED" "$NOEXIT" "$D1CTRL" "$D2CTRL"' EXIT
pass=0; fail=0
chk() { if eval "$2"; then echo "$1"; pass=$((pass + 1)); else echo "$1"; fail=$((fail + 1)); fi; }
SECRET='SUPER-SECRET-TOKEN-do-not-log-b1'
# A framework-owned file the shim fails the copy of. The seeded target holds GOOD
# bytes; source ships different bytes, so sync_framework_keep() attempts the copy
# and the shim intercepts it. Root-level framework files sort before guides/, so
# several framework files are already refreshed when the copy reaches this one.
POISON_REL='guides/E2E-DELIVERY.md'
GOOD='GOOD-REFERENCE-CONTENT-pre-upgrade-b1'
GARBAGE='PARTIAL-WRITE-GARBAGE-mid-sync-b1'
# A `cp` shim: for the poisoned destination, simulate an interrupted copy — write
# partial garbage to the target, then fail — otherwise delegate to the real cp
# (resolved via the ORIGINAL PATH so make_snapshot/restore still work).
make_cp_shim() {
local dir="$1"
cat > "$dir/cp" <<SHIM
#!/usr/bin/env bash
dest="\${@: -1}"
case "\$dest" in
*/$POISON_REL)
printf '%s' '$GARBAGE' > "\$dest" 2>/dev/null || true
exit 1 ;;
esac
exec env PATH="$ORIG_PATH" cp "\$@"
SHIM
chmod +x "$dir/cp"
}
# A `find` shim that fails every enumeration scan (`-print0`) as if it hit an
# EACCES/I/O error partway — it emits the real (here: complete) list first, then
# exits non-zero, exactly the class of failure a `< <(find …)` process
# substitution silently swallows. All non-`-print0` finds (e.g. the -delete
# sweep) delegate to the real find on the original PATH. Used to prove #791
# blocker-D1: the shipped installer must honor find's exit status and roll back.
make_find_fail_shim() {
local dir="$1"
cat > "$dir/find" <<SHIM
#!/usr/bin/env bash
for a in "\$@"; do
if [ "\$a" = "-print0" ]; then
env PATH="$ORIG_PATH" find "\$@" # emit the real list…
exit 1 # …then fail as if the scan hit EACCES
fi
done
exec env PATH="$ORIG_PATH" find "\$@"
SHIM
chmod +x "$dir/find"
}
# An `rm` shim that fails ONLY `rm -rf <FAIL_RM_TARGET>` (the restore's target
# reset) and delegates every other rm to the real one. Used to prove #791
# blocker-D2: when the target reset inside restore_snapshot fails, the installer
# must emit the manual-recovery pointer (snapshot path) instead of exiting
# silently under `set -e`. FAIL_RM_TARGET is exported into the installer env.
make_rm_fail_shim() {
local dir="$1"
cat > "$dir/rm" <<'SHIM'
#!/usr/bin/env bash
last="${@: -1}"
if [ -n "${FAIL_RM_TARGET:-}" ] && [ "$last" = "$FAIL_RM_TARGET" ]; then
exit 1
fi
exec env PATH="$ORIG_PATH_FOR_RM" rm "$@"
SHIM
chmod +x "$dir/rm"
}
seed_home() {
local H="$1"
mkdir -p "$H/agents" "$H/tools/_lib" "$H/memory" "$H/guides"
printf '# persona\n' > "$H/SOUL.md" # recognized install → keep mode + snapshot
printf 'MODEL=opus\n' > "$H/agents/coder0.conf"
printf '# operator memory\n' > "$H/memory/note.md"
printf 'TOKEN=%s\n' "$SECRET" > "$H/tools/_lib/credentials.json"
echo 3 > "$H/.framework-version"
# Good pre-upgrade bytes; source ships different bytes, so cp is attempted.
printf '%s' "$GOOD" > "$H/$POISON_REL"
}
# Run one keep-mode upgrade against $1=installer, seeding a fresh home and a
# byte-for-byte reference of the pre-upgrade state, with the cp shim first on
# PATH. Echoes: "<exit>\t<out>\t<ref>\t<home>".
run_upgrade() {
local installer="$1" shim_maker="${2:-make_cp_shim}" H REF OUT SHIM rc
H=$(mktemp -d); REF=$(mktemp -d); OUT=$(mktemp); SHIM=$(mktemp -d)
seed_home "$H"
env PATH="$ORIG_PATH" cp -a "$H/." "$REF/" # pre-upgrade reference (real cp)
"$shim_maker" "$SHIM"
set +e
PATH="$SHIM:$ORIG_PATH" \
MOSAIC_HOME="$H" MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1 bash "$installer" >"$OUT" 2>&1
rc=$?
set -e 2>/dev/null || true
rm -rf "$SHIM"
printf '%s\t%s\t%s\t%s\n' "$rc" "$OUT" "$REF" "$H"
}
echo "#791 upgrade rollback (B1 regression gate):"
# ── Part A: the shipped installer must roll back a mid-sync failure ───────────
IFS=$'\t' read -r rcA OUTA REFA HA < <(run_upgrade "$INSTALL")
chk "[shipped] upgrade aborts non-zero on the injected mid-sync failure" \
"[ '$rcA' -ne 0 ]"
chk "[shipped] restore_snapshot fires (rollback message present)" \
"grep -q 'restoring previous state from snapshot' '$OUTA'"
chk "[shipped] the corrupted file is restored to its pre-upgrade bytes" \
"[ \"\$(cat '$HA/$POISON_REL')\" = '$GOOD' ]"
chk "[shipped] target rolled back byte-identical to pre-upgrade state" \
"diff -r '$REFA' '$HA' >/dev/null 2>&1"
chk "[shipped] operator secret value absent from installer output" \
"! grep -q '$SECRET' '$OUTA'"
# ── Part B: control — strip `-E`, the trap is dead, no rollback happens ───────
# Proves errtrace is what makes the trap reachable. If `set -E` is ever removed
# from install.sh, Part A's rollback assertions fail exactly like this control.
sed 's/^set -Eeuo pipefail/set -euo pipefail/' "$INSTALL" > "$STRIPPED"
chk "[control] the -E strip actually changed the installer" \
"! cmp -s '$INSTALL' '$STRIPPED'"
IFS=$'\t' read -r rcB OUTB REFB HB < <(run_upgrade "$STRIPPED")
chk "[control] without -E the upgrade still aborts non-zero" \
"[ '$rcB' -ne 0 ]"
# The load-bearing, deterministic proof of B1: without errtrace the ERR trap
# never fires for a failure inside sync_framework_keep(), so no rollback runs.
chk "[control] without -E the rollback message does NOT fire (dead trap)" \
"! grep -q 'restoring previous state from snapshot' '$OUTB'"
chk "[control] without -E the mid-sync corruption survives (no rollback)" \
"[ \"\$(cat '$HB/$POISON_REL')\" = '$GARBAGE' ]"
# ── Part C: an INT/TERM interrupt must terminate, not resume (blocker-A) ──────
# A bash signal trap that merely returns lets the script continue past the
# interrupt — restoring the snapshot, then resuming the sync and reporting
# success. The earlier test used a child cp shim to signal its parent, making
# child completion race Bash's interrupted wait. Concurrency is not part of the
# guarded property: sync_framework_keep() runs in the installer's own Bash
# process, and `kill` is a builtin. Generate two installer fixtures that signal
# themselves at the same known mid-sync point. Their TERM handlers emit the same
# observable before diverging, so missing signal delivery fails BOTH arms rather
# than manufacturing a pass. The only semantic difference between fixtures is
# the explicit `exit 1` whose load-bearing behavior this control proves.
TERM_MARKER='[test-control] TERM handler entered'
HANDLER_WITH_EXIT="trap 'echo \"$TERM_MARKER\" >&2; restore_snapshot; exit 1' TERM # TEST-TERM-HANDLER"
HANDLER_WITHOUT_EXIT="trap 'echo \"$TERM_MARKER\" >&2; restore_snapshot' TERM # TEST-TERM-HANDLER"
make_signal_installer() {
local output="$1" handler="$2"
local target_trap="trap 'restore_snapshot; exit 1' ERR INT TERM"
local target_cp=' cp "$abs" "$dst/$rel"'
local inject_open=" if [[ \"\$rel\" == \"$POISON_REL\" ]]; then"
local inject_kill=' kill -TERM "$$" # TEST-TERM-INJECTION'
local inject_close=' fi'
if ! awk \
-v target_trap="$target_trap" -v target_cp="$target_cp" \
-v handler="$handler" -v inject_open="$inject_open" \
-v inject_kill="$inject_kill" -v inject_close="$inject_close" '
$0 == target_cp {
print inject_open
print inject_kill
print inject_close
injection_sites++
}
{ print }
$0 == target_trap {
print handler
handler_sites++
}
END {
if (handler_sites != 1 || injection_sites != 1) exit 42
}
' "$INSTALL" > "$output"; then
rm -f "$output"
fail "Could not construct the self-TERM control installer at the exact trap/copy sites"
exit 1
fi
chmod +x "$output"
}
make_signal_installer "$SIGNALED" "$HANDLER_WITH_EXIT"
make_signal_installer "$NOEXIT" "$HANDLER_WITHOUT_EXIT"
signal_fixture_ready() {
local fixture="$1" expected_handler="$2"
[[ "$(grep -cF '# TEST-TERM-INJECTION' "$fixture")" -eq 1 ]] \
&& [[ "$(grep -cF '# TEST-TERM-HANDLER' "$fixture")" -eq 1 ]] \
&& grep -Fqx "$expected_handler" "$fixture"
}
signaled_fixture_ready() { signal_fixture_ready "$SIGNALED" "$HANDLER_WITH_EXIT"; }
noexit_fixture_ready() { signal_fixture_ready "$NOEXIT" "$HANDLER_WITHOUT_EXIT"; }
chk "[signal] shipped fixture has exactly one self-TERM injection and marked handler" \
"signaled_fixture_ready"
chk "[control] no-exit fixture has exactly one self-TERM injection and marked handler" \
"noexit_fixture_ready"
chk "[control] removing the explicit TERM exit changes the fixture" \
"! cmp -s '$SIGNALED' '$NOEXIT'"
# Run one keep-mode upgrade whose own shell delivers SIGTERM synchronously at
# the selected copy. Echoes "<exit>\t<out>\t<home>".
run_signal_upgrade() {
local installer="$1" H OUT rc
H=$(mktemp -d); OUT=$(mktemp)
seed_home "$H"
set +e
PATH="$ORIG_PATH" \
MOSAIC_HOME="$H" MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1 bash "$installer" >"$OUT" 2>&1
rc=$?
set -e 2>/dev/null || true
printf '%s\t%s\t%s\n' "$rc" "$OUT" "$H"
}
IFS=$'\t' read -r rcC OUTC HC < <(run_signal_upgrade "$SIGNALED")
chk "[signal] TERM handler observable fires exactly once" \
"[ \"\$(grep -cF '$TERM_MARKER' '$OUTC')\" -eq 1 ]"
chk "[signal] SIGTERM mid-sync aborts non-zero (trap exits, does not resume)" \
"[ '$rcC' -ne 0 ]"
chk "[signal] restore_snapshot fires on the interrupt" \
"grep -q 'restoring previous state from snapshot' '$OUTC'"
chk "[signal] does NOT resume to report sync success after the interrupt" \
"! grep -q 'file phase complete' '$OUTC'"
IFS=$'\t' read -r rcD OUTD HD < <(run_signal_upgrade "$NOEXIT")
chk "[control] TERM handler observable fires exactly once" \
"[ \"\$(grep -cF '$TERM_MARKER' '$OUTD')\" -eq 1 ]"
chk "[control] without 'exit 1' the handler restores before returning" \
"grep -q 'restoring previous state from snapshot' '$OUTD'"
chk "[control] without 'exit 1' the installer exits zero after resuming" \
"[ '$rcD' -eq 0 ]"
chk "[control] without 'exit 1' the trap resumes and reports sync success (the bug)" \
"grep -q 'file phase complete' '$OUTD'"
# ── Part D: a failed source/prune `find` scan must abort + roll back (D1) ─────
# A `< <(find …)` process substitution discards find's exit status, so an
# EACCES/I/O failure mid-scan would truncate the file list yet leave the reading
# loop exiting 0 — a partial upgrade committed and reported as success, with the
# ERR/restore trap never firing. The shipped installer captures the scan into a
# checked temp file (_scan_or_die) and aborts on failure. We inject a `find` that
# fails every `-print0` scan and assert the shipped installer rolls back.
IFS=$'\t' read -r rcE OUTE REFE HE < <(run_upgrade "$INSTALL" make_find_fail_shim)
chk "[find-fail] a failing framework scan aborts the upgrade non-zero" \
"[ '$rcE' -ne 0 ]"
chk "[find-fail] restore_snapshot fires on the aborted scan" \
"grep -q 'restoring previous state from snapshot' '$OUTE'"
chk "[find-fail] the abort is a fail-closed enumeration error (not a silent truncation)" \
"grep -q 'Could not enumerate framework files' '$OUTE'"
chk "[find-fail] target rolled back byte-identical to pre-upgrade state" \
"diff -r '$REFE' '$HE' >/dev/null 2>&1"
# Control: neuter the D1 guard (turn its `return 1` into a no-op) so a find
# failure is swallowed exactly as `< <(find …)` would — the scan appears to
# succeed and the upgrade reports completion with NO rollback.
sed 's/return 1 # D1-GUARD/: # D1-GUARD-DISABLED/' "$INSTALL" > "$D1CTRL"
chk "[control] the D1-guard strip actually changed the installer" \
"! cmp -s '$INSTALL' '$D1CTRL'"
IFS=$'\t' read -r _rcF OUTF REFF HF < <(run_upgrade "$D1CTRL" make_find_fail_shim)
chk "[control] with the D1 guard disabled the find failure is swallowed (no rollback)" \
"! grep -q 'restoring previous state from snapshot' '$OUTF'"
chk "[control] with the D1 guard disabled the upgrade wrongly reports success" \
"grep -q 'file phase complete' '$OUTF'"
# ── Part E: a failed target reset inside restore must not exit silently (D2) ──
# restore_snapshot resets the target (`rm -rf; mkdir -p`) before rebuilding from
# the snapshot. Under `set -e` (trap disarmed) a bare reset that fails would exit
# the whole script immediately — after `rm` may have deleted part of the target —
# WITHOUT printing where the snapshot lives. We trigger a rollback (cp poison) AND
# fail the target reset (rm shim), then assert the shipped installer emits the
# manual-recovery pointer and preserves the snapshot.
run_rmfail_upgrade() {
local installer="$1" H OUT SHIM rc
H=$(mktemp -d); OUT=$(mktemp); SHIM=$(mktemp -d)
seed_home "$H"
make_cp_shim "$SHIM" # poison cp → triggers the abort + restore
make_rm_fail_shim "$SHIM" # rm -rf <H> fails → exercises the D2 reset guard
set +e
PATH="$SHIM:$ORIG_PATH" ORIG_PATH_FOR_RM="$ORIG_PATH" FAIL_RM_TARGET="$H" \
MOSAIC_HOME="$H" MOSAIC_INSTALL_MODE=keep MOSAIC_SYNC_ONLY=1 bash "$installer" >"$OUT" 2>&1
rc=$?
set -e 2>/dev/null || true
rm -rf "$SHIM"
printf '%s\t%s\t%s\n' "$rc" "$OUT" "$H"
}
IFS=$'\t' read -r rcG OUTG HG < <(run_rmfail_upgrade "$INSTALL")
chk "[reset-fail] a failed target reset still aborts non-zero" \
"[ '$rcG' -ne 0 ]"
chk "[reset-fail] the manual-recovery pointer is emitted (not a silent set -e exit)" \
"grep -q 'Snapshot restore could not reset' '$OUTG'"
chk "[reset-fail] the recovery message points at a preserved snapshot dir" \
"grep -q 'preserved at: .*mosaic-snapshot' '$OUTG'"
SNAP_E="$(grep -m1 -o '/[^ ]*mosaic-snapshot[^ ]*' "$OUTG")"
chk "[reset-fail] the named snapshot directory actually survives for recovery" \
"[ -n '$SNAP_E' ] && [ -d '$SNAP_E' ]"
chk "[reset-fail] operator secret value never appears in installer output" \
"! grep -q '$SECRET' '$OUTG'"
# Control: delete the D2 recovery line so a failed reset returns non-zero with NO
# operator pointer — the observable defect (half-reset target, snapshot orphaned
# in /tmp with no path told to the operator). Proves the message is load-bearing.
sed '/Snapshot restore could not reset/d' "$INSTALL" > "$D2CTRL"
chk "[control] the D2-recovery strip actually changed the installer" \
"! cmp -s '$INSTALL' '$D2CTRL'"
IFS=$'\t' read -r _rcH OUTH HH < <(run_rmfail_upgrade "$D2CTRL")
chk "[control] without the D2 recovery line the operator gets no snapshot pointer" \
"! grep -q 'Snapshot restore could not reset' '$OUTH'"
[ -n "${SNAP_E:-}" ] && rm -rf "$SNAP_E"
# Reap any snapshot the reset-fail runs left in /tmp (reset failed → never cleaned).
orphan_snapshot="$(grep -m1 -o '/[^ ]*mosaic-snapshot[^ ]*' "$OUTH" 2>/dev/null || true)"
[ -n "$orphan_snapshot" ] && rm -rf "$orphan_snapshot"
# Cleanup (generated installer controls are also removed by the EXIT trap).
for d in "$HA" "$REFA" "$HB" "$REFB" "$HC" "$HD" "$HE" "$REFE" "$HF" "$REFF" "$HG" "$HH"; do rm -rf "$d"; done
rm -f "$OUTA" "$OUTB" "$OUTC" "$OUTD" "$OUTE" "$OUTF" "$OUTG" "$OUTH" \
"$STRIPPED" "$SIGNALED" "$NOEXIT" "$D1CTRL" "$D2CTRL"
echo
echo "RESULT: $pass passed, $fail failed"
[ "$fail" -eq 0 ]
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
# verify-sanitized.sh — blocking CI gate: the public framework package must
# contain no operator-specific personal data or private executable defaults.
#
# Two rule classes, with DELIBERATELY DIFFERENT scopes:
# 1. DENYLIST (identity) — a LABELED, one-time regression guard for the CURRENT
# operator's identity tokens. Scanned EVERYWHERE including examples/, because a
# jarvis/jason/private-home regression in a SHIPPED example would break the
# open-source guarantee just as badly as one in a default. NOT a general PII
# detector (a future operator's name can't be enumerated) — the durable control
# is the L0 framework-PR firewall + human review; this just stops re-contamination.
# 2. STRUCTURAL (private $HOME default in *.sh) — scanned everywhere EXCEPT examples/,
# because worked example overlays/personas legitimately show placeholder paths.
#
# File types: *.md, *.sh, *.ps1, *.json, *.yml/*.yaml, *.toml, *.env, *.service, and the CLI scripts under
# tools/_scripts/. Excludes node_modules/ and this gate file.
#
# NOTE: '\bPDA\b' intentionally matches "PDA-friendly" (the contamination removed in P2);
# a hyphen is not a \b word boundary on the right, so "PDA-foo" matches. If a future
# legitimate doc needs the literal token "PDA" in a non-personal sense, reword it or
# narrow this rule — do not weaken the gate silently.
#
# NOTE: private THIRD-PARTY host refs (e.g. a maintainer's employer Gitea) are NOT in
# this denylist — they are functionally entangled in host-routing + test fixtures and
# tracked as a separate follow-up.
#
# Usage: verify-sanitized.sh [FRAMEWORK_ROOT]
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FRAMEWORK_ROOT="${1:-$(cd "$SCRIPT_DIR/../../.." && pwd)}"
SELF_REL="tools/quality/scripts/verify-sanitized.sh"
DENYLIST='jarvis|jason|woltje|brain\.woltje\.com|/home/jwoltje|\bPDA\b'
STRUCTURAL_SH=':[-=]\$\{?HOME\}?/src/'
cd "$FRAMEWORK_ROOT" || { echo "FRAMEWORK_ROOT not found: $FRAMEWORK_ROOT" >&2; exit 3; }
# Identity scope = ALL shipped text files (examples/ INCLUDED).
_files_identity() {
find . -type f \
\( -name '*.md' -o -name '*.sh' -o -name '*.ps1' -o -name '*.json' -o -name '*.yml' -o -name '*.yaml' -o -name '*.toml' -o -name '*.env' -o -name '*.service' -o -path '*/tools/_scripts/*' \) \
-not -path '*/node_modules/*' -not -path "./$SELF_REL" -print0
}
# Structural scope = shipped scripts, examples/ EXCLUDED.
_files_structural() {
find . -type f \( -name '*.sh' -o -path '*/tools/_scripts/*' \) \
-not -path '*/examples/*' -not -path '*/node_modules/*' -not -path "./$SELF_REL" -print0
}
# ---- self-test FIRST: a broken regex must never silently no-op the gate ----
_selftest() {
local tmp; tmp="$(mktemp -d)" || return 1
printf 'contact jason.woltje at jarvis-brain (PDA-friendly)\n' > "$tmp/planted.md"
printf 'X="${VAR:-$HOME/src/whatever/x.json}"\n' > "$tmp/planted.sh"
printf 'name: jason-woltje\n' > "$tmp/planted.yaml"
printf '[Service]\nUser=jarvis\n' > "$tmp/planted.service"
local rc=0
grep -qIEi "$DENYLIST" "$tmp/planted.md" || { echo "✗ SELF-TEST: identity denylist regex broken" >&2; rc=1; }
grep -qIE "$STRUCTURAL_SH" "$tmp/planted.sh" || { echo "✗ SELF-TEST: structural regex broken" >&2; rc=1; }
# Prove the identity scan covers the config formats it claims to (yaml/service/etc).
local n_ext
n_ext=$(find "$tmp" -type f \( -name '*.yaml' -o -name '*.service' \) -print0 | xargs -0 -r grep -lIEi "$DENYLIST" 2>/dev/null | wc -l)
[[ "$n_ext" -eq 2 ]] || { echo "✗ SELF-TEST: identity scan does not cover .yaml/.service extensions" >&2; rc=1; }
rm -rf "$tmp"; return $rc
}
_selftest || exit 2
fail=0
deny_hits="$(_files_identity | xargs -0 -r grep -nIEi "$DENYLIST" 2>/dev/null || true)"
if [[ -n "$deny_hits" ]]; then
echo "✗ [denylist] operator-identity tokens in shipped files (examples/ included):"
echo "$deny_hits" | sed "s#^\./##; s/^/ /"
fail=1
fi
struct_hits="$(_files_structural | xargs -0 -r grep -nIE "$STRUCTURAL_SH" 2>/dev/null || true)"
if [[ -n "$struct_hits" ]]; then
echo "✗ [structural] private \$HOME/src default in a shipped script:"
echo "$struct_hits" | sed "s#^\./##; s/^/ /"
fail=1
fi
if [[ "$fail" -ne 0 ]]; then
echo
echo "Sanitization gate FAILED. Public framework files must not contain operator identity" >&2
echo "or private \$HOME defaults. Move personal content to init-generated files or genericize." >&2
exit 1
fi
echo "✓ sanitization gate passed (identity scan incl. examples/; structural scan excl. examples/)"
@@ -0,0 +1,91 @@
# Quality Rails Verification Script (Windows)
Write-Host "═══════════════════════════════════════════"
Write-Host "Quality Rails Enforcement Verification"
Write-Host "═══════════════════════════════════════════"
Write-Host ""
$Passed = 0
$Failed = 0
# Test 1: Type error blocked
Write-Host "Test 1: Type errors should be blocked..."
"const x: string = 123;" | Out-File -FilePath test-file.ts -Encoding utf8
git add test-file.ts 2>$null
$output = git commit -m "Test commit" 2>&1 | Out-String
if ($output -match "error") {
Write-Host "✅ PASS: Type errors blocked" -ForegroundColor Green
$Passed++
} else {
Write-Host "❌ FAIL: Type errors NOT blocked" -ForegroundColor Red
$Failed++
}
git reset HEAD test-file.ts 2>$null
Remove-Item test-file.ts -ErrorAction SilentlyContinue
# Test 2: any type blocked
Write-Host ""
Write-Host "Test 2: 'any' types should be blocked..."
"const x: any = 123;" | Out-File -FilePath test-file.ts -Encoding utf8
git add test-file.ts 2>$null
$output = git commit -m "Test commit" 2>&1 | Out-String
if ($output -match "no-explicit-any") {
Write-Host "✅ PASS: 'any' types blocked" -ForegroundColor Green
$Passed++
} else {
Write-Host "❌ FAIL: 'any' types NOT blocked" -ForegroundColor Red
$Failed++
}
git reset HEAD test-file.ts 2>$null
Remove-Item test-file.ts -ErrorAction SilentlyContinue
# Test 3a: gitleaks binary must be present
Write-Host ""
Write-Host "Test 3a: gitleaks must be installed..."
$gitleaksPath = Get-Command gitleaks -ErrorAction SilentlyContinue
if ($gitleaksPath) {
$gitleaksVer = & gitleaks version 2>&1 | Out-String
Write-Host "✅ PASS: gitleaks found ($($gitleaksVer.Trim()))" -ForegroundColor Green
$Passed++
} else {
Write-Host "❌ FAIL: gitleaks is NOT installed — secret scanning will not work" -ForegroundColor Red
Write-Host " Install: winget install gitleaks"
$Failed++
}
# Test 3b: gitleaks detects a planted AWS key
Write-Host ""
Write-Host "Test 3b: gitleaks should detect planted AWS key..."
if ($gitleaksPath) {
"aws_access_key_id = AKIAIOSFODNN7REALKEY" | Out-File -FilePath gitleaks-test-secret.txt -Encoding utf8
git add gitleaks-test-secret.txt 2>$null
$output = & gitleaks git --pre-commit --staged --redact 2>&1 | Out-String
if ($output -match "leak|finding") {
Write-Host "✅ PASS: gitleaks detected planted secret" -ForegroundColor Green
$Passed++
} else {
Write-Host "❌ FAIL: gitleaks did NOT detect planted secret" -ForegroundColor Red
$Failed++
}
git reset HEAD gitleaks-test-secret.txt 2>$null
Remove-Item gitleaks-test-secret.txt -ErrorAction SilentlyContinue
} else {
Write-Host "⚠ SKIP: gitleaks not installed (Test 3a already failed)"
}
# Summary
Write-Host ""
Write-Host "═══════════════════════════════════════════"
Write-Host "Verification Summary"
Write-Host "═══════════════════════════════════════════"
Write-Host "✅ Passed: $Passed"
Write-Host "❌ Failed: $Failed"
Write-Host ""
if ($Failed -eq 0) {
Write-Host "🎉 All tests passed! Quality enforcement is working." -ForegroundColor Green
exit 0
} else {
Write-Host "⚠ Some tests failed. Review configuration." -ForegroundColor Yellow
exit 1
}
@@ -0,0 +1,104 @@
#!/bin/bash
# Quality Rails Verification Script
# Tests that enforcement actually works
echo "═══════════════════════════════════════════"
echo "Quality Rails Enforcement Verification"
echo "═══════════════════════════════════════════"
echo ""
PASSED=0
FAILED=0
# Test 1: Type error blocked
echo "Test 1: Type errors should be blocked..."
echo "const x: string = 123;" > test-file.ts
git add test-file.ts 2>/dev/null
if git commit -m "Test commit" 2>&1 | grep -q "error"; then
echo "✅ PASS: Type errors blocked"
((PASSED++))
else
echo "❌ FAIL: Type errors NOT blocked"
((FAILED++))
fi
git reset HEAD test-file.ts 2>/dev/null
rm test-file.ts 2>/dev/null
# Test 2: any type blocked
echo ""
echo "Test 2: 'any' types should be blocked..."
echo "const x: any = 123;" > test-file.ts
git add test-file.ts 2>/dev/null
if git commit -m "Test commit" 2>&1 | grep -q "no-explicit-any"; then
echo "✅ PASS: 'any' types blocked"
((PASSED++))
else
echo "❌ FAIL: 'any' types NOT blocked"
((FAILED++))
fi
git reset HEAD test-file.ts 2>/dev/null
rm test-file.ts 2>/dev/null
# Test 3a: gitleaks binary must be present
echo ""
echo "Test 3a: gitleaks must be installed..."
if command -v gitleaks &> /dev/null; then
echo "✅ PASS: gitleaks found ($(gitleaks version 2>/dev/null || echo 'unknown version'))"
PASSED=$((PASSED + 1))
else
echo "❌ FAIL: gitleaks is NOT installed — secret scanning will not work"
echo " Install: https://github.com/gitleaks/gitleaks#installing"
FAILED=$((FAILED + 1))
fi
# Test 3b: gitleaks detects a planted AWS key
echo ""
echo "Test 3b: gitleaks should detect planted AWS key..."
if command -v gitleaks &> /dev/null; then
echo 'aws_access_key_id = AKIAIOSFODNN7REALKEY' > gitleaks-test-secret.txt
git add gitleaks-test-secret.txt 2>/dev/null
if gitleaks git --pre-commit --staged --redact 2>&1 | grep -q -i "leak\|finding"; then
echo "✅ PASS: gitleaks detected planted secret"
PASSED=$((PASSED + 1))
else
echo "❌ FAIL: gitleaks did NOT detect planted secret"
FAILED=$((FAILED + 1))
fi
git reset HEAD gitleaks-test-secret.txt 2>/dev/null
rm gitleaks-test-secret.txt 2>/dev/null
else
echo "⚠ SKIP: gitleaks not installed (Test 3a already failed)"
fi
# Test 4: Lint error blocked
echo ""
echo "Test 4: Lint errors should be blocked..."
echo "const x=123" > test-file.ts # Missing semicolon
git add test-file.ts 2>/dev/null
if git commit -m "Test commit" 2>&1 | grep -q "prettier"; then
echo "✅ PASS: Lint errors blocked"
((PASSED++))
else
echo "❌ FAIL: Lint errors NOT blocked"
((FAILED++))
fi
git reset HEAD test-file.ts 2>/dev/null
rm test-file.ts 2>/dev/null
# Summary
echo ""
echo "═══════════════════════════════════════════"
echo "Verification Summary"
echo "═══════════════════════════════════════════"
echo "✅ Passed: $PASSED"
echo "❌ Failed: $FAILED"
echo ""
if [ $FAILED -eq 0 ]; then
echo "🎉 All tests passed! Quality enforcement is working."
exit 0
else
echo "⚠ Some tests failed. Review configuration."
exit 1
fi