Skip to content

fix(strategy): formalize canary and verified provenance #2

fix(strategy): formalize canary and verified provenance

fix(strategy): formalize canary and verified provenance #2

name: Reusable Drift Check

Check failure on line 1 in .github/workflows/reusable-drift-check.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/reusable-drift-check.yml

Invalid workflow file

(Line: 67, Col: 40): Unrecognized named-value: 'runner'. Located at position 1 within expression: runner.temp
on:
workflow_call:
inputs:
strategy_domain:
required: true
type: string
snapshot_repository:
required: true
type: string
snapshot_checkout_path:
required: true
type: string
snapshot_repository_ref:
required: false
type: string
default: "main"
python_version:
required: false
type: string
default: "3.11"
quant_platform_kit_ref:
required: false
type: string
default: "main"
ai_gateway_service_url:
required: false
type: string
default: ""
lifecycle_performance_bucket:
required: false
type: string
default: ""
caller_event_name:
required: true
type: string
caller_pr_head_repository:
required: false
type: string
default: ""
lifecycle_preflight_artifact:
required: false
type: string
default: ""
secrets:
codex_audit_service_url:
required: false
snapshot_repository_token:
required: false
permissions:
contents: read
issues: write
id-token: write
jobs:
drift:
runs-on: ubuntu-latest
timeout-minutes: 15
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
STRATEGY_DOMAIN: ${{ inputs.strategy_domain }}
QUANT_PROJECTS_ROOT: ${{ github.workspace }}/external
LIFECYCLE_PERFORMANCE_BUCKET: ${{ inputs.lifecycle_performance_bucket || vars.LIFECYCLE_PERFORMANCE_BUCKET || '' }}
LIFECYCLE_LOCAL_ROOT: ${{ github.workspace }}/data/lifecycle_store
CRYPTO_LIFECYCLE_PREFLIGHT_ROOT: ${{ runner.temp }}/lifecycle-preflight
steps:
- name: Validate trusted caller
env:
CALLER_REPOSITORY: ${{ github.repository }}
CALLER_EVENT_NAME: ${{ inputs.caller_event_name }}
CALLER_PR_HEAD_REPOSITORY: ${{ inputs.caller_pr_head_repository }}
SNAPSHOT_REPOSITORY: ${{ inputs.snapshot_repository }}
shell: bash
run: |
set -euo pipefail
caller_repo="$CALLER_REPOSITORY"
caller_event="$CALLER_EVENT_NAME"
caller_pr_head_repo="$CALLER_PR_HEAD_REPOSITORY"
snapshot_repo="$SNAPSHOT_REPOSITORY"
expected_snapshot_repo=""
case "$caller_repo" in
QuantStrategyLab/CnEquityStrategies)
expected_snapshot_repo="QuantStrategyLab/CnEquitySnapshotPipelines"
;;
QuantStrategyLab/HkEquityStrategies)
expected_snapshot_repo="QuantStrategyLab/HkEquitySnapshotPipelines"
;;
QuantStrategyLab/UsEquityStrategies)
expected_snapshot_repo="QuantStrategyLab/UsEquitySnapshotPipelines"
;;
QuantStrategyLab/CryptoStrategies)
expected_snapshot_repo="QuantStrategyLab/CryptoLivePoolPipelines"
;;
*)
echo "::error::Untrusted reusable workflow caller: $caller_repo"
exit 1
;;
esac
case "$caller_event" in
schedule|workflow_dispatch)
;;
pull_request|pull_request_target)
if [ -n "$caller_pr_head_repo" ] && [ "$caller_pr_head_repo" != "$caller_repo" ]; then
echo "::error::Fork pull_request callers are not trusted: $caller_pr_head_repo"
else
echo "::error::pull_request callers are not trusted: $caller_repo"
fi
exit 1
;;
"")
echo "::error::caller_event_name must be provided by the caller workflow"
exit 1
;;
*)
echo "::error::Unsupported caller_event_name: $caller_event"
exit 1
;;
esac
if [ "$snapshot_repo" != "$expected_snapshot_repo" ]; then
echo "::error::snapshot_repository mismatch for caller $caller_repo: $snapshot_repo"
exit 1
fi
- name: Checkout
uses: actions/checkout@v6
- name: Checkout snapshot pipeline repo
uses: actions/checkout@v6
with:
repository: ${{ inputs.snapshot_repository }}
ref: ${{ inputs.snapshot_repository_ref }}
token: ${{ secrets.snapshot_repository_token || github.token }}
path: ${{ inputs.snapshot_checkout_path }}
- name: Checkout QuantPlatformKit
uses: actions/checkout@v6
with:
repository: QuantStrategyLab/QuantPlatformKit
ref: ${{ inputs.quant_platform_kit_ref }}
path: external/QuantPlatformKit
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python_version }}
- name: Install dependencies
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install -e . pandas
python -m pip install --no-deps -e external/QuantPlatformKit
- name: Download lifecycle preflight artifact
if: inputs.lifecycle_preflight_artifact != ''
uses: actions/download-artifact@v4
with:
name: ${{ inputs.lifecycle_preflight_artifact }}
path: ${{ runner.temp }}/lifecycle-preflight
- name: Restore lifecycle preflight inputs
if: inputs.lifecycle_preflight_artifact != ''
env:
PREFLIGHT_BUNDLE_ROOT: ${{ runner.temp }}/lifecycle-preflight
SNAPSHOT_CHECKOUT_PATH: ${{ inputs.snapshot_checkout_path }}
shell: bash
run: |
set -euo pipefail
case "$SNAPSHOT_CHECKOUT_PATH" in
external/*) ;;
*) echo "::error::snapshot_checkout_path must remain under external/"; exit 1 ;;
esac
if [[ "$SNAPSHOT_CHECKOUT_PATH" == *".."* ]]; then
echo "::error::snapshot_checkout_path must not contain traversal segments"
exit 1
fi
if find "$PREFLIGHT_BUNDLE_ROOT" -type l -print -quit | grep -q .; then
echo "::error::lifecycle preflight artifact must not contain symlinks"
exit 1
fi
lifecycle_source="$PREFLIGHT_BUNDLE_ROOT/data/lifecycle_store"
snapshot_source="$PREFLIGHT_BUNDLE_ROOT/$SNAPSHOT_CHECKOUT_PATH/data/output"
if [ ! -d "$lifecycle_source" ] || [ ! -d "$snapshot_source" ]; then
echo "::error::lifecycle preflight artifact is missing required allowlisted directories"
exit 1
fi
snapshot_root="$GITHUB_WORKSPACE/$SNAPSHOT_CHECKOUT_PATH"
resolved_snapshot_root="$(realpath "$snapshot_root")"
case "$resolved_snapshot_root" in
"$GITHUB_WORKSPACE"/external/*) ;;
*) echo "::error::snapshot checkout resolved outside the external workspace"; exit 1 ;;
esac
if find "$snapshot_root" -type l -print -quit | grep -q .; then
echo "::error::snapshot checkout must not contain symlinks"
exit 1
fi
snapshot_target="$snapshot_root/data/output"
rm -rf -- "$LIFECYCLE_LOCAL_ROOT" "$snapshot_target"
mkdir -p "$LIFECYCLE_LOCAL_ROOT" "$GITHUB_WORKSPACE/$SNAPSHOT_CHECKOUT_PATH/data/output"
cp -a "$lifecycle_source/." "$LIFECYCLE_LOCAL_ROOT/"
cp -a "$snapshot_source/." "$snapshot_target/"
echo "CRYPTO_LIFECYCLE_PREFLIGHT_ROOT=$PREFLIGHT_BUNDLE_ROOT" >> "$GITHUB_ENV"
- name: Build lifecycle performance snapshots
run: quant-lifecycle monitor --domain ${{ inputs.strategy_domain }}
- name: Validate lifecycle prerequisites
run: quant-lifecycle doctor --domain ${{ inputs.strategy_domain }} --require-snapshot --require-backtest --max-freshness-days 7
- name: Run drift detection
run: quant-lifecycle drift --domain ${{ inputs.strategy_domain }} --no-alerts
- name: Sync drift alerts to GitHub Issues
env:
GH_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
python - <<'PY'
import os
from quant_platform_kit.strategy_lifecycle.codex_integration import create_issues_for_domain
owner, _, repository = os.environ["GITHUB_REPOSITORY"].partition("/")
if not owner or not repository:
raise RuntimeError("GITHUB_REPOSITORY must identify the reusable workflow caller")
os.environ["CODEX_AUDIT_ORG"] = owner
os.environ["CODEX_AUDIT_ORCHESTRATOR_REPO"] = repository
results = create_issues_for_domain("${{ inputs.strategy_domain }}")
created = [item for item in results if item.get("issue_url")]
print({"created_issues": len(created), "results": len(results)})
PY
- name: Checkout AIAuditBridge
uses: actions/checkout@v6
with:
repository: QuantStrategyLab/AIAuditBridge
ref: 59192b259bfbc4c959e7799ae514e2e5d4925498
path: external/AIAuditBridge
- name: Dual-review critical drift
env:
AIAUDIT_BRIDGE_ROOT: external/AIAuditBridge
CODEX_AUDIT_SERVICE_URL: ${{ secrets.codex_audit_service_url }}
AI_GATEWAY_SERVICE_URL: ${{ inputs.ai_gateway_service_url }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
script="external/AIAuditBridge/scripts/run_drift_dual_review.py"
if [ ! -f "$script" ]; then
echo "::notice::dual-review scripts unavailable; skipping until AIAuditBridge is merged"
exit 0
fi
if [ -z "${CODEX_AUDIT_SERVICE_URL:-}" ]; then
echo "::notice::CODEX_AUDIT_SERVICE_URL not configured; skipping dual-review dispatch"
exit 0
fi
review_output="${RUNNER_TEMP}/drift-dual-review.json"
set +e
PYTHONPATH=external/AIAuditBridge python "$script" \
--domain "${STRATEGY_DOMAIN}" --dispatch >"$review_output"
review_rc=$?
set -e
cat "$review_output"
if [ "$review_rc" -eq 3 ]; then
python - "$review_output" <<'PY'
import json
import sys
payload = json.load(open(sys.argv[1], encoding="utf-8"))
degraded = [item for item in payload.get("results", []) if item.get("degraded")]
if not payload.get("degraded") or not degraded:
raise SystemExit("exit code 3 requires degraded review results")
missing = [item for item in degraded if not item.get("dispatch", {}).get("github_issue")]
if missing:
raise SystemExit("degraded review requires a durable GitHub issue")
PY
echo "::warning::Dual review providers unavailable; durable issues created and critical drift alerts remain active"
exit 0
fi
exit "$review_rc"