Skip to content

Commit f4dfbe0

Browse files
authored
Improve monthly review audit controls (#48)
* Improve monthly review audit controls * Fix release status summary freshness test
1 parent 527404e commit f4dfbe0

9 files changed

Lines changed: 43 additions & 14 deletions

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ Outputs:
525525

526526
Behavior:
527527

528-
- uses only upstream monthly build outputs
528+
- requires upstream monthly build outputs, including the same-cycle shadow build summary and shadow candidate track summary
529529
- summarizes official baseline release status, publish manifest status, and shadow track coverage
530530
- emits warnings when monthly artifacts do not align on `as_of_date`, `version`, or `mode`
531531
- produces a structured review prompt/checklist for manual follow-up
@@ -541,18 +541,20 @@ The AI review covers:
541541
- **Anomaly detection**: flags unexpected warnings, stale artifacts, validation failures, or suspicious ranking scores
542542
- **Downstream impact**: notes implications for BinancePlatform (the downstream execution engine), including pool changes and degradation risk
543543
- **Operator action items**: summarizes the checklist and adds any AI-identified follow-up items
544-
- **Code improvements**: if concrete, low-risk improvements are found, Claude may open a Pull Request (never auto-merged)
544+
- **Code improvements**: structured review output can feed the monthly optimization planner; low-risk `auto-pr-safe` tasks may become automation PRs, while sensitive selector changes remain manual-review work
545545

546546
All analysis is posted in both English and Chinese.
547547

548548
### Required GitHub Secret
549549

550550
- `ANTHROPIC_API_KEY`: Anthropic API key for Claude Code Action
551+
- `OPENAI_API_KEY`: OpenAI API key for the secondary monthly review
551552

552553
Setup:
553554

554555
```bash
555556
gh secret set ANTHROPIC_API_KEY --body "sk-ant-..."
557+
gh secret set OPENAI_API_KEY --body "sk-..."
556558
```
557559

558560
The AI review workflow runs on `ubuntu-latest` (no self-hosted runner required) and costs approximately $0.01-0.05 per monthly run.

docs/operator_runbook.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ This repository owns the monthly reporting and publish-status summary layer for
6262
Operator-facing summary entrypoints:
6363

6464
- `scripts/run_release_status_summary.py` for the canonical release-status summary built from the current official artifacts
65-
- `scripts/run_monthly_review_briefing.py` for the monthly review package; it can run on official release artifacts alone and adds shadow coverage when available
65+
- `scripts/run_monthly_review_briefing.py` for the monthly review package; it requires same-cycle shadow outputs so review evidence always includes official and challenger coverage
6666
- `scripts/run_monthly_build_telegram.py` for the optional short Telegram health notification or local preview text
6767
- `scripts/run_monthly_report_bundle.py` for the standard monthly report bundle used by Actions artifacts and AI review handoff
6868
- `scripts/write_release_heartbeat.py` for the lightweight logs-branch heartbeat record
@@ -106,6 +106,11 @@ Boundary rules:
106106
.venv/bin/python scripts/run_release_status_summary.py
107107
```
108108

109+
Before the review package is generated, confirm the monthly shadow build has produced both
110+
`data/output/monthly_shadow_build_summary.json` and
111+
`data/output/shadow_candidate_tracks/track_summary.csv`. The hosted monthly publish
112+
workflow runs this shadow build automatically before the review step.
113+
109114
6. Generate the monthly review package:
110115

111116
```bash

scripts/prepare_auto_optimization_pr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ def _is_completed_low_risk_task(action: dict[str, Any], repo_root: Path) -> bool
187187
):
188188
monthly_report = _read_text(repo_root / "scripts" / "run_monthly_report_bundle.py")
189189
return (
190-
"No explicit gating or no-trade reasons were recorded this month." in monthly_report
191-
and "gating_summary" in monthly_report
190+
"## Zero-Trade Diagnostics" in monthly_report
191+
and "by_category_and_gate" in monthly_report
192192
)
193193

194194
return False

scripts/render_monthly_ai_review.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,12 @@ def build_full_review_markdown(
7777
primary_title: str,
7878
secondary_review_payload: dict[str, Any] | None = None,
7979
) -> str:
80-
lines = [f"## {primary_title}", "", primary_review_text.strip()]
80+
normalized_primary_review = primary_review_text.strip()
81+
duplicate_title = f"## {primary_title}"
82+
if normalized_primary_review.startswith(duplicate_title):
83+
normalized_primary_review = normalized_primary_review[len(duplicate_title) :].lstrip()
84+
85+
lines = [f"## {primary_title}", "", normalized_primary_review]
8186
if secondary_review_payload is not None:
8287
lines.extend(["", "---", "", render_secondary_review_markdown(secondary_review_payload)])
8388
return "\n".join(lines).strip() + "\n"

scripts/run_monthly_review_briefing.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,22 @@ def derive_warnings(inputs: dict[str, Any]) -> list[str]:
162162
return warnings
163163

164164

165+
def require_shadow_outputs(inputs: dict[str, Any]) -> None:
166+
availability = inputs["availability"]
167+
missing_items: list[str] = []
168+
if not availability["monthly_shadow_build_summary"]:
169+
missing_items.append("monthly_shadow_build_summary.json")
170+
if not availability["track_summary"]:
171+
missing_items.append("shadow_candidate_tracks/track_summary.csv")
172+
173+
if missing_items:
174+
missing_text = ", ".join(missing_items)
175+
raise RuntimeError(
176+
"monthly shadow build outputs are required before generating the monthly review package: "
177+
f"{missing_text}"
178+
)
179+
180+
165181
def build_review_questions() -> list[str]:
166182
return [
167183
"Does the official baseline publish chain look internally consistent for this month?",
@@ -342,6 +358,7 @@ def main() -> None:
342358
args = parse_args()
343359
output_dir = Path(args.output_dir).resolve()
344360
inputs = build_review_inputs(output_dir)
361+
require_shadow_outputs(inputs)
345362
payload = build_review_payload(inputs)
346363
outputs = write_outputs(payload, output_dir)
347364

scripts/run_release_status_summary.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def build_release_status_payload(
8080
max_age_days: int = 45,
8181
require_freshness: bool = True,
8282
ranking_preview_size: int = 5,
83+
reference_date: Any = None,
8384
) -> dict[str, Any]:
8485
root = Path(output_dir)
8586
universe = load_json(root / "latest_universe.json")
@@ -94,6 +95,7 @@ def build_release_status_payload(
9495
expected_source_project=live_pool.get("source_project"),
9596
expected_pool_size=live_pool.get("pool_size"),
9697
max_age_days=max_age_days,
98+
reference_date=reference_date,
9799
require_manifest=True,
98100
require_artifact_manifest=True,
99101
require_freshness=require_freshness,

tests/test_monthly_review_briefing.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,12 @@ def test_build_review_payload_warns_when_track_dates_do_not_align(self) -> None:
116116
self.assertIn("## Warnings", review_md)
117117
self.assertIn("official_baseline remains the production reference", prompt_md)
118118

119-
def test_build_review_payload_allows_official_only_outputs(self) -> None:
119+
def test_build_review_payload_requires_shadow_outputs(self) -> None:
120120
with tempfile.TemporaryDirectory() as tmp_dir:
121121
output_dir = self.write_fixture_files(Path(tmp_dir), include_shadow_outputs=False)
122122
inputs = MODULE.build_review_inputs(output_dir)
123-
payload = MODULE.build_review_payload(inputs)
124-
review_md = MODULE.render_review_markdown(payload)
125-
126-
self.assertEqual(payload["status"], "ok")
127-
self.assertFalse(payload["shadow_analysis_available"])
128-
self.assertIn("not generated in this run", review_md)
123+
with self.assertRaisesRegex(RuntimeError, "monthly shadow build outputs are required"):
124+
MODULE.require_shadow_outputs(inputs)
129125

130126

131127
if __name__ == "__main__":

tests/test_release_status_summary.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def test_build_release_status_payload_reports_ok_for_consistent_release(self) ->
162162
max_age_days=45,
163163
require_freshness=False,
164164
ranking_preview_size=3,
165+
reference_date="2026-04-01",
165166
)
166167
outputs = MODULE.write_outputs(payload, output_dir)
167168

tests/test_render_monthly_ai_review.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_render_secondary_review_markdown_includes_actions_and_flags(self) -> No
3737

3838
def test_build_full_review_markdown_includes_primary_and_secondary_sections(self) -> None:
3939
markdown = build_full_review_markdown(
40-
"## English\nPrimary review",
40+
"## Claude Primary Review\n\n## English\nPrimary review",
4141
primary_title="Claude Primary Review",
4242
secondary_review_payload={
4343
"provider_display_name": "GPT Secondary Review",
@@ -54,6 +54,7 @@ def test_build_full_review_markdown_includes_primary_and_secondary_sections(self
5454
self.assertIn("## Claude Primary Review", markdown)
5555
self.assertIn("## Secondary Review (GPT Secondary Review)", markdown)
5656
self.assertIn("## English", markdown)
57+
self.assertEqual(markdown.count("## Claude Primary Review"), 1)
5758

5859

5960
if __name__ == "__main__":

0 commit comments

Comments
 (0)