#!/usr/bin/env bash # Regression tests for the fail-closed schema-v3-only absence gate. set -euo pipefail project_root="$(cd "$(dirname "$0")/.." && pwd -P)" gate="$project_root/scripts/verify-schema-v3-only.sh" gate_bash="${BASH:-bash}" sandbox="$(mktemp -d "${TMPDIR:-/tmp}/thoth-v3-gate-test.XXXXXX")" fixture="$sandbox/fixture repository" output="$sandbox/output" real_git="$(command -v git)" canonical_schema="$project_root/backend/dist/workspaces/schema.js" canonical_server="$project_root/backend/dist/server.js" canonical_schema_checksum="$(cksum <"$canonical_schema")" canonical_server_checksum="$(cksum <"$canonical_server")" cleanup() { rm -rf "$sandbox" } trap cleanup EXIT HUP INT TERM fail() { echo "FAIL: $*" >&2 [[ ! -f "$output" ]] || cat "$output" >&2 exit 1 } write_fixture_descriptor() { local path="$1" workspace_key="${2:-workspace:}" schema_key="${3:- schema_version: 3}" printf '%s\n' "$workspace_key" "$schema_key" >"$path" cat >>"$path" <<'YAML' id: fixture-workspace name: Fixture Workspace language: en dwh: engine: postgres database: warehouse schema: public supported_transports: [postgres_direct] semantic_index: vector_store: engine: qdrant collection: fixture-workspace dimensions: 1024 distance: cosine embedding: provider: ollama_internal model: qwen3-embedding:0.6b dimensions: 1024 llm_policy: allowed: [fixture/model] YAML } seed_fixture() { rm -rf "$fixture" mkdir -p \ "$fixture/backend/src/nested dir" \ "$fixture/backend/scripts" \ "$fixture/frontend/src/api" \ "$fixture/deploy/workspaces" \ "$fixture/scripts/fixtures" "$real_git" -C "$fixture" init -q "$real_git" -C "$fixture" config user.email fixture@example.invalid "$real_git" -C "$fixture" config user.name Fixture printf '%s\n' 'export const schemaVersion = 3;' >"$fixture/backend/src/server.ts" printf '%s\n' 'export const spaced = true;' >"$fixture/backend/src/nested dir/file name.ts" newline_path="$fixture/backend/src/line break.ts" printf '%s\n' 'export const newline = true;' >"$newline_path" printf '%s\n' 'export const currentWorkspace = true;' >"$fixture/frontend/src/api/workspaces.ts" printf '%s\n' 'export const productionCheck = true;' >"$fixture/backend/scripts/runtime-check.mjs" write_fixture_descriptor "$fixture/deploy/workspaces/example.yaml" write_fixture_descriptor "$fixture/deploy/workspaces/psd.yaml.example" printf '%s\n' '#!/usr/bin/env bash' 'echo operator-smoke' >"$fixture/scripts/workspace-registry-smoke.sh" write_fixture_descriptor "$fixture/scripts/fixtures/workspace-registry-smoke.yaml" write_fixture_descriptor "$fixture/scripts/fixtures/workspace-registry-windows.yaml" printf '%s\n' 'Write-Output "schema v3"' >"$fixture/scripts/test-windows-clone-contract.ps1" "$real_git" -C "$fixture" add . "$real_git" -C "$fixture" commit -qm seed } commit_fixture() { "$real_git" -C "$fixture" add . "$real_git" -C "$fixture" commit -qm "$1" } run_gate() { set +e "$gate_bash" "$gate" --root "$fixture" --runtime-only >"$output" 2>&1 gate_status=$? set -e } expect_pass() { local label="$1" run_gate [[ $gate_status -eq 0 ]] || fail "$label: expected pass, got status $gate_status" } expect_rejected() { local label="$1" expected="$2" run_gate [[ $gate_status -eq 1 ]] || fail "$label: expected rejection status 1, got $gate_status" grep -Fq -- "$expected" "$output" || fail "$label: rejection did not identify $expected" } # Clean tracked live paths, including spaces and an embedded newline, are NUL-safe. seed_fixture expect_pass "clean runtime fixture" seed_fixture mkfifo "$fixture/scripts/runtime-fifo" expect_rejected "runtime FIFO" "scripts/runtime-fifo" set +e "$gate_bash" "$gate" --help >"$output" 2>&1 help_status=$? set -e [[ $help_status -eq 0 ]] || fail "gate help failed with status $help_status" grep -Fq 'Node' "$output" || fail "gate help omits the runtime-only Node dependency" grep -Fq 'backend/dist/workspaces/schema.js' "$output" \ || fail "gate help omits the runtime-only compiled schema dependency" grep -Fq 'scripts/verify-schema-v3-only-release.sh' "$output" \ || fail "gate help omits the durable release entry point" grep -Fq 'npm ci' "$output" || fail "gate help omits lockfile install order" # Prescribed symbols and migration markers are case-insensitive substrings. seed_fixture printf '%s\n' 'export type WorkspaceV2Compat = unknown;' >"$fixture/backend/src/legacy.ts" commit_fixture backend-symbol expect_rejected "backend prescribed derivative symbol" "backend/src/legacy.ts" seed_fixture printf '%s\n' 'export type LegacyWorkspace = unknown;' >"$fixture/backend/src/legacy-workspace.ts" commit_fixture legacy-workspace-symbol expect_rejected "exact LegacyWorkspace symbol" "backend/src/legacy-workspace.ts" seed_fixture printf '%s\n' 'export const status = "MIGRATION_REQUIRED";' >"$fixture/backend/src/status.ts" commit_fixture backend-case-insensitive-marker expect_rejected "case-insensitive marker" "backend/src/status.ts" # Every forbidden category is applied to every recursive policy root without extension filters. policy_roots=(backend/src frontend/src backend/scripts scripts) policy_extensions=(ts py js yaml.example) policy_names=(WorkspaceV2 LegacyWorkspace migration_required 'revision.state') for category_index in 0 1 2 3; do for root_index in 0 1 2 3; do seed_fixture matrix_root="${policy_roots[$root_index]}" matrix_extension="${policy_extensions[$root_index]}" matrix_path="$matrix_root/matrix-$category_index.$matrix_extension" mkdir -p "${fixture:?}/$matrix_root" printf '%s\n' "${policy_names[$category_index]}" >"$fixture/$matrix_path" commit_fixture "policy-matrix-$category_index-$root_index" expect_rejected "policy category $category_index root $matrix_root" "$matrix_path" done done seed_fixture printf '%s\n' 'export const status = "migration_required";' >"$fixture/frontend/src/api/workspaces.ts" commit_fixture frontend-marker expect_rejected "frontend migration status" "frontend/src/api/workspaces.ts" seed_fixture printf '%s\n' 'export const blocked = record.revision.state !== "operational";' >"$fixture/frontend/src/api/workspaces.ts" commit_fixture frontend-revision-state expect_rejected "frontend revision state gate" "frontend/src/api/workspaces.ts" seed_fixture mkdir -p "$fixture/backend/scripts/nested/production" printf '%s\n' 'const helper = "migrate-v2-qdrant.js";' >"$fixture/backend/scripts/nested/production/runtime.mjs" commit_fixture nested-mjs expect_rejected "nested backend production script" "backend/scripts/nested/production/runtime.mjs" seed_fixture printf '%s\n' 'const historical = entry.revision.state;' >"$fixture/backend/scripts/runtime-check.mjs" commit_fixture backend-historical-state expect_rejected "backend historical revision state" "backend/scripts/runtime-check.mjs" seed_fixture printf '%s\n' 'node backend/dist/workspaces/MIGRATE-LEGACY.js' >"$fixture/scripts/workspace-registry-smoke.sh" commit_fixture operator-migrator expect_rejected "operator smoke migrator" "scripts/workspace-registry-smoke.sh" # Workspace YAML embedded in live non-test deployment scripts is validated structurally. seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/heredoc-smoke.sh" <<'EOF' #!/usr/bin/env bash cat <<'YAML' workspace: schema_version: 2 YAML EOF commit_fixture script-heredoc-v2 expect_rejected "deployment-script workspace schema" "scripts/operators/heredoc-smoke.sh" seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/quoted-heredoc-smoke.sh" <<'EOF' #!/usr/bin/env bash cat <<'YAML' "workspace" : 'schema_version' : 0x2 YAML EOF commit_fixture script-quoted-heredoc expect_rejected "quoted deployment-script workspace schema" "scripts/operators/quoted-heredoc-smoke.sh" seed_fixture mkdir -p "$fixture/scripts/operators" { printf '%s\n' '#!/usr/bin/env bash' "cat <<'---'" printf '%s \n' '---' printf '%s\n' 'workspace:' ' schema_version: 2' '---' } >"$fixture/scripts/operators/exact-close-smoke.sh" "$gate_bash" -n "$fixture/scripts/operators/exact-close-smoke.sh" commit_fixture script-exact-heredoc-close expect_rejected "heredoc false close with trailing blanks" "scripts/operators/exact-close-smoke.sh" seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/double-quoted-backslash-smoke.sh" <<'EOF' #!/usr/bin/env bash cat <<"\---" --- workspace: schema_version: 2 \--- EOF "$gate_bash" -n "$fixture/scripts/operators/double-quoted-backslash-smoke.sh" reviewer_output="$("$gate_bash" "$fixture/scripts/operators/double-quoted-backslash-smoke.sh")" printf '%s\n' "$reviewer_output" | grep -F 'schema_version: 2' >/dev/null || fail "double-quoted backslash reviewer fixture did not execute with the Bash delimiter" commit_fixture script-double-quoted-backslash expect_rejected "double-quoted non-special backslash delimiter" "scripts/operators/double-quoted-backslash-smoke.sh" seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/split-operator-smoke.sh" <<'EOF' #!/usr/bin/env bash cat <\ <'YAML' workspace: schema_version: 2 YAML EOF "$gate_bash" -n "$fixture/scripts/operators/split-operator-smoke.sh" reviewer_output="$("$gate_bash" "$fixture/scripts/operators/split-operator-smoke.sh")" printf '%s\n' "$reviewer_output" | grep -F 'schema_version: 2' >/dev/null || fail "split-operator reviewer fixture did not execute as a Bash heredoc" commit_fixture script-split-heredoc-operator expect_rejected "split heredoc operator continuation" "scripts/operators/split-operator-smoke.sh" seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/evidence-bundle-smoke.sh" <<'EOF' #!/usr/bin/env bash cat <<'YAML' evidence: source: bundle schema_version: 2 YAML EOF "$gate_bash" -n "$fixture/scripts/operators/evidence-bundle-smoke.sh" commit_fixture script-evidence-bundle expect_pass "evidence bundle without workspace mapping" seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/powershell-comment-smoke.ps1" <<'EOF' # harmless PowerShell comment \ $workspace = @' workspace: schema_version: 2 '@ EOF commit_fixture powershell-comment-v2 expect_rejected "PowerShell comment backslash before v2 here-string" "scripts/operators/powershell-comment-smoke.ps1" seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/powershell-valid-smoke.ps1" <<'EOF' $workspace = @' EOF cat "$fixture/deploy/workspaces/example.yaml" >>"$fixture/scripts/operators/powershell-valid-smoke.ps1" cat >>"$fixture/scripts/operators/powershell-valid-smoke.ps1" <<'EOF' '@ $bundle = @' evidence: source: bundle schema_version: 2 '@ EOF commit_fixture powershell-v3-and-bundle expect_rejected "PowerShell embedded v3 descriptor" "scripts/operators/powershell-valid-smoke.ps1" seed_fixture mkdir -p "$fixture/scripts/operators" cat >"$fixture/scripts/operators/powershell-bundle-smoke.ps1" <<'EOF' $bundle = @' evidence: source: bundle schema_version: 2 '@ EOF commit_fixture powershell-bundle expect_pass "PowerShell non-workspace bundle" # Quoted/space/indented YAML keys are real mappings; v3 passes and non-v3 fails. seed_fixture write_fixture_descriptor \ "$fixture/deploy/workspaces/example.yaml" \ '"workspace" :' \ " 'schema_version' : 3" commit_fixture quoted-yaml-v3 expect_pass "quoted and indented workspace v3" seed_fixture cat >"$fixture/deploy/workspaces/example.yaml" <<'EOF' 'workspace' : "schema_version" : 02 EOF commit_fixture quoted-yaml-noncanonical expect_rejected "quoted workspace noncanonical schema" "deploy/workspaces/example.yaml" seed_fixture printf '%s\n' 'workspace: { schema_version: 3 }' >"$fixture/deploy/workspaces/example.yaml" commit_fixture inline-workspace expect_rejected "inline workspace mapping" "deploy/workspaces/example.yaml" # Deleted migrator basenames are rejected case-insensitively at any live nesting depth. seed_fixture mkdir -p "$fixture/backend/src/deep/nested" printf '%s\n' 'export const otherwiseClean = true;' >"$fixture/backend/src/deep/nested/Migrate-Legacy.ts" commit_fixture deleted-case-path expect_rejected "case-insensitive deleted basename" "backend/src/deep/nested/Migrate-Legacy.ts" # Live filesystem/index trust is fail-closed, including ignored and newline-bearing files. seed_fixture printf '%s\n' 'export const changed = true;' >"$fixture/backend/src/server.ts" expect_rejected "modified tracked source" "backend/src/server.ts" seed_fixture printf '%s\n' 'export const staged = true;' >"$fixture/backend/src/server.ts" "$real_git" -C "$fixture" add backend/src/server.ts expect_rejected "staged tracked source" "backend/src/server.ts" seed_fixture printf '%s\n' 'export const untracked = true;' >"$fixture/backend/src/untracked.ts" expect_rejected "untracked production source" "backend/src/untracked.ts" seed_fixture printf '%s\n' 'backend/src/ignored.ts' >"$fixture/.gitignore" commit_fixture ignore-rule printf '%s\n' 'export const ignored = true;' >"$fixture/backend/src/ignored.ts" expect_rejected "ignored production source" "backend/src/ignored.ts" seed_fixture untracked_newline="$fixture/backend/src/untracked production.ts" printf '%s\n' 'export const untrackedNewline = true;' >"$untracked_newline" expect_rejected "newline-bearing untracked source" "backend/src/untracked\nproduction.ts" seed_fixture ln -s server.ts "$fixture/backend/src/tracked-link.ts" commit_fixture tracked-symlink expect_rejected "tracked live symlink" "backend/src/tracked-link.ts" # Generic non-workspace state/version formats remain allowed on live paths. seed_fixture mkdir -p "$fixture/backend/scripts/nested" "$fixture/scripts/operators" printf '%s\n' \ 'const first = entry.state;' \ 'const second = lease.state === "operational";' \ 'const third = job.state;' >"$fixture/backend/scripts/nested/generic-state.mjs" printf '%s\n' '#!/usr/bin/env bash' 'bundle_schema_version=1' >"$fixture/scripts/operators/bundle-smoke.sh" commit_fixture unrelated-state-version expect_pass "unrelated entry lease job state and bundle version" seed_fixture printf '%s\n' 'const stale = selectedWorkspace?.state;' >"$fixture/backend/scripts/runtime-check.mjs" commit_fixture workspace-state-gate expect_rejected "selected workspace revision state" "backend/scripts/runtime-check.mjs" seed_fixture printf '%s\n' 'const revision = { revision: { id: "x", state: "operational" } };' >"$fixture/backend/scripts/runtime-check.mjs" commit_fixture revision-object-state expect_rejected "bounded revision object state" "backend/scripts/runtime-check.mjs" # A top-level workspace descriptor has one exact schema_version: 3 key. for malformed in schema-v1 schema-v2 leading-zero hexadecimal multiline duplicate; do seed_fixture case "$malformed" in schema-v1) printf '%s\n' 'workspace:' ' schema_version: 1' >"$fixture/deploy/workspaces/example.yaml" ;; schema-v2) printf '%s\n' 'workspace:' ' schema_version: 2' >"$fixture/deploy/workspaces/example.yaml" ;; leading-zero) printf '%s\n' 'workspace:' ' schema_version: 02' >"$fixture/deploy/workspaces/example.yaml" ;; hexadecimal) printf '%s\n' 'workspace:' ' schema_version: 0x2' >"$fixture/deploy/workspaces/example.yaml" ;; multiline) printf '%s\n' 'workspace:' ' schema_version: >' ' 3' >"$fixture/deploy/workspaces/example.yaml" ;; duplicate) printf '%s\n' 'workspace:' ' schema_version: 3' ' schema_version: 3' >"$fixture/deploy/workspaces/example.yaml" ;; esac commit_fixture "yaml-$malformed" expect_rejected "malformed workspace schema $malformed" "deploy/workspaces/example.yaml" done # YAML that is not a top-level workspace descriptor is not a generic schema-version target. seed_fixture printf '%s\n' 'bundle_schema_version: 1' >"$fixture/deploy/workspaces/preprocess-dwh.yaml" commit_fixture unrelated-yaml-version expect_pass "unrelated YAML schema version" # Explicit deleted source paths and case-insensitive deleted basenames cannot hide as directories. seed_fixture mkdir -p "$fixture/backend/src/workspaces/migrate-legacy.ts" expect_rejected "deleted source directory" "backend/src/workspaces/migrate-legacy.ts" seed_fixture mkdir -p "$fixture/backend/src/deep/Migrate-V2-Qdrant.ts" expect_rejected "case-insensitive deleted directory" "backend/src/deep/Migrate-V2-Qdrant.ts" # Test and fixture naming never bypasses trust or content policy. seed_fixture mkdir -p "$fixture/frontend/src/test" ln -s ../api/workspaces.ts "$fixture/frontend/src/test/negative.test.ts" commit_fixture tracked-test-symlink expect_rejected "tracked test symlink" "frontend/src/test/negative.test.ts" seed_fixture mkdir -p "$fixture/frontend/src/test" printf '%s\n' 'export const clean = true;' >"$fixture/frontend/src/test/changed.test.ts" commit_fixture tracked-test-dirty printf '%s\n' 'export const changed = true;' >"$fixture/frontend/src/test/changed.test.ts" expect_rejected "dirty test file" "frontend/src/test/changed.test.ts" seed_fixture mkdir -p "$fixture/frontend/src/test" printf '%s\n' 'migration_required' >"$fixture/frontend/src/test/negative.test.ts" commit_fixture tracked-test-forbidden expect_rejected "forbidden marker in test path" "frontend/src/test/negative.test.ts" # Explicitly live test-named Windows and workspace fixtures are not excluded. seed_fixture printf '%s\n' 'Write-Output "MIGRATE-LEGACY"' >"$fixture/scripts/test-windows-clone-contract.ps1" commit_fixture live-windows-exception expect_rejected "live Windows fixture exception" "scripts/test-windows-clone-contract.ps1" seed_fixture printf '%s\n' 'workspace:' ' schema_version: 2' >"$fixture/scripts/fixtures/workspace-registry-smoke.yaml" commit_fixture live-workspace-fixture expect_rejected "live workspace fixture exception" "scripts/fixtures/workspace-registry-smoke.yaml" seed_fixture write_fixture_descriptor \ "$fixture/scripts/fixtures/workspace-registry-future.yaml" \ 'workspace:' \ ' schema_version: 2' commit_fixture future-workspace-family expect_rejected "future workspace fixture family" "scripts/fixtures/workspace-registry-future.yaml" # Only exact path+category policy literals are allowed; paths outside policy roots remain out of scope. seed_fixture mkdir -p "$fixture/docs/superpowers/plans" printf '%s\n' 'Historical schema_version: 2 and migration_required.' >"$fixture/docs/superpowers/plans/history.md" printf '%s\n' 'migration_required migrate-legacy WorkspaceV2 revision.state' >"$fixture/scripts/test-verify-schema-v3-only.sh" commit_fixture exact-policy-allowlist expect_pass "exact self-test policy allowlist and historical docs" # Diagnostic paths are shell-escaped so a newline cannot forge another log line. seed_fixture newline_spoof="$fixture/backend/src/spoof forged.py" printf '%s\n' harmless >"$newline_spoof" run_gate [[ $gate_status -eq 1 ]] || fail "newline untracked path was not rejected" grep -Fq 'backend/src/spoof\nforged.py' "$output" \ || fail "newline path diagnostic was not escaped on one line" # Scanner operational errors are propagated, not converted into absence. seed_fixture fake_bin="$sandbox/fake-bin" mkdir -p "$fake_bin" cat >"$fake_bin/git" <<'EOF' #!/usr/bin/env bash set -euo pipefail for argument in "$@"; do if [[ "$argument" == grep ]]; then echo "simulated git grep failure" >&2 exit 2 fi done exec "$REAL_GIT" "$@" EOF chmod +x "$fake_bin/git" set +e PATH="$fake_bin:$PATH" REAL_GIT="$real_git" "$gate_bash" "$gate" --root "$fixture" --runtime-only >"$output" 2>&1 gate_status=$? set -e [[ $gate_status -eq 2 ]] || fail "git grep status 2 was masked as $gate_status" grep -Fq 'simulated git grep failure' "$output" || fail "git grep failure diagnostics were lost" # Foreign roots are test-only and can never select fixture code for a full check. set +e "$gate_bash" "$gate" --root "$fixture" >"$output" 2>&1 gate_status=$? set -e [[ $gate_status -ne 0 ]] || fail "foreign-root full mode unexpectedly passed" grep -Fq -- '--root is available only with --runtime-only or --bootstrap-trust-only' "$output" \ || fail "foreign-root full rejection did not report the trust boundary" # Prescribed symbols are forbidden as case-insensitive substrings, including derivatives. derivative_names=(WorkspaceV2Compat wOrKsPaCeV2 deprecatedV2Descriptor WRITEMIGRATEDWORKSPACE LegacyWorkspaceAdapter migrateLegacyWorkspaceCompat) for derivative in "${derivative_names[@]}"; do for matrix_root in "${policy_roots[@]}"; do seed_fixture matrix_path="$matrix_root/derivative.ts" printf '%s\n' "$derivative" >"$fixture/$matrix_path" commit_fixture "derivative-$derivative-${matrix_root//\//-}" expect_rejected "derivative $derivative in $matrix_root" "$matrix_path" done done # Mixed/all-lower legacy spellings fail; the approved lower-camel identifier remains valid. for spelling in legacyworkspace LeGaCyWoRkSpAcE; do seed_fixture printf '%s\n' "$spelling" >"$fixture/backend/src/legacy-variant.ts" commit_fixture legacy-spelling expect_rejected "legacy spelling $spelling" "backend/src/legacy-variant.ts" done seed_fixture printf '%s\n' 'const legacyWorkspacePath = current;' >"$fixture/backend/src/legacy-allowed.ts" commit_fixture lower-camel-legacy expect_pass "approved lower-camel legacy identifier" # Full-text revision scanning covers bracket access and newline-separated dot access. for revision_source in \ 'selectedWorkspace["state"]' \ $'workspaceRevision\n .state'; do seed_fixture printf '%s\n' "$revision_source" >"$fixture/frontend/src/revision-variant.ts" commit_fixture revision-variant expect_rejected "revision structural variant" "frontend/src/revision-variant.ts" done seed_fixture mkdir -p "$fixture/backend/src/workspaces" printf '%s\n' 'if (revision.state !== "operational") return;' >"$fixture/backend/src/workspaces/registry.ts" commit_fixture historical-decoder expect_pass "single exact historical decoder" printf '%s\n' 'if (revision["state"] === "retired") return;' >>"$fixture/backend/src/workspaces/registry.ts" commit_fixture extra-historical-branch expect_rejected "extra registry revision branch" "backend/src/workspaces/registry.ts" # Binary/NUL policy files are decoded and rejected rather than skipped by git grep -I. seed_fixture printf 'WorkspaceV2\0hidden\n' >"$fixture/backend/src/binary.ts" commit_fixture nul-policy expect_rejected "NUL policy file" "backend/src/binary.ts" # Workspace fixture-family discovery is recursive by basename. seed_fixture mkdir -p "$fixture/scripts/fixtures/nested/deeper" write_fixture_descriptor "$fixture/scripts/fixtures/nested/deeper/workspace-registry-nested.yaml" workspace: ' schema_version: 2' commit_fixture nested-workspace-fixture expect_rejected "nested workspace fixture" "scripts/fixtures/nested/deeper/workspace-registry-nested.yaml" # Python bytecode is disabled before pre-gate docs, and release dry-run starts with bootstrap trust. grep -Fq 'PYTHONDONTWRITEBYTECODE: "1"' "$project_root/.github/workflows/deployment.yml" \ || fail "workflow does not disable Python bytecode" grep -Fq 'export PYTHONDONTWRITEBYTECODE=1' "$project_root/scripts/verify-schema-v3-only-release.sh" \ || fail "release wrapper does not disable Python bytecode" release_plan="$($gate_bash "$project_root/scripts/verify-schema-v3-only-release.sh" --dry-run)" first_command="$(printf '%s\n' "$release_plan" | sed -n '1p')" bootstrap_command="$(printf '%s\n' "$release_plan" | sed -n '4p')" [[ "$first_command" == 'export PYTHONDONTWRITEBYTECODE=1' ]] \ || fail "release dry-run does not print the Python bytecode export" [[ "$bootstrap_command" == '/bin/bash scripts/verify-schema-v3-only.sh --bootstrap-trust-only' ]] \ || fail "release plan does not bootstrap trust before npm" printf '%s\n' "$release_plan" | grep -Fq '(cd backend && npm ci --ignore-scripts)' \ || fail "release plan does not disable npm lifecycle scripts" py_fixture="$sandbox/python-bytecode" mkdir -p "$py_fixture/scripts" printf '%s\n' 'value = 3' >"$py_fixture/scripts/module.py" PYTHONPATH="$py_fixture" PYTHONDONTWRITEBYTECODE=1 python3 -c 'import scripts.module' [[ ! -e "$py_fixture/scripts/__pycache__" ]] || fail "pre-gate Python created ignored bytecode" seed_bootstrap_fixture() { seed_fixture mkdir -p "$fixture/.github/workflows" for required in \ backend/package.json backend/package-lock.json \ backend/scripts/verify-workspace-descriptor-files.mjs \ backend/scripts/verify-workspace-descriptor-files.test.mjs \ backend/scripts/revision-state-policy.mjs \ backend/scripts/revision-state-policy.test.mjs \ backend/scripts/bash-heredoc.mjs \ backend/scripts/revision_state_policy.py \ backend/scripts/test_revision_state_policy.py \ scripts/verify-schema-v3-only.sh scripts/test-verify-schema-v3-only.sh \ scripts/verify-schema-v3-only-release.sh scripts/workspace_descriptor_doc_contract.py \ .github/workflows/deployment.yml; do mkdir -p "$fixture/${required%/*}" cp "$project_root/$required" "$fixture/$required" done "$real_git" -C "$fixture" add . "$real_git" -C "$fixture" commit -qm bootstrap-files } assert_release_stops_before_npm() { local label="$1" fake_lifecycle="$sandbox/fake-lifecycle" mkdir -p "$fake_lifecycle" cat >"$fake_lifecycle/npm" <>"$sandbox/npm-invoked" exit 99 EOF chmod +x "$fake_lifecycle/npm" rm -f "$sandbox/npm-invoked" set +e PATH="$fake_lifecycle:$PATH" /bin/bash "$fixture/scripts/verify-schema-v3-only-release.sh" >"$output" 2>&1 release_status=$? set -e [[ $release_status -ne 0 ]] || fail "$label unexpectedly passed" [[ ! -e "$sandbox/npm-invoked" ]] || fail "$label invoked npm before bootstrap trust" } seed_bootstrap_fixture printf '%s\n' '# dirty' >>"$fixture/backend/package.json" assert_release_stops_before_npm "dirty package bootstrap" seed_bootstrap_fixture printf '%s\n' '# dirty' >>"$fixture/backend/scripts/verify-workspace-descriptor-files.test.mjs" assert_release_stops_before_npm "dirty checker test bootstrap" seed_bootstrap_fixture printf '%s\n' '// dirty' >>"$fixture/backend/scripts/revision-state-policy.mjs" assert_release_stops_before_npm "dirty revision policy bootstrap" seed_bootstrap_fixture printf '%s\n' '# dirty' >>"$fixture/backend/scripts/revision_state_policy.py" assert_release_stops_before_npm "dirty Python policy helper bootstrap" seed_bootstrap_fixture printf '%s\n' '# dirty' >>"$fixture/scripts/verify-schema-v3-only.sh" assert_release_stops_before_npm "dirty gate bootstrap" seed_bootstrap_fixture rm "$fixture/backend/scripts/verify-workspace-descriptor-files.mjs" ln -s /dev/null "$fixture/backend/scripts/verify-workspace-descriptor-files.mjs" assert_release_stops_before_npm "symlink checker bootstrap" seed_bootstrap_fixture printf '%s\n' 'scripts/__pycache__/' >"$fixture/.gitignore" "$real_git" -C "$fixture" add .gitignore "$real_git" -C "$fixture" commit -qm ignore-rule mkdir -p "$fixture/scripts/__pycache__" printf x >"$fixture/scripts/__pycache__/ignored.pyc" assert_release_stops_before_npm "ignored trusted artifact bootstrap" seed_bootstrap_fixture printf x >"$fixture/scripts/untracked-helper.sh" assert_release_stops_before_npm "untracked helper bootstrap" seed_bootstrap_fixture mkfifo "$fixture/scripts/bootstrap-fifo" assert_release_stops_before_npm "FIFO bootstrap" seed_bootstrap_fixture printf '%s\n' unsafe >"$fixture/backend/.npmrc" assert_release_stops_before_npm "untracked backend npmrc bootstrap" seed_bootstrap_fixture global_ignore="$sandbox/global-ignore" printf '%s\n' backend/.npmrc >"$global_ignore" "$real_git" -C "$fixture" config core.excludesFile "$global_ignore" printf '%s\n' unsafe >"$fixture/backend/.npmrc" assert_release_stops_before_npm "globally ignored backend npmrc bootstrap" # Dist failures are isolated to fixture roots; canonical backend/dist is never mutated. run_gate_dist() { set +e "$gate_bash" "$gate" --root "$fixture" --runtime-only --check-dist >"$output" 2>&1 gate_status=$? set -e } seed_fixture mkdir -p "$fixture/backend/dist" printf '%s\n' server >"$fixture/backend/dist/server.js" run_gate_dist [[ $gate_status -eq 1 ]] || fail "fixture missing schema module unexpectedly passed" grep -Fq 'backend/dist/workspaces/schema.js' "$output" || fail "fixture missing schema path not reported" seed_fixture mkdir -p "$fixture/backend/dist/workspaces" printf '%s\n' schema >"$fixture/backend/dist/workspaces/schema.js" run_gate_dist [[ $gate_status -eq 1 ]] || fail "fixture missing server unexpectedly passed" grep -Fq 'backend/dist/server.js' "$output" || fail "fixture missing server path not reported" seed_fixture mkdir -p "$fixture/backend/dist/workspaces" printf '%s\n' schema >"$fixture/backend/dist/workspaces/schema.js" printf '%s\n' server >"$fixture/backend/dist/server.js" printf '%s\n' stale >"$fixture/backend/dist/workspaces/Migrate-Legacy.js" run_gate_dist [[ $gate_status -eq 1 ]] || fail "fixture stale migrator unexpectedly passed" grep -Fq 'backend/dist/workspaces/Migrate-Legacy.js' "$output" || fail "fixture stale migrator path not reported" [[ "$(cksum <"$canonical_schema")" == "$canonical_schema_checksum" ]] \ || fail "shell regression mutated canonical compiled schema" [[ "$(cksum <"$canonical_server")" == "$canonical_server_checksum" ]] \ || fail "shell regression mutated canonical compiled server" echo "schema-v3-only absence gate regression tests passed"