diff --git a/.github/workflows/drift-check.yml b/.github/workflows/drift-check.yml index 0aaa8f2..578b9f8 100644 --- a/.github/workflows/drift-check.yml +++ b/.github/workflows/drift-check.yml @@ -10,6 +10,7 @@ on: permissions: contents: read issues: write + id-token: write jobs: drift: @@ -50,3 +51,19 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPOSITORY: ${{ github.repository }} run: python scripts/run_drift_github_issues.py + + - name: Checkout AIAuditBridge + uses: actions/checkout@v6 + with: + repository: QuantStrategyLab/AIAuditBridge + ref: main + 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: ${{ vars.AI_GATEWAY_SERVICE_URL }} + run: | + PYTHONPATH=external/AIAuditBridge python external/AIAuditBridge/scripts/run_drift_dual_review.py \ + --domain "${STRATEGY_DOMAIN}" --dispatch diff --git a/.github/workflows/evidence-gate.yml b/.github/workflows/evidence-gate.yml index 9261593..d6676c7 100644 --- a/.github/workflows/evidence-gate.yml +++ b/.github/workflows/evidence-gate.yml @@ -14,6 +14,7 @@ on: permissions: contents: read pull-requests: read + id-token: write concurrency: group: evidence-gate-${{ github.event.pull_request.number }} @@ -70,7 +71,17 @@ jobs: python -m pip install -e . pandas python -m pip install --no-deps -e external/QuantPlatformKit + - name: Checkout AIAuditBridge + uses: actions/checkout@v6 + with: + repository: QuantStrategyLab/AIAuditBridge + ref: main + path: external/AIAuditBridge + - name: Evaluate Evidence Gate env: GITHUB_BASE_REF: ${{ github.base_ref }} + AIAUDIT_BRIDGE_ROOT: external/AIAuditBridge + CODEX_AUDIT_SERVICE_URL: ${{ secrets.CODEX_AUDIT_SERVICE_URL }} + AI_GATEWAY_SERVICE_URL: ${{ vars.AI_GATEWAY_SERVICE_URL }} run: python scripts/gate_evidence_package.py diff --git a/scripts/gate_evidence_package.py b/scripts/gate_evidence_package.py index 7547c7c..4549367 100755 --- a/scripts/gate_evidence_package.py +++ b/scripts/gate_evidence_package.py @@ -93,6 +93,42 @@ def _validate_with_promotion_standard(path: Path) -> tuple[bool, list[str]]: return False, issues or ["promotion evidence package validation failed"] +def _run_promotion_dual_review(evidence_files: list[Path]) -> int: + root = Path(os.environ.get("AIAUDIT_BRIDGE_ROOT", "external/AIAuditBridge")) + script = root / "scripts" / "run_dual_review_pipeline.py" + if not script.is_file(): + print("[evidence-gate] dual-review skipped: AIAuditBridge not found") + return 0 + if str(os.environ.get("DUAL_REVIEW_GATE_SKIP", "")).strip().lower() in {"1", "true", "yes"}: + print("[evidence-gate] dual-review skipped by DUAL_REVIEW_GATE_SKIP") + return 0 + + worst = 0 + for path in evidence_files: + proc = subprocess.run( + [ + sys.executable, + str(script), + "--from-evidence", + str(path), + "--dispatch", + ], + cwd=str(root), + env={**os.environ, "PYTHONPATH": str(root)}, + check=False, + ) + print(f"[evidence-gate] dual-review {path.name} exit={proc.returncode}") + if proc.stdout: + print(proc.stdout.strip()) + if proc.stderr: + print(proc.stderr.strip(), file=sys.stderr) + worst = max(worst, proc.returncode) + if worst >= 2: + print("::error::Dual-review blocked promotion (disagreement or reject)", file=sys.stderr) + return 1 + return 0 + + def main() -> int: base_ref = os.environ.get("GITHUB_BASE_REF", "main").strip() or "main" diff = _git_diff(base_ref) @@ -122,7 +158,9 @@ def main() -> int: for issue in lifecycle_issues + standard_issues: print(f" - {issue}", file=sys.stderr) - return 1 if failed else 0 + if failed: + return 1 + return _run_promotion_dual_review(evidence_files) if __name__ == "__main__":