forked from NSPG13/agent-bounties
-
Notifications
You must be signed in to change notification settings - Fork 0
118 lines (106 loc) · 4.27 KB
/
Copy pathoperational-control-loop.yml
File metadata and controls
118 lines (106 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
name: Operational Control Loop
on:
workflow_dispatch:
inputs:
api_base_url:
description: "Deployed API base URL"
required: false
type: string
mcp_base_url:
description: "Deployed MCP base URL"
required: false
type: string
expected_revision:
description: "Exact deployed revision; defaults to checked-out main"
required: false
type: string
schedule:
- cron: "17,47 * * * *"
permissions:
contents: read
concurrency:
group: operational-control-loop
cancel-in-progress: true
jobs:
deployment-paused:
if: github.event_name != 'workflow_dispatch' && vars.RENDER_DEPLOY_PAUSE_REASON != ''
runs-on: ubuntu-latest
steps:
- name: Report declared deployment pause
run: echo "Operational control loop paused - ${{ vars.RENDER_DEPLOY_PAUSE_REASON }}" >> "$GITHUB_STEP_SUMMARY"
observe-plan-verify:
if: github.event_name == 'workflow_dispatch' || vars.RENDER_DEPLOY_PAUSE_REASON == ''
runs-on: ubuntu-latest
timeout-minutes: 10
env:
PRODUCTION_API_BASE_URL: ${{ inputs.api_base_url || vars.PRODUCTION_API_BASE_URL || 'https://api.agentbounties.app' }}
PRODUCTION_MCP_BASE_URL: ${{ inputs.mcp_base_url || vars.PRODUCTION_MCP_BASE_URL || 'https://mcp.agentbounties.app' }}
PRODUCTION_EXPECTED_REVISION: ${{ inputs.expected_revision || github.sha }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Validate bounded recovery policy and corpus
run: |
python scripts/self_heal.py validate-policy \
--policy ops/self-healing-policy.json
python scripts/self_heal.py bench \
--policy ops/self-healing-policy.json \
--fixtures ops/fixtures/recovery-cases.json \
--output target/operations/recovery-bench.json
- name: Observe production and build recovery plan
id: observe
continue-on-error: true
run: |
python scripts/self_heal.py observe \
--policy ops/self-healing-policy.json \
--api-url "${PRODUCTION_API_BASE_URL}" \
--mcp-url "${PRODUCTION_MCP_BASE_URL}" \
--expected-revision "${PRODUCTION_EXPECTED_REVISION}" \
--snapshot-out target/operations/snapshot.json \
--plan-out target/operations/recovery-plan.json
- name: Publish bounded recovery summary
if: always()
run: |
python - <<'PY'
import json
import os
from pathlib import Path
plan_path = Path("target/operations/recovery-plan.json")
summary_path = Path(os.environ["GITHUB_STEP_SUMMARY"])
if not plan_path.exists():
summary_path.write_text(
"# Operational control loop\n\nNo recovery plan was produced. Inspect the workflow logs.\n",
encoding="utf-8",
)
raise SystemExit(0)
plan = json.loads(plan_path.read_text(encoding="utf-8"))
lines = [
"# Operational control loop",
"",
f"- Decision: `{plan['decision']}`",
f"- Severity: `{plan['severity']}`",
f"- Automatic actions: `{len(plan['automatic_actions'])}`",
f"- Manual actions: `{len(plan['manual_actions'])}`",
"",
"Automatic actions are restricted to R0-R2 availability/read-model recovery. No wallet, contract, verification, or settlement authority is present.",
]
if plan["reasons"]:
lines.extend(["", "## Reasons", ""])
lines.extend(f"- {reason}" for reason in plan["reasons"])
summary_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
- name: Upload recovery evidence
if: always()
uses: actions/upload-artifact@v7.0.1
with:
name: operational-control-loop-${{ github.run_id }}
path: target/operations/
if-no-files-found: error
retention-days: 30
- name: Fail closed on unresolved escalation
if: steps.observe.outcome != 'success'
run: |
echo "The recovery plan requires containment or operator escalation." >&2
exit 1