Skip to content

Long Horizon Shadow Signal #5

Long Horizon Shadow Signal

Long Horizon Shadow Signal #5

name: Long Horizon Shadow Signal
on:
schedule:
# Monthly shadow research run. This remains shadow-only and does not place
# trades.
- cron: "30 2 1 * *"
workflow_dispatch:
inputs:
provider:
description: "Bridge provider"
required: false
type: choice
default: "auto"
options:
- auto
- api
- anthropic
- codex
- openai
source_ref:
description: "Ref to pass to CodexAuditBridge"
required: false
default: "main"
permissions:
contents: read
issues: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: false
jobs:
dispatch-shadow-signal:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
BRIDGE_REPOSITORY: ${{ vars.CODEX_AUDIT_BRIDGE_REPOSITORY || 'QuantStrategyLab/CodexAuditBridge' }}
BRIDGE_REF: ${{ vars.CODEX_AUDIT_BRIDGE_REF || 'main' }}
BRIDGE_TASK: long_horizon_signal_shadow
BRIDGE_PROVIDER: ${{ github.event.inputs.provider || 'auto' }}
SOURCE_REF: ${{ github.event.inputs.source_ref || 'main' }}
SHADOW_SIGNAL_LABEL: long-horizon-shadow
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.11"
- name: Validate existing latest signal if present
run: python scripts/validate_latest_signal.py --allow-missing
- name: Build point-in-time context bundle
run: |
python scripts/build_context_bundle.py \
--output data/output/context_bundle/latest_context_bundle.json \
--allow-download-errors
- name: Create or update shadow signal issue
id: shadow_issue
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
python scripts/post_shadow_signal_request.py \
--repo "${GITHUB_REPOSITORY}" \
--source-ref "${SOURCE_REF}" \
--provider "${BRIDGE_PROVIDER}" \
--bridge-repository "${BRIDGE_REPOSITORY}" \
--context-file data/output/context_bundle/latest_context_bundle.json \
--label "${SHADOW_SIGNAL_LABEL}"
- name: Append shadow signal job summary
run: |
{
echo "## Long-horizon shadow signal"
echo ""
echo "- Issue: [${{ steps.shadow_issue.outputs.issue_title }}](${{ steps.shadow_issue.outputs.issue_url }})"
echo "- Issue action: \`${{ steps.shadow_issue.outputs.issue_action }}\`"
echo "- Source ref: \`${SOURCE_REF}\`"
echo "- Provider: \`${BRIDGE_PROVIDER}\`"
echo "- Bridge: \`${BRIDGE_REPOSITORY}\`"
echo "- Mode: \`shadow\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Detect GitHub App Credentials
id: app_credentials
env:
APP_ID: ${{ vars.CROSS_REPO_GITHUB_APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }}
run: |
set -euo pipefail
if [ -n "${APP_ID:-}" ] && [ -n "${APP_PRIVATE_KEY:-}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi
- name: Create GitHub App Token For Bridge Repository
id: bridge_app_token
if: steps.app_credentials.outputs.available == 'true'
continue-on-error: true
uses: actions/create-github-app-token@v3
with:
app-id: ${{ vars.CROSS_REPO_GITHUB_APP_ID }}
private-key: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: |
CodexAuditBridge
permission-actions: write
- name: Create shadow signal issue and dispatch bridge
env:
GH_TOKEN: ${{ github.token }}
APP_TOKEN: ${{ steps.bridge_app_token.outputs.token }}
CODEX_AUDIT_DISPATCH_TOKEN: ${{ secrets.CODEX_AUDIT_DISPATCH_TOKEN }}
ISSUE_NUMBER: ${{ steps.shadow_issue.outputs.issue_number }}
run: |
python - <<'PY'
import json
import os
import urllib.request
import urllib.error
repo = os.environ["GITHUB_REPOSITORY"]
bridge_repo = os.environ["BRIDGE_REPOSITORY"]
token = os.environ["GH_TOKEN"]
dispatch_token = os.environ.get("APP_TOKEN") or os.environ.get("CODEX_AUDIT_DISPATCH_TOKEN")
if not dispatch_token:
raise SystemExit("Bridge dispatch requires a GitHub App token or CODEX_AUDIT_DISPATCH_TOKEN")
def request(method, url, payload=None, auth_token=token):
data = json.dumps(payload).encode("utf-8") if payload is not None else None
req = urllib.request.Request(
url,
data=data,
method=method,
headers={
"Authorization": f"Bearer {auth_token}",
"Accept": "application/vnd.github+json",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req, timeout=60) as response:
body = response.read().decode("utf-8")
return response.status, json.loads(body) if body else None
dispatch_payload = {
"ref": os.environ["BRIDGE_REF"],
"inputs": {
"source_repo": repo,
"issue_number": os.environ["ISSUE_NUMBER"],
"source_ref": os.environ["SOURCE_REF"],
"mode": "review_and_fix",
"provider": os.environ["BRIDGE_PROVIDER"],
"task": os.environ["BRIDGE_TASK"],
"auto_merge": "false",
},
}
status, _ = request(
"POST",
f"https://api.github.com/repos/{bridge_repo}/actions/workflows/codex_audit.yml/dispatches",
dispatch_payload,
auth_token=dispatch_token,
)
if status not in (201, 204):
raise RuntimeError(f"unexpected dispatch status: {status}")
print(f"Dispatched {bridge_repo} for issue #{os.environ['ISSUE_NUMBER']}")
PY
- name: Upload context bundle
uses: actions/upload-artifact@v7
with:
name: long-horizon-context-bundle
path: data/output/context_bundle/latest_context_bundle.json