Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/global_etf_research_codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ jobs:
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@de13e486da1bdba60f425e576e944591fc97b809" \
./ues-source ./

- name: Verify audit-service authentication before research entry
if: inputs.execute == true
run: |
"$CASE_ROOT/venv/bin/python" -m scripts.run_global_etf_research_codegen --auth-preflight

- name: Run the fixed plan or execute path
run: |
set -euo pipefail
Expand All @@ -90,7 +95,7 @@ jobs:
umask 077
mkdir -p "$CASE_ROOT/artifact"
chmod 700 "$CASE_ROOT/artifact"
python3 - "$HOME/.local/state/aiauditbridge/global-etf-review-20260917/result.json" "$CASE_ROOT/artifact/result.json" <<'PY'
python3 - "$HOME/.local/state/aiauditbridge/global-etf-review-20260917-auth-recovery-35124525442/result.json" "$CASE_ROOT/artifact/result.json" <<'PY'
import json, sys
from pathlib import Path
source, target = map(Path, sys.argv[1:])
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/vps_codex_service_ops.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
CODEX_AUDIT_SSH_UNBAN_IP: ${{ inputs.ssh_unban_ip }}
CODEX_AUDIT_SERVICE_ALLOWED_REPOSITORIES: QuantStrategyLab/AIAuditBridge,QuantStrategyLab/BinancePlatform,QuantStrategyLab/CharlesSchwabPlatform,QuantStrategyLab/CnEquitySnapshotPipelines,QuantStrategyLab/CnEquityStrategies,QuantStrategyLab/CryptoLivePoolPipelines,QuantStrategyLab/CryptoStrategies,QuantStrategyLab/FirstradePlatform,QuantStrategyLab/HkEquitySnapshotPipelines,QuantStrategyLab/HkEquityStrategies,QuantStrategyLab/IBKRGatewayManager,QuantStrategyLab/InteractiveBrokersPlatform,QuantStrategyLab/LongBridgePlatform,QuantStrategyLab/MarketSignalSources,QuantStrategyLab/PoliticalEventTrackingResearch,QuantStrategyLab/QmtPlatform,QuantStrategyLab/QuantAdvisorResearch,QuantStrategyLab/QuantPlatformKit,QuantStrategyLab/QuantRuntimeSettings,QuantStrategyLab/QuantStrategyPlugins,QuantStrategyLab/ResearchSignalContextPipelines,QuantStrategyLab/SchwabTokenAutoRefresher,QuantStrategyLab/UsEquitySnapshotPipelines,QuantStrategyLab/UsEquityStrategies
# workflow_dispatch emits protected-main workflow_ref claims; the deploy script pins delegated QPK code by exact job_workflow_ref SHA.
CODEX_AUDIT_SERVICE_ALLOWED_WORKFLOW_REFS: QuantStrategyLab/AIAuditBridge/.github/workflows/codex_audit.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/research_input_readback.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/strategy_optimization_watcher.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/portfolio_research_proposal_diagnosis.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/dependency_audit.yml@refs/heads/main,QuantStrategyLab/CnEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/HkEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/UsEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/CryptoStrategies/.github/workflows/drift-check.yml@refs/heads/main
CODEX_AUDIT_SERVICE_ALLOWED_WORKFLOW_REFS: QuantStrategyLab/AIAuditBridge/.github/workflows/codex_audit.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/research_input_readback.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/strategy_optimization_watcher.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/portfolio_research_proposal_diagnosis.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/dependency_audit.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/global_etf_research_codegen.yml@refs/heads/main,QuantStrategyLab/CnEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/HkEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/UsEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/CryptoStrategies/.github/workflows/drift-check.yml@refs/heads/main
CODEX_AUDIT_SERVICE_ALLOWED_REFS: refs/heads/main
# Exact canonical audit job plus immutable QPK `uses:` refs pinned by strategy drift callers.
# Rotation tracked in #64; remove the old QPK SHA after final strategy-run verification.
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ actual synthetic test status; its structured assessments remain advisory.
`review_completed` proves a returned review and successful synthetic checks,
not correct financial claims, profitability, or out-of-sample validation.

The new fixed state directory is `~/.local/state/aiauditbridge/global-etf-review-20260917`;
the old `global-etf-codegen-20260916` directory is not changed or replayed.
The old `global-etf-review-20260917` claim, result, and response are retained as the
authorization failure record; this explicitly authorized recovery uses
`~/.local/state/aiauditbridge/global-etf-review-20260917-auth-recovery-35124525442`.
The older `global-etf-codegen-20260916` directory is not changed or replayed.
A claim without a terminal result remains unknown and is never retried
automatically. Only the advisory result is projected to a seven-day GitHub
artifact; source body and raw response stay private on VPS. No deployment,
Expand Down
2 changes: 1 addition & 1 deletion scripts/deploy_codex_audit_service.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ALLOWED_REPOSITORIES="${CODEX_AUDIT_SERVICE_ALLOWED_REPOSITORIES:-QuantStrategyL
# Direct review and strategy workflow identities are pinned to protected main because GitHub emits workflow_ref with the dispatch branch.
# Delegated reusable code is constrained separately by the exact job_workflow_ref SHA below.
# The ref allowlist retains PR merge refs because GitHub can preserve the incoming PR ref for reusable calls; _verify_github_oidc requires both allowlists.
ALLOWED_WORKFLOW_REFS="${CODEX_AUDIT_SERVICE_ALLOWED_WORKFLOW_REFS:-QuantStrategyLab/AIAuditBridge/.github/workflows/codex_audit.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/research_input_readback.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/strategy_optimization_watcher.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/portfolio_research_proposal_diagnosis.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/dependency_audit.yml@refs/heads/main,QuantStrategyLab/CnEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/HkEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/UsEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/CryptoStrategies/.github/workflows/drift-check.yml@refs/heads/main}"
ALLOWED_WORKFLOW_REFS="${CODEX_AUDIT_SERVICE_ALLOWED_WORKFLOW_REFS:-QuantStrategyLab/AIAuditBridge/.github/workflows/codex_audit.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/research_input_readback.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/strategy_optimization_watcher.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/portfolio_research_proposal_diagnosis.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/dependency_audit.yml@refs/heads/main,QuantStrategyLab/AIAuditBridge/.github/workflows/global_etf_research_codegen.yml@refs/heads/main,QuantStrategyLab/CnEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/HkEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/UsEquityStrategies/.github/workflows/drift-check.yml@refs/heads/main,QuantStrategyLab/CryptoStrategies/.github/workflows/drift-check.yml@refs/heads/main}"
ALLOWED_REFS="${CODEX_AUDIT_SERVICE_ALLOWED_REFS:-refs/heads/main}"
ALLOWED_REPOSITORY_VISIBILITIES="${CODEX_AUDIT_SERVICE_ALLOWED_REPOSITORY_VISIBILITIES:-public}"
# Exact canonical audit job identity. Single source of truth for delegated drift code follows.
Expand Down
18 changes: 17 additions & 1 deletion scripts/run_global_etf_research_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"docs/research/global_etf_absolute_volatility.md",
})
GLOBAL_ETF_TARGET_PATH = "src/us_equity_strategies/research/global_etf_absolute_volatility.py"
GLOBAL_ETF_STATE_ROOT = Path.home() / ".local/state/aiauditbridge/global-etf-review-20260917"
GLOBAL_ETF_STATE_ROOT = Path.home() / ".local/state/aiauditbridge/global-etf-review-20260917-auth-recovery-35124525442"
GLOBAL_ETF_WORKFLOW_NAME = "Global ETF Candidate Review"
GLOBAL_ETF_SOURCE_REPOSITORY = "QuantStrategyLab/AIAuditBridge"
_REVISION = re.compile(r"^[0-9a-f]{40}$")
Expand Down Expand Up @@ -365,6 +365,17 @@ def execute(prompt: str):
return execute


def _auth_preflight() -> bool:
"""Check the audit-service route before any bounded research work begins."""
try:
from ai_gateway_client import AiGatewayClient, GatewayConfig

AiGatewayClient(GatewayConfig.from_env()).get_health()
except Exception:
return False
return True


def _run_global_candidate_tests(candidate_root: Path, *, baseline_root: Path) -> dict[str, str]:
"""Run the fixed Global profile through the shared Docker test helper."""
try:
Expand Down Expand Up @@ -468,8 +479,13 @@ def plan() -> dict[str, Any]:
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--execute", action="store_true", help="run only in the fixed main self-hosted workflow")
parser.add_argument("--auth-preflight", action="store_true", help="verify audit-service authentication only")
parser.add_argument("--ues-repo-root", type=Path, default=Path("/opt/ues-source"))
args = parser.parse_args(argv)
if args.auth_preflight:
passed = _auth_preflight()
print("auth_preflight_passed" if passed else "auth_preflight_failed")
return 0 if passed else 1
if not args.execute:
print(json.dumps(plan(), ensure_ascii=False, sort_keys=True, separators=(",", ":")))
return 0
Expand Down
34 changes: 34 additions & 0 deletions tests/test_global_etf_research_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,40 @@ def test_global_codegen_docker_integration_fixture(tmp_path):


class GlobalResearchCodegenTests(TestCase):
def test_auth_preflight_is_an_execute_only_gate_before_research_entry(self):
workflow = (Path(__file__).parents[1] / ".github/workflows/global_etf_research_codegen.yml").read_text()
preflight = " - name: Verify audit-service authentication before research entry\n"
entry = " - name: Run the fixed plan or execute path\n"

self.assertIn(preflight, workflow)
self.assertIn(" if: inputs.execute == true\n", workflow.split(preflight, 1)[1].split(entry, 1)[0])
self.assertLess(workflow.index(preflight), workflow.index(entry))

def test_auth_preflight_hides_exception_and_stops_before_research(self):
client = SimpleNamespace(get_health=lambda: (_ for _ in ()).throw(RuntimeError("sensitive detail")))
output = io.StringIO()
with patch.dict(sys.modules, {"ai_gateway_client": SimpleNamespace(
AiGatewayClient=lambda config: client, GatewayConfig=SimpleNamespace(from_env=lambda: object()),
)}), patch.object(codegen, "run_global_etf_research_codegen_case") as run_research, redirect_stdout(output):
status = codegen.main(["--auth-preflight"])

self.assertEqual(status, 1)
self.assertEqual(output.getvalue().strip(), "auth_preflight_failed")
run_research.assert_not_called()

def test_auth_preflight_only_reports_passed_after_health_check(self):
calls = []
client = SimpleNamespace(get_health=lambda: calls.append("health"))
output = io.StringIO()
with patch.dict(sys.modules, {"ai_gateway_client": SimpleNamespace(
AiGatewayClient=lambda config: client, GatewayConfig=SimpleNamespace(from_env=lambda: object()),
)}), redirect_stdout(output):
status = codegen.main(["--auth-preflight"])

self.assertEqual(status, 0)
self.assertEqual(calls, ["health"])
self.assertEqual(output.getvalue().strip(), "auth_preflight_passed")

def test_workflow_plan_branch_runs_without_execute_venv(self):
workflow = (Path(__file__).parents[1] / ".github/workflows/global_etf_research_codegen.yml").read_text()
step = workflow.split(" - name: Run the fixed plan or execute path\n", 1)[1]
Expand Down
45 changes: 45 additions & 0 deletions tests/test_oidc_reusable_workflow_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import time
import unittest
from pathlib import Path
from unittest.mock import patch

from service import auth
Expand Down Expand Up @@ -114,6 +115,50 @@ def test_direct_audit_bridge_caller_does_not_require_reusable_workflow(self) ->
with self.assertRaisesRegex(PermissionError, "workflow_ref must be a string"):
self._verify(payload, env)

def test_global_etf_codegen_workflow_is_direct_audit_bridge_caller(self) -> None:
workflow_ref = (
"QuantStrategyLab/AIAuditBridge/.github/workflows/"
"global_etf_research_codegen.yml@refs/heads/main"
)
payload: dict[str, object] = {
"aud": "quant-codex-audit",
"iss": auth.GITHUB_OIDC_ISSUER,
"exp": int(time.time()) + 300,
"repository": "QuantStrategyLab/AIAuditBridge",
"workflow_ref": workflow_ref,
"ref": "refs/heads/main",
}
env = {
"CODEX_AUDIT_SERVICE_ALLOWED_REPOSITORIES": "QuantStrategyLab/AIAuditBridge",
"CODEX_AUDIT_SERVICE_ALLOWED_WORKFLOW_REFS": workflow_ref,
"CODEX_AUDIT_SERVICE_ALLOWED_REFS": "refs/heads/main",
"CODEX_AUDIT_SERVICE_ALLOWED_DIRECT_REPOSITORIES": "QuantStrategyLab/AIAuditBridge",
"CODEX_AUDIT_SERVICE_ALLOWED_JOB_WORKFLOW_REFS": "",
}

self.assertEqual(self._verify(payload, env)["workflow_ref"], workflow_ref)
for rejected_workflow_ref, rejected_ref in (
(workflow_ref.replace("refs/heads/main", "refs/heads/feature"), "refs/heads/feature"),
(workflow_ref.replace("global_etf_research_codegen.yml", "other.yml"), "refs/heads/main"),
):
with self.subTest(workflow_ref=rejected_workflow_ref):
payload["workflow_ref"] = rejected_workflow_ref
payload["ref"] = rejected_ref
with self.assertRaises(PermissionError):
self._verify(payload, env)

def test_global_etf_codegen_workflow_is_in_both_deployment_allowlists(self) -> None:
workflow_ref = (
"QuantStrategyLab/AIAuditBridge/.github/workflows/"
"global_etf_research_codegen.yml@refs/heads/main"
)
root = Path(__file__).parents[1]
deploy_script = (root / "scripts/deploy_codex_audit_service.sh").read_text()
ops_workflow = (root / ".github/workflows/vps_codex_service_ops.yml").read_text()

self.assertIn(workflow_ref, deploy_script)
self.assertIn(workflow_ref, ops_workflow)

def test_strategy_drift_requires_trusted_qpk_reusable_workflow(self) -> None:
qpk_job_ref = (
"QuantStrategyLab/QuantPlatformKit/.github/workflows/"
Expand Down