#!/usr/bin/env bash # scripts/verify_phase10.sh - Phase 10 v1-spike-l2-and-contract-e2e gate (capstone). set -u ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" fail() { echo "FAIL: $*" >&2; exit 1; } ok() { echo "ok: $*"; } ENV_FILE="$ROOT/.env.secrets" [ -f "$ENV_FILE" ] || fail ".env.secrets missing (run scripts/rotate_spike_key.sh first)" git check-ignore -q "$ENV_FILE" || fail ".env.secrets is not gitignored" set -a . "$ENV_FILE" set +a export AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID" export AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY" export AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION" # --- Check (a): composition.json exists + shape --- python3 <<'PY' || fail "composition.json shape wrong" import json c = json.load(open('modules-ir/l2/l2-static-asset/composition.json')) assert c['kind'] == 'l2' and c['depth'] == 1 assert len(c['children']) == 1 and c['children'][0]['module'] == 'l1-s3@1.0.0' assert c['wires']['bucket_name']['target'] == 's3' assert c['wires']['region']['target'] == 's3' print('composition.json: kind=l2 depth=1 one child l1-s3@1.0.0 wires passthrough') PY ok "composition.json: l2-static-asset references l1-s3 only (depth 1)" # --- Check (b): spike.yaml validates against contract schema --- python3 <<'PY' || fail "spike.yaml does not validate against contract schema" import yaml, json, jsonschema contract = yaml.safe_load(open('contracts/spike.yaml')) schema = json.load(open('schemas/contract.schema.json')) jsonschema.validate(contract, schema) print('spike.yaml validates against contract.schema.json') PY ok "contracts/spike.yaml validates against the contract schema" # --- Check (c): resolver py_compiles + emits IR validating against ir.schema.json --- python3 -m py_compile acdl_platform/contract_resolver.py || fail "contract_resolver.py py_compile failed" TMP=$(mktemp -d) python3 acdl_platform/contract_resolver.py contracts/spike.yaml "$TMP/spike_ir.json" 2>/dev/null ( cd /tmp && python3 -c " import json, jsonschema inst = json.load(open('$TMP/spike_ir.json')) schema = json.load(open('$ROOT/schemas/ir.schema.json')) jsonschema.validate(inst, schema) print('IR validates against ir.schema.json') " ) || fail "resolver IR does not validate against ir.schema.json" ok "contract_resolver.py resolves spike.yaml to an IR-schema-valid instance" # --- Check (d): adapter py_compiles + emits main.tf with aws_s3_bucket --- python3 -m py_compile adapters/terraform/adapter.py || fail "adapter.py py_compile failed" python3 adapters/terraform/adapter.py "$TMP/spike_ir.json" "$TMP/tf" 2>/dev/null grep -q 'resource "aws_s3_bucket"' "$TMP/tf/main.tf" || fail "adapter did not emit aws_s3_bucket" ok "adapter.py compiles L2 IR to terraform with aws_s3_bucket" rm -rf "$TMP" # --- Check (e): run_spike_e2e.sh exits 0 --- bash scripts/run_spike_e2e.sh > /tmp/verify_phase10_e2e.log 2>&1 || { cat /tmp/verify_phase10_e2e.log >&2 fail "run_spike_e2e.sh failed" } grep -q "SPIKE E2E OK" /tmp/verify_phase10_e2e.log || fail "run_spike_e2e.sh did not print SPIKE E2E OK" ok "run_spike_e2e.sh completes the full pipeline end-to-end" # --- Check (f): confidence band is pass for dev --- grep -q "band=pass" /tmp/verify_phase10_e2e.log || fail "confidence band is not pass for dev" ok "confidence band is pass for dev" # --- Check (g): outbox item exists --- python3 <<'PY' || fail "outbox item not found in DynamoDB" import boto3 s = boto3.Session(region_name='us-east-1') dyn = s.client('dynamodb') r = dyn.query(TableName='acdl-outbox', KeyConditionExpression='contractId = :cid', ExpressionAttributeValues={':cid': {'S': '11111111-1111-1111-1111-111111111111'}}) assert r.get('Count', 0) >= 1, f'no outbox item for the spike contractId (Count={r.get("Count", 0)})' print(f'outbox item present (Count={r["Count"]})') PY ok "evidence event is written to the DynamoDB outbox" # --- Check (h): REQ-28 - the adapter is the only substrate-specific code --- # The IR commitments hold: the adapter is the only place that knows Terraform # resource types (aws_s3_bucket). The L1/L2 interfaces, the IR schema, the # contract, the resolver, the confidence signal, and the outbox writer are # substrate-agnostic. Documentation (.md) + schema $comment/description strings # may mention aws_s3_bucket *to explain the mapping* — that's not a violation; # the check scans actual executable code (.py) + data files (.json/.yaml) # for resource-type declarations, excluding .md files + description/comment # string values. LEAK=$(grep -rn --include='*.py' -E 'aws_s3_bucket|aws_[a-z]+_[a-z]+' \ acdl_platform/ 2>/dev/null) if [ -n "$LEAK" ]; then echo "$LEAK" >&2 fail "REQ-28 violated: substrate-specific terms found in acdl_platform/ Python code (the platform must be substrate-agnostic)" fi # modules-ir/ data files: exclude .md (docs may reference the mapping); check # only .json for actual resource-type field declarations (not description strings). LEAK2=$(python3 <<'PY' 2>&1 || true import json, os, sys leaks = [] for root, dirs, files in os.walk('modules-ir'): for f in files: if not f.endswith('.json'): continue path = os.path.join(root, f) with open(path) as fh: try: data = json.load(fh) except Exception: continue # Walk the JSON; flag 'aws_s3_bucket' (Terraform type) appearing as a # VALUE (not a key), excluding description/comment strings. def walk(obj, path_str=''): if isinstance(obj, dict): for k, v in obj.items(): if k in ('description', '$comment') and isinstance(v, str): continue # docs/comment strings are allowed to mention it walk(v, path_str + '/' + k) elif isinstance(obj, str): if obj.startswith('aws_') and obj != 'aws:s3:bucket': leaks.append(f'{path}: {path_str} = {obj!r}') walk(data) if leaks: print('\n'.join(leaks)) PY ) if [ -n "$LEAK2" ]; then echo "$LEAK2" >&2 fail "REQ-28 violated: substrate-specific resource-type values found in modules-ir/ JSON" fi ADAPT_HAS=$(grep -rn --include='*.py' -E 'aws_s3_bucket' adapters/terraform/ 2>/dev/null) [ -n "$ADAPT_HAS" ] || fail "REQ-28: adapter does not contain aws_s3_bucket (it should — it's the substrate-specific code)" ok "REQ-28: adapter is the only substrate-specific code; modules-ir/ + acdl_platform/ are substrate-agnostic (docs/comments excluded)" echo "VERIFIED — Phase 10: L2 + contract-e2e; IR commitments hold (REQ-28)"