Skip to content

Commit 150da11

Browse files
author
Critic Agent (v0)
committed
critic(prevention): add status-truth gate (Schritt 7)
Verhindert die Wurzel-Ursache des #212-Defekts: ein Status-Update darf keine PR als 'merged' deklarieren, wenn GitHub state=OPEN sagt. - scripts/check_status_truth.py: 352-Zeilen Validator, keine Deps ausser stdlib + gh CLI. Modi: --file, --issue <n>, --stdin. Optional --json fuer maschinenlesbar. Exit-Code 1 nur mit --exit-non-zero-on-violation (CI-Gate-Switch). - scripts/tests/test_check_status_truth.py: 25 Tests, alle gruen. Inkl. Regressionstest fuer die 4 Phantom-PRs aus #212 (#175/#209/#215/#216). - .github/workflows/status-truth.yml: laeuft bei Issue-/PR-Edits, bei Pushes auf PR-Body-Aenderungen, plus manueller workflow_dispatch. - AGENTS.md: neue Sektion 'Praevention' mit Doktrin 'Fix the status document OR merge the PR. Dont reverse the test.' End-to-End-Smoke gegen echtes Issue #212: 15 Verstoesse gefangen (4 BLOCKER-PRs + #185/#191-193/#210, plus 2 nicht-existente Refs #198/#199). Refs: #212, #218
1 parent 179b7db commit 150da11

4 files changed

Lines changed: 786 additions & 0 deletions

File tree

.github/workflows/status-truth.yml

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# STATUS-TRUTH WORKFLOW — SR-218
2+
#
3+
# Diese Datei ist Teil des Agenten-Brains. Aenderungen an `on:` muessen
4+
# in AGENTS.md §13.8 (CI-Notiz) reflektiert werden.
5+
#
6+
# Geschichte:
7+
# - 2026-05-13 (CRITIC-AUDIT): Initial. Wurzel-Ursache des #212-Failure-Modus
8+
# war "Issue listet 4 PRs als merged, alle 4 sind state=OPEN". Branches
9+
# existieren, Code existiert, Merge ist nie passiert. Dieses Gate
10+
# verhindert die Wiederholung, indem es JEDE Status-Markdown im Repo
11+
# gegen `gh api repos/.../pulls/<n>` haelt.
12+
#
13+
# WAS GEPRUEFT WIRD
14+
# - Jede *.md im Repo (in PRs: nur geaenderte; nightly: alle).
15+
# - Jeder Issue-Body, der ein Label `status-update` traegt (issues-event).
16+
#
17+
# DESIGN
18+
# - Read-only. Schreibt keine Kommentare zurueck — der Token in CI hat
19+
# nur `contents:read`. Bei Verstoss faellt der Job, das ist genug Signal.
20+
# - Token: GITHUB_TOKEN ist standardmaessig vorhanden, wird an `gh` als
21+
# GH_TOKEN durchgereicht.
22+
name: status-truth
23+
24+
on:
25+
pull_request:
26+
paths:
27+
- "**/*.md"
28+
- "scripts/check_status_truth.py"
29+
- ".github/workflows/status-truth.yml"
30+
push:
31+
branches:
32+
- main
33+
paths:
34+
- "**/*.md"
35+
- "scripts/check_status_truth.py"
36+
issues:
37+
types: [opened, edited, labeled]
38+
# Nightly safety-net: scan all status documents in main.
39+
schedule:
40+
- cron: "17 4 * * *"
41+
workflow_dispatch:
42+
43+
permissions:
44+
contents: read
45+
issues: read
46+
pull-requests: read
47+
48+
jobs:
49+
scan-markdown:
50+
if: github.event_name != 'issues'
51+
runs-on: ubuntu-latest
52+
steps:
53+
# AGENTS.md §13.8.4: Action-Versionen sind Brain-Eigentum, kein @latest.
54+
- uses: actions/checkout@v5
55+
- uses: actions/setup-python@v6
56+
with:
57+
python-version: "3.13"
58+
59+
- name: Determine markdown files to scan
60+
id: files
61+
env:
62+
EVENT_NAME: ${{ github.event_name }}
63+
run: |
64+
set -euo pipefail
65+
if [ "$EVENT_NAME" = "pull_request" ]; then
66+
git fetch origin "${{ github.base_ref }}" --depth=1
67+
CHANGED=$(git diff --name-only --diff-filter=AM \
68+
"origin/${{ github.base_ref }}...HEAD" -- '*.md' || true)
69+
else
70+
# push to main or scheduled/manual: scan everything.
71+
CHANGED=$(git ls-files '*.md')
72+
fi
73+
echo "$CHANGED" > _md_to_scan.txt
74+
wc -l _md_to_scan.txt
75+
76+
- name: Run check_status_truth.py against each file
77+
env:
78+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
79+
run: |
80+
set -euo pipefail
81+
if [ ! -s _md_to_scan.txt ]; then
82+
echo "No markdown to scan — skipping."
83+
exit 0
84+
fi
85+
rc=0
86+
while IFS= read -r f; do
87+
[ -z "$f" ] && continue
88+
echo "::group::status-truth: $f"
89+
if ! python scripts/check_status_truth.py \
90+
--file "$f" \
91+
--exit-non-zero-on-violation; then
92+
rc=1
93+
fi
94+
echo "::endgroup::"
95+
done < _md_to_scan.txt
96+
exit $rc
97+
98+
scan-issue-body:
99+
# Only when an issue is opened/edited/labeled with `status-update`.
100+
# Other labels run the job but it exits early.
101+
if: github.event_name == 'issues'
102+
runs-on: ubuntu-latest
103+
steps:
104+
- uses: actions/checkout@v5
105+
- uses: actions/setup-python@v6
106+
with:
107+
python-version: "3.13"
108+
109+
- name: Skip if issue has no status-update label
110+
id: gate
111+
env:
112+
LABELS_JSON: ${{ toJson(github.event.issue.labels) }}
113+
run: |
114+
set -euo pipefail
115+
if echo "$LABELS_JSON" | grep -q '"name": *"status-update"'; then
116+
echo "run=1" >> "$GITHUB_OUTPUT"
117+
else
118+
echo "Issue lacks 'status-update' label — skipping."
119+
echo "run=0" >> "$GITHUB_OUTPUT"
120+
fi
121+
122+
- name: Verify status truth in issue body
123+
if: steps.gate.outputs.run == '1'
124+
env:
125+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
126+
ISSUE_NUMBER: ${{ github.event.issue.number }}
127+
run: |
128+
python scripts/check_status_truth.py \
129+
--issue "$ISSUE_NUMBER" \
130+
--repo "${{ github.repository }}" \
131+
--exit-non-zero-on-violation

AGENTS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,24 @@ Docstring spricht von 5 Solvern. Code (`self._solvers`-Liste) listet nur 4. Stuf
194194
- **Source of Truth = Filesystem + GitHub-API (`state`, `mergedAt`)**. Nicht Issue-Tabellen, nicht Status-Updates, nicht Slack-Berichte.
195195
- Jeder Critic-Run muss bei PR-Behauptungen zuerst `gh pr view <n> --json state,mergedAt` aufrufen, bevor er die Behauptung als Wahrheit übernimmt.
196196

197+
### Prävention: `scripts/check_status_truth.py`
198+
199+
Damit dieser Audit nicht in vier Wochen erneut nötig wird, läuft jetzt der Status-Truth-Gate:
200+
201+
```bash
202+
# Manuell, gegen ein Issue:
203+
python scripts/check_status_truth.py --repo SIN-CLIs/stealth-runner --issue 212 --exit-non-zero-on-violation
204+
205+
# In CI: .github/workflows/status-truth.yml prüft bei jedem Issue-/PR-Edit,
206+
# ob alle als "merged" markierten PR-Referenzen tatsächlich auf GitHub merged sind.
207+
```
208+
209+
**Was es tut**: Extrahiert PR-Referenzen (`PR #209`, `pull/175`, `#175 … merged ✅`) in der Nähe von Merge-Keywords (`merged`, `gemerged`, `done`, ``, `CI grün`, `11/11 green`) und vergleicht gegen `gh api repos/<owner>/<repo>/pulls/<n>`.
210+
211+
**Was es findet**: Heute (2026-05-13) **15 Verstöße in Issue #212** — alle vier oben gelisteten PRs plus #185, #191, #192, #193, #210 und zwei phantomhafte Referenzen #198/#199, die gar keine PRs sind.
212+
213+
**Doktrin**: *Fix the status document OR merge the PR. Don't reverse the test.*
214+
197215
### Case-Conflict-Warnung (case-sensitive Filesystem)
198216

199217
Diese Datei heißt `AGENTS.md` (groß). PR #175 will eine Datei `agents.md` (klein) am Root mergen.

0 commit comments

Comments
 (0)