Files
CASAN/AINative_OKR_CASAN5/.specify/scripts/bash/casan-harness.sh
T
thanhnvandClaude Sonnet 4.6 a8edbea534 fix T1+T4: wire rollback into pipeline; real Ollama telemetry in metrics
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>
2026-07-01 11:25:50 +09:00

99 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# CASAN Level 5 unified harness wrapper.
# Usage:
# casan-harness.sh <input-file> <output-file> [action-name] [-- <command> ...]
#
# Flow:
# H4 input security -> H5 governance -> H6 metrics around execution/cache -> H4 output filter
INPUT_FILE="${1:-}"
FINAL_OUTPUT="${2:-}"
ACTION_NAME="${3:-agent_step}"
shift 3 || true
if [[ "${1:-}" == "--" ]]; then
shift
fi
if [[ -z "$INPUT_FILE" || -z "$FINAL_OUTPUT" ]]; then
echo "Usage: casan-harness.sh <input-file> <output-file> [action-name] [-- <command> ...]" >&2
exit 64
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
TMP_DIR="$PROJECT_ROOT/.specify/logs/tmp"
CACHE_DIR="$PROJECT_ROOT/.specify/logs/idempotency"
mkdir -p "$TMP_DIR" "$CACHE_DIR" "$(dirname "$FINAL_OUTPUT")"
hash_text() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum | awk '{print $1}'
else
shasum -a 256 | awk '{print $1}'
fi
}
CMD_STR="${*:-no_cmd}"
INPUT_HASH="$(cat "$INPUT_FILE" | hash_text)"
CMD_HASH="$(printf '%s' "$CMD_STR" | hash_text)"
IDEMPOTENCY_KEY="$(printf '%s|%s|%s' "$INPUT_HASH" "$CMD_HASH" "$ACTION_NAME" | hash_text)"
CACHE_META="$CACHE_DIR/$IDEMPOTENCY_KEY.json"
CACHE_OUT="$CACHE_DIR/$IDEMPOTENCY_KEY.output"
TRACE_SUFFIX="$(date +%s)-$$"
SAFE_INPUT="$TMP_DIR/security-input-$TRACE_SUFFIX.txt"
APPROVED_INPUT="$TMP_DIR/governance-approved-$TRACE_SUFFIX.txt"
RAW_OUTPUT="$TMP_DIR/raw-output-$TRACE_SUFFIX.txt"
"$SCRIPT_DIR/security-check.sh" "$INPUT_FILE" "$SAFE_INPUT" input
"$SCRIPT_DIR/governance-check.sh" "$SAFE_INPUT" "$APPROVED_INPUT" "$ACTION_NAME"
# H2 tool registry gate is in the line of fire for side-effecting actions:
# it enforces idempotency key, per-agent permission, and rollback strategy
# before the command is allowed to execute. The wrapper already derived a
# content-addressed idempotency key above.
case "$ACTION_NAME" in
write_code|migration|deploy|db_write|external_api|write_file)
CASAN_IDEMPOTENCY_KEY="$IDEMPOTENCY_KEY" "$SCRIPT_DIR/tool-registry-gate.sh" "$ACTION_NAME"
;;
esac
# T4: propagate step name so any nested model calls (model-call.py) log against the same step
# name, enabling provider-cost-lookup.py to match real Ollama token counts in agent-metrics.sh.
export CASAN_STEP_NAME="${CASAN_STEP_NAME:-$ACTION_NAME}"
# WP-S5: wrap command execution under a hard wall-clock timeout (tool-exec.sh).
# Prevents runaway or hung tool calls from blocking the pipeline indefinitely.
TOOL_TIMEOUT="${CASAN_TOOL_TIMEOUT_SECONDS:-30}"
if [[ -f "$CACHE_META" && -f "$CACHE_OUT" ]]; then
"$SCRIPT_DIR/agent-metrics.sh" "$APPROVED_INPUT" "$RAW_OUTPUT" -- bash -c 'cp "$1" "$CASAN_OUTPUT"' _ "$CACHE_OUT"
CACHE_STATUS="cached"
elif [[ "$#" -gt 0 ]]; then
"$SCRIPT_DIR/agent-metrics.sh" "$APPROVED_INPUT" "$RAW_OUTPUT" -- \
"$SCRIPT_DIR/tool-exec.sh" "$TOOL_TIMEOUT" -- "$@"
CACHE_STATUS="stored"
else
"$SCRIPT_DIR/agent-metrics.sh" "$APPROVED_INPUT" "$RAW_OUTPUT"
CACHE_STATUS="stored"
fi
"$SCRIPT_DIR/security-check.sh" "$RAW_OUTPUT" "$FINAL_OUTPUT" output
if [[ "$CACHE_STATUS" == "stored" ]]; then
cat <<EOF > "$CACHE_META"
{
"idempotency_key": "$IDEMPOTENCY_KEY",
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"action": "$ACTION_NAME",
"command": "$(printf '%s' "$CMD_STR" | sed 's/"/\\"/g')",
"output_hash": "$(cat "$FINAL_OUTPUT" | hash_text)"
}
EOF
cp "$FINAL_OUTPUT" "$CACHE_OUT"
fi
echo "CASAN_HARNESS_COMPLETE cache=$CACHE_STATUS key=$IDEMPOTENCY_KEY output=$FINAL_OUTPUT"