Files
ThothII/scripts/test-verify-schema-v3-only.sh

716 lines
29 KiB
Bash
Executable File

#!/usr/bin/env bash
# Regression tests for the fail-closed schema-v4-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: 4}"
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]
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 = 4;' >"$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 v4"' >"$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; v4 passes and non-v4 fails.
seed_fixture
write_fixture_descriptor \
"$fixture/deploy/workspaces/example.yaml" \
'"workspace" :' \
" 'schema_version' : 4"
commit_fixture quoted-yaml-v4
expect_pass "quoted and indented workspace v4"
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: 4 }' >"$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: 4 key.
for malformed in schema-v1 schema-v2 schema-v3 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"
;;
schema-v3)
printf '%s\n' 'workspace:' ' schema_version: 3' >"$fixture/deploy/workspaces/example.yaml"
;;
leading-zero)
printf '%s\n' 'workspace:' ' schema_version: 04' >"$fixture/deploy/workspaces/example.yaml"
;;
hexadecimal)
printf '%s\n' 'workspace:' ' schema_version: 0x4' >"$fixture/deploy/workspaces/example.yaml"
;;
multiline)
printf '%s\n' 'workspace:' ' schema_version: >' ' 4' >"$fixture/deploy/workspaces/example.yaml"
;;
duplicate)
printf '%s\n' 'workspace:' ' schema_version: 4' ' schema_version: 4' >"$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/maintenance-job.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.
seed_fixture
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"
# 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 'python3 -m venv .venv' "$project_root/.github/workflows/deployment.yml" \
|| fail "workflow does not install the real harness CLI before backend integration tests"
grep -Fq '.venv/bin/python -m pip install -e .' "$project_root/.github/workflows/deployment.yml" \
|| fail "workflow does not install the harness package into its canonical virtual environment"
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')"
user_config_command="$(printf '%s\n' "$release_plan" | sed -n '2p')"
global_config_command="$(printf '%s\n' "$release_plan" | sed -n '3p')"
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"
[[ "$user_config_command" == 'export NPM_CONFIG_USERCONFIG=<private-empty-user-config>' ]] \
|| fail "release dry-run does not isolate npm user configuration"
[[ "$global_config_command" == 'export NPM_CONFIG_GLOBALCONFIG=<private-empty-global-config>' ]] \
|| fail "release dry-run does not isolate npm global configuration"
[[ "$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" <<EOF
#!/usr/bin/env bash
echo invoked >>"$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-v4-only absence gate regression tests passed"