diff --git a/docs/scratchpads/1194-framework-tool-drift.md b/docs/scratchpads/1194-framework-tool-drift.md index d026323f..469a9d53 100644 --- a/docs/scratchpads/1194-framework-tool-drift.md +++ b/docs/scratchpads/1194-framework-tool-drift.md @@ -8,11 +8,12 @@ The reported queue-guard source defect was already fixed on `main` by `58b971ab` ## Classification -The existing `framework-manifest.txt` is authoritative: +The existing `framework-manifest.txt` is authoritative. The detector invokes the canonical shared `tools/_lib/manifest.sh classify` implementation over the complete source census and refuses missing, unreadable, malformed, incomplete, or zero-framework ownership output. Policy is therefore read rather than duplicated: -- Every source file under `tools/**` is framework-owned and required in the deployed tools tree. -- `tools/_lib/credentials.json` is explicitly operator-owned and excluded from byte comparison. -- A file present only in the deployed tools tree is operator-owned/unknown by the manifest's fail-safe default. The detector reports it as `INSTALLED_ONLY operator-or-unknown` but does not fail or delete it. +- Current policy classifies source files under `tools/**` as framework-owned and required in the deployed tools tree. +- Current policy explicitly classifies `tools/_lib/credentials.json` operator-owned and excludes it from byte comparison; future policy changes take effect without a detector edit. +- A file present only in the deployed tools tree is operator-owned/unknown by the manifest's fail-safe default. The detector reports it as `INSTALLED_ONLY operator-or-unknown` under `--verbose` but does not fail or delete it. +- Empty/partial source traversal, unreadable directories/files, symlinked census entries, root aliases, and descendant source aliases all return `CANNOT_ASSERT` rather than manufacturing agreement. This means `NOT_INSTALLED` is not suppressed by filename guesses such as “test” or “README”: if it ships below source `tools/**`, the installer contract says it should be installed. Source-only implementation files outside `tools/**` are outside this detector population by construction. @@ -60,4 +61,11 @@ Do not run this while agent seats are active: the stale set includes identity se ## Probe evidence -The detector regression constructs a stale installed tool plus a missing shipped tool and observes rc 1 with distinct `STALE` and `NOT_INSTALLED` lines. That case would pass or be invisible before this change because no installed-vs-shipped comparison existed. Controls prove byte-identical tools pass, the credential carve-out is ignored, installed-only paths are classified without deletion/failure, and comparing a tree to itself refuses with `CANNOT_ASSERT`. +The detector regression constructs a stale installed tool plus a missing shipped tool and observes rc 1 with distinct `STALE` and `NOT_INSTALLED` lines. That case would pass or be invisible before this change because no installed-vs-shipped comparison existed. Additional review-red controls prove: + +- empty and unreadable source censuses return `CANNOT_ASSERT` (they returned clean rc 0 at the first PR head); +- deleting the manifest returns `CANNOT_ASSERT`, while changing manifest ownership changes the verdict through the canonical resolver (the first head never opened the manifest); +- root and descendant symlink/source aliases cannot return clean (the first head returned clean for a source-backed installed subtree); +- a checker hung during doctor is terminated by a bounded watchdog, emits `CANNOT_ASSERT`, and doctor reaches its final warnings line (the first head hung and suppressed the remaining audit). + +Controls retain byte-identical success, exact credential carve-out behavior, and installed-only preservation. diff --git a/packages/mosaic/framework/tools/_scripts/mosaic-doctor b/packages/mosaic/framework/tools/_scripts/mosaic-doctor index a31c889f..52d81df7 100755 --- a/packages/mosaic/framework/tools/_scripts/mosaic-doctor +++ b/packages/mosaic/framework/tools/_scripts/mosaic-doctor @@ -160,11 +160,26 @@ echo "[mosaic-doctor] Mosaic home: $MOSAIC_HOME" framework_drift_checker="$(cd -- "$(dirname -- "$0")/../quality/scripts" && pwd)/framework-drift-check.py" if [[ -f "$framework_drift_checker" ]]; then echo "[mosaic-doctor] Checking installed framework-tool drift..." - if python3 "$framework_drift_checker" --installed-root "$MOSAIC_HOME/tools"; then - pass "Installed framework tools match shipped source" - else + drift_timeout="${MOSAIC_DOCTOR_DRIFT_TIMEOUT_SEC:-15}" + if ! [[ "$drift_timeout" =~ ^[1-9][0-9]*$ ]]; then + warn "Invalid MOSAIC_DOCTOR_DRIFT_TIMEOUT_SEC='$drift_timeout' (expected positive integer); using 15s" + drift_timeout=15 + fi + if command -v timeout >/dev/null 2>&1; then + set +e + timeout --signal=TERM --kill-after=2 "${drift_timeout}s" \ + python3 "$framework_drift_checker" --installed-root "$MOSAIC_HOME/tools" drift_rc=$? - warn "Installed framework-tool drift detected (checker exit $drift_rc; no files changed)" + set -e + if [[ "$drift_rc" -eq 0 ]]; then + pass "Installed framework tools match shipped source" + elif [[ "$drift_rc" -eq 124 || "$drift_rc" -eq 137 ]]; then + warn "CANNOT_ASSERT framework drift checker timed out after ${drift_timeout}s; continuing remaining doctor checks" + else + warn "Installed framework-tool drift detected (checker exit $drift_rc; no files changed)" + fi + else + warn "CANNOT_ASSERT timeout utility unavailable; refusing unbounded framework drift check and continuing remaining doctor checks" fi else warn "Framework drift checker is absent from the shipped tools tree" diff --git a/packages/mosaic/framework/tools/quality/scripts/framework-drift-check.py b/packages/mosaic/framework/tools/quality/scripts/framework-drift-check.py old mode 100644 new mode 100755 index d1bb679e..748ae28f --- a/packages/mosaic/framework/tools/quality/scripts/framework-drift-check.py +++ b/packages/mosaic/framework/tools/quality/scripts/framework-drift-check.py @@ -1,11 +1,5 @@ #!/usr/bin/env python3 -"""Compare deployed Mosaic framework tools with the shipped framework source. - -The framework ownership manifest declares tools/** framework-owned. Consequently every -regular file shipped below source tools/ is expected below MOSAIC_HOME/tools/, except -the explicit operator credential carve-out. Files that exist only in the deployed tree -are operator/unknown state and are reported but never treated as framework drift. -""" +"""Fail-closed comparison of deployed Mosaic tools to manifest-owned shipped tools.""" from __future__ import annotations @@ -13,10 +7,10 @@ import argparse import hashlib import os from pathlib import Path +import stat +import subprocess import sys -OPERATOR_CARVE_OUTS = {"_lib/credentials.json"} - def digest(path: Path) -> str: value = hashlib.sha256() @@ -27,76 +21,150 @@ def digest(path: Path) -> str: def default_source_tools() -> Path: - # .../tools/quality/scripts/framework-drift-check.py -> .../tools return Path(__file__).resolve().parents[2] def normalize_source(path: Path) -> Path: candidate = path.resolve() - if (candidate / "tools").is_dir(): - candidate = candidate / "tools" - return candidate + return candidate / "tools" if (candidate / "tools").is_dir() else candidate -def files_below(root: Path) -> dict[str, Path]: - return { - path.relative_to(root).as_posix(): path - for path in root.rglob("*") - if path.is_file() - } +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(), - help="shipped framework root or tools root (default: this script's shipped tools tree)", - ) - parser.add_argument( - "--installed-root", - type=Path, - default=Path(os.environ.get("MOSAIC_HOME", Path.home() / ".config/mosaic")) / "tools", - help="deployed tools root (default: $MOSAIC_HOME/tools)", - ) - parser.add_argument("--verbose", action="store_true", help="list in-sync paths too") + 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() - if not source.is_dir(): - print(f"[framework-drift] CANNOT_ASSERT source tools missing: {source}", file=sys.stderr) - return 2 - if not installed.is_dir(): - print(f"[framework-drift] CANNOT_ASSERT installed tools missing: {installed}", file=sys.stderr) - return 2 - if source == installed: - print( - "[framework-drift] CANNOT_ASSERT source and installed roots are identical; " - "run the checker from the bundled package or pass --source-root", - file=sys.stderr, - ) + 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 - source_files = files_below(source) - installed_files = files_below(installed) - required = sorted(set(source_files) - OPERATOR_CARVE_OUTS) 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) - elif digest(source_files[relative]) == digest(deployed): - in_sync.append(relative) - else: - stale.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 - installed_only = sorted(set(installed_files) - set(source_files) - OPERATOR_CARVE_OUTS) + 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}") @@ -104,6 +172,8 @@ def main() -> int: 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}") @@ -111,19 +181,11 @@ def main() -> int: print( "[framework-drift] summary " f"in-sync={len(in_sync)} stale={len(stale)} not-installed={len(not_installed)} " - f"installed-only={len(installed_only)}" + f"unsafe-alias={len(unsafe_alias)} installed-only={len(installed_only)}" ) - print( - "[framework-drift] classification tools/**=framework-owned-required; " - "tools/_lib/credentials.json=operator-owned-excluded; " - "installed-only=operator-or-unknown-preserved" - ) - if stale or not_installed: - print( - "[framework-drift] FAIL deployed framework tools do not match shipped source; " - "schedule a reviewed framework reseed", - file=sys.stderr, - ) + 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 diff --git a/packages/mosaic/framework/tools/quality/scripts/test-framework-drift-check.py b/packages/mosaic/framework/tools/quality/scripts/test-framework-drift-check.py old mode 100644 new mode 100755 index cae4dd6c..ecba1e95 --- a/packages/mosaic/framework/tools/quality/scripts/test-framework-drift-check.py +++ b/packages/mosaic/framework/tools/quality/scripts/test-framework-drift-check.py @@ -3,73 +3,110 @@ 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.source = root / "framework" / "tools" + 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.source.parent), "--installed-root", str(self.installed), *extra], - text=True, - capture_output=True, - check=False, + [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") - (self.installed / "_lib" / "credentials.json").write_text("secret\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.assertNotIn("STALE _lib/credentials.json", result.stdout) - self.assertNotIn("NOT_INSTALLED _lib/credentials.json", result.stdout) - self.assertIn("in-sync=0 stale=1 not-installed=1 installed-only=1", result.stdout) self.assertIn("FAIL deployed framework tools", result.stderr) - def test_passes_only_when_every_framework_owned_source_file_matches(self) -> None: - (self.installed / "git" / "guard.sh").write_text("fixed\n") - (self.installed / "git" / "new-wrapper.sh").write_text("new\n") - (self.installed / "_lib" / "credentials.json").write_text("different-operator-secret\n") - + 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("in-sync=2 stale=0 not-installed=0 installed-only=0", result.stdout) + self.assertIn("stale=0 not-installed=0 unsafe-alias=0", result.stdout) - def test_refuses_self_comparison_that_would_make_drift_unobservable(self) -> None: - result = subprocess.run( - [sys.executable, str(CHECKER), "--source-root", str(self.source), "--installed-root", str(self.source)], - text=True, - capture_output=True, - check=False, - ) + 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("source and installed roots are identical", result.stderr) + 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__": diff --git a/packages/mosaic/framework/tools/quality/scripts/test-framework-drift-doctor.sh b/packages/mosaic/framework/tools/quality/scripts/test-framework-drift-doctor.sh new file mode 100755 index 00000000..3a237a2e --- /dev/null +++ b/packages/mosaic/framework/tools/quality/scripts/test-framework-drift-doctor.sh @@ -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" diff --git a/packages/mosaic/package.json b/packages/mosaic/package.json index 618c219e..d6a81a44 100644 --- a/packages/mosaic/package.json +++ b/packages/mosaic/package.json @@ -25,7 +25,7 @@ "lint": "eslint src", "typecheck": "tsc --noEmit", "test": "vitest run --passWithNoTests && pnpm run test:framework-shell", - "test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 framework/tools/quality/scripts/test-framework-drift-check.py && bash framework/tools/fleet/test-start-agent-session.sh && bash framework/systemd/user/test-fleet-units.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-edit.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/git/test-explain-diagnostic-status-neutral.sh && bash framework/tools/git/test-detect-platform-outside-repo.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh" + "test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 framework/tools/quality/scripts/test-framework-drift-check.py && bash framework/tools/quality/scripts/test-framework-drift-doctor.sh && bash framework/tools/fleet/test-start-agent-session.sh && bash framework/systemd/user/test-fleet-units.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-edit.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-tristate.sh && bash framework/tools/git/test-ci-queue-wait-github-checks.sh && bash framework/tools/git/test-pr-merge-queue-branch.sh && bash framework/tools/git/test-pr-merge-head-pin.sh && bash framework/tools/git/test-pr-merge-message-field.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/git/test-explain-diagnostic-status-neutral.sh && bash framework/tools/git/test-detect-platform-outside-repo.sh && bash framework/tools/woodpecker/test-terminal-green-contract.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh" }, "dependencies": { "@mosaicstack/brain": "workspace:*",