Embedding $response as Python triple-quoted string caused json.loads()
to fail with 'Invalid control character' when Vault's JSON contained
\n sequences in the PEM public key (bash \n → Python newline → invalid
JSON control char).
Fix: write response to mktemp, pass path as argv, read with open().
Instead of skipping the frontend test when vitest is not installed,
security-gate.sh now runs `npm ci -w frontend` automatically.
Also add `cache: "npm"` to security-gate's actions/setup-node so the
npm cache from the frontend-tests job is reused — prevents OOM on
the 1GB VPS (cache restore is disk-only, not 300MB download).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
security-gate.sh checked only `command -v node` before running
`npm test -w frontend`. In the CI security-gate job, node is in PATH
(from actions/setup-node) but root node_modules are NOT installed
(npm ci was removed to prevent OOM). This caused the frontend test to
fail with "Cannot find module vitest".
Fix: also require node_modules/.bin/vitest to exist. Without it the
step SKIPs gracefully — frontend tests are already covered by the
dedicated frontend-tests CI job which runs first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
T1 (H7): casan-step.mjs now calls rollback-manager.sh checkpoint before
overwriting plan.md at attempt-2, writes tx-id to plan.checkpoint.txid
sidecar, and executes rollback on REJECTED verdict. rollback-transactions.jsonl
records a real cp restore command. Adversarial test: checkpoint exists,
real cp command recorded, plan hash matches pre-overwrite content.
T4 (H6): casan-harness.sh exports CASAN_STEP_NAME=$ACTION_NAME before
agent-metrics.sh so nested model calls (model-call.py) and the provider-
cost-lookup.py query share the same step label. metrics.jsonl now writes
cost_source=provider_telemetry instead of word_count_estimate when a real
Ollama call is made within the same step. Adversarial test: verified with
CASAN_STEP_NAME=t4-telemetry-test end-to-end.
adversarial-harness-tests.sh: 40 → 44 PASS / 0 FAIL (+3 T1, +1 T4)
security-gate.sh: PASS=10 FAIL=0 SKIP=0 (verified, local ornith:9b)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>