Skip to content

status.py health alerts - #59

Merged
Deicyde merged 4 commits into
mainfrom
claude/status-health-alerts
Oct 14, 2026
Merged

Deicyde merged 4 commits into
mainfrom
claude/status-health-alerts

Conversation

@Deicyde

@Deicyde Deicyde commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Problem

manage/status.py --live is the dashboard HANDOFF.md calls live ground truth. It could not see any of the failures that matter now:

  • Nightly review. Every run since the 2026-09-18 night has errored on every article with 0 tokens. Live /stats read review at 117 runs / 1,761 articles / 843 errors on 2026-10-12 and 118 / 1,776 / 858 on 2026-10-13, tokens 26,345,645 both times. status.py has no pipeline-health check (manage/status.py:88-104 on main).
  • Bot gate. --live runs bot/poll.py --decide with a 12 s timeout and drops the line silently when it times out (manage/status.py:44-49, 69-73). It took about 17 s on a loaded machine. Even when it finishes, it uses the operator's own gh login, so it can't see the CI poller's token failing: CI run 38435564796 (2026-10-13) logged POLL_DECISION=wait.
  • Batch PR. Nothing reports that mathlib4 #42608 has been open since 2026-08-10, or that it carries merge-conflict (gh pr view 42608 --repo leanprover-community/mathlib4).
  • Brain release. Nothing shows which Brain release is live (https://wikilean.jackmccarthy.org/assets/brain/current.json).
  • False graph alert. "concept graph is stale" fires on a 4-second mtime gap left by one bulk copy (manage/refresh.py:78, strict <).

Change

All under --live, read-only (GETs and gh reads):

  • Review health.
    • Per run from GET /api/runs?kind=review when that endpoint is deployed (it ships in a separate Worker PR). Alerts on a streak of runs where errors equal articles or tokens are 0, prints the newest run's notes (the runner's error sample), and alerts when no run has finished in 36 h.
    • Until then it falls back to the /stats Pipeline-runs totals, compared with the previous reading in gitignored site/cache/.status_live.json. That state file is validated and written atomically. The fallback alerts when errors equal articles, when tokens are unchanged across new runs, when no new run appears for 36 h, and when the table can't be parsed.
  • Gate.
    • Always printed: gate CI <decision> (run <id>, <age>), read from the latest finished wikidata-poll run on main (cancelled runs skipped).
    • CI's decision only. A local decide would read this checkout's copy of the bot state, which the poller stops updating once its state lives on the bot-state branch, so it says nothing about the live poller.
    • Alerts on CI-side signals: a POLL_ERROR: line from any poll step, a failed run, or no finished run in 12 h.
    • Alerts when gh can't list the runs or read the newest run's log, instead of showing only CI ?.
  • Batch PR. Reads current_pr from the poller's bot-state branch.
    • On a 404 (no such branch yet) it uses the local snapshot and labels the line local snapshot; no bot-state branch. Any other read failure alerts and skips the batch PR check rather than fall back to the local snapshot.
    • An OPEN PR alerts on a conflict (mergeable or mathlib's merge-conflict label) and when it has been open over 30 days. The conflict alert notes that the poller rebuilds a batch off master only once it is settled.
    • CLOSED-unmerged alerts. MERGED doesn't, and neither does a Bors merge (CLOSED with a [Merged by Bors] - title), which is how mathlib merges.
  • Brain. Prints the live release_id and audited_at.
  • Graph staleness. A 300 s allowance; status.py recomputes it from the digest's raw mtimes, so existing digests stop alerting. refresh.py shares the helper and finds it under python -I.
  • No digest. All live checks still run.
  • Offline path (the SessionStart hook) is unchanged apart from the graph rule. It makes one git status call and writes nothing; a test asserts both.
  • manage/test_status.py (23 tests, every network call stubbed), registered in scripts/ci-python.sh.

How tested

  • python manage/test_status.py: 23 passed under Python 3.12 and under macOS system Python 3.9.6. The SessionStart hook may run either.
  • The gh checks were exercised against the real repos, read-only:
    • ci_gate() → wait from the latest main run (38518943971), no alert;
    • ci_bot_state() → HTTP 404, reported as no bot-state branch (none exists yet), no alert;
    • batch_pr(42608) → OPEN, conflict (via the label while GitHub reported UNKNOWN), open 65 days;
    • batch_pr on a recent Bors-merged mathlib PR → MERGED (Bors), no alert.
  • Parsers were checked against a saved copy of the live /stats page and a real gh run view --log.
  • Release check: site/ops/brain_release_authority.py blocking_changes on this branch's diff reports release-neutral.

Risks

  • The current token failure. On main, the CI poller turns a 401 into wait, so the gate alert can't see it. It will once the poller fails closed with POLL_ERROR; that change is in a separate PR.
  • Fallback blind spot. The /stats fallback can't see a failing night between two healthy ones, and its first reading only saves a baseline. The per-run path has neither limit.
  • Latency. --live makes about seven network calls in sequence. The worst case is a few minutes when GitHub is slow.
  • Missing doc pointer. Alerts point to docs/OPERATIONS.md, which arrives with PR Operations runbook and review-outage diagnosis #56.

What Jack must do

Nothing to configure. gh must be logged in for the gate and batch-PR lines.

… PR and Brain

status.py --live could not see the failures that mattered: the nightly
review has errored on every article with 0 tokens since 2026-09-18, the
bot gate line vanished whenever the local decide timed out (and a local
decide uses the operator's gh login, so it cannot see the CI token
failing), and nothing reported the batch PR's conflict or age.

--live now compares the /stats review totals with the previous reading
(site/cache/.status_live.json) and alerts when errors equal articles or
tokens stay flat; prints 'gate: local <act|wait|timeout/error> · CI
<decision>' from the latest completed wikidata-poll run's Decide step
and alerts when CI says wait where local says act; alerts when the
batch PR conflicts (mergeable or the merge-conflict label) or has been
open 30+ days; prints the live Brain release_id and audited_at. Live
checks also run when no digest exists.

The concept-graph staleness check now allows 300 s of slack, so one
bulk copy (4 s apart on 2026-08-19) no longer raises it; status.py
recomputes it from the digest's mtimes so old digests stop alerting.
- Review health reads GET /api/runs per run when deployed (failing
  streak, notes, no run in 36h), else compares /stats totals with a
  validated, atomically written state; alerts when review stops running
  and when the /stats table can't be parsed.
- The gate no longer alerts on local-vs-CI disagreement (local state can
  lag CI's); it alerts on CI-side signals: POLL_ERROR, a failed run, or
  no finished run in 12h. Cancelled runs are skipped.
- The batch PR reads current_pr from the bot-state branch when it
  exists; the conflict alert is OPEN-only and defers to the poller's
  rebuild; a CLOSED-unmerged PR alerts; MERGED doesn't.
- Live checks run in full without a digest; the offline path makes only
  the git status call (tested).
- refresh.py finds status.py under python -I.
@Deicyde

Deicyde commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner Author

Review status: merged in c7febd42; follow-ups remain

Update 2026-10-14 (re-checked at 984c4847):

  • Landed in 3a34d9c: the gate is CI-only, with no local poll.py --decide. Runs are filtered to main. Failed run-list, log and bot-state reads now alert. The POLL_ERROR alert no longer blames decide. The conflict alert no longer promises a rebuild for an unsettled PR. Bors merges count as merged.
  • Departure: the inline engine was fixed in place rather than rebuilt on Bot: read-only poller status helper for dashboards #61's behaviour. Bot: read-only poller status helper for dashboards #61 is being closed, so this engine is the one that stays, and the items below are its remaining follow-ups.
  • Still open: manage/status.py:254 skips cancelled runs. Historical cancellations were 30-minute timeouts, so a stuck poller shows the last success, or CI ? with no alert.
  • Still silent: a failed batch PR read (manage/status.py:307) and a missing bot-state branch (no branch exists today, so poller runs fail at restore).
  • Not done: HANDOFF.md:15-16 still lists only site-200 and bot-gate checks. The PR body still says "Nothing to configure" and gives no baseline instructions.
Original review (2026-10-14)

Independent adversarial review of 02799261: a defect reviewer with skeptics, a design reviewer with a devil's advocate, and an arbiter.

Verdict: keep the review-health check, Brain release line and 300 s graph slack; replace the poller and batch-PR code with one manage/status.py implementation built on #61's behaviour, and close #61.

Required before merge

  • Delete the current poller and batch-PR code (manage/status.py:228-310) and its tests. Three confirmed defects live there: the gate note blames state lag for an expired token, the conflict alert promises a rebuild the poller only does for settled PRs, and any POLL_ERROR is reported as a decide failure.
  • Rebuild those checks from Bot: read-only poller status helper for dashboards #61: every failed read becomes an alert and cancelled poller runs count as timeouts. Also no local bot/poll.py --decide, POLL_ERROR attributed to the failing step, runs filtered to main.
  • Update HANDOFF.md:15-16, which still says --live adds "site-200 + bot-gate checks"; it is the only doc describing --live.
  • Correct the PR body: the SessionStart hook runs without --live, and a first --live run only saves a baseline. Until GET /api/runs ships, run --live from one checkout before and after a nightly.

Decisions for the maintainer

Merge order: after #56, since the alerts point at docs/OPERATIONS.md; release-neutral, small HANDOFF.md rebase against #53.

Fixes are in progress; this comment will be updated.

Posted by the WikiLean PR swarm (lead); updated 2026-10-14.

@Deicyde

Deicyde commented Oct 14, 2026

Copy link
Copy Markdown
Owner Author

Status review (2026-10-14): 🔧 Needs changes

The code is read-only, release-neutral and well tested, and it works against live data. In the combined build, review_from_runs() reads #50's GET /api/runs, and the fallback still parses /stats v6. Two of its alerts are wrong in ways that matter, though.

Fix before merge

  • A false alert fires after every batch merge. Mathlib merges through Bors, which closes the PR (state: CLOSED, mergedAt: null, title prefixed [Merged by Bors] - ). manage/status.py:306 therefore reports every successful batch as "closed without merging; the poller waits for a human", and a test locks that in. Reproduced with batch_pr(40682). Use settle.is_merged, or also fetch title and treat a CLOSED PR whose title starts with [Merged by Bors] as merged.
  • The gate note blames the wrong cause (status.py:271). When local and CI decides disagree, it says local state "can lag CI's". Without Make the wikidata poller fail closed #48 both read the same file, so lag can't explain the gap; the likely cause is CI's failing token, and live it prints local act · CI wait with no alert. The first commit alerted on exactly this; the review-fix commit removed it. Alert when there's no bot-state branch, or when the two states are identical.

Nits

  • Failed reads degrade silently. A failed gh run list shows CI ? with no alert. A failed bot-state read falls back to main's snapshot, which is frozen after Make the wikidata poller fail closed #48, with only a "(state: local snapshot)" label.
  • The conflict alert promises a rebuild that may not come. It says the poller rebuilds the PR "on its next act tick", but poll.py only rebuilds settled PRs.
  • POLL_ERROR is matched anywhere in the run log, yet the alert always says "decide failed".

Overlap with #61. #61's bot/gate_status.py implements the same checks with different rules: 12 h vs 24 h staleness, all branches vs main only, log scraping vs step outcomes, and a silent fallback vs an alert. Keep one engine; for example, have status.py call gate_status.py, or close one. Note that gate_status.py:199 has the same Bors blind spot.

Order: after #56, whose docs/OPERATIONS.md these alerts link to.


🤖 Automated status pass by Claude Code over all 18 open PRs (#46–#63), posted for the repo owner. An independent verifier re-checked each finding. All 18 heads were also merged and tested together in a scratch tree; the only test failure that comes from combining them is #47 × #55 (three other pairs had mechanical text conflicts). Reviewed head: 02799261.

…t as merged

- The gate line shows CI's decision only. The local poll.py --decide
  call is gone: it reads this checkout's copy of the bot state, which
  the poller stops updating once state lives on the bot-state branch.
- Poller runs are listed with --branch main.
- A failed gh run list, or a failed read of the run's log, raises an
  alert instead of a bare "CI ?".
- A bot-state read that fails for any reason other than HTTP 404 raises
  an alert and skips the batch PR check rather than falling back to the
  local snapshot; a 404 is labelled "no bot-state branch".
- The POLL_ERROR alert names the run, not decide (any poll.py step can
  print it), and keeps the run's decision when one was printed.
- The conflict alert no longer promises a rebuild on the next act tick:
  the poller rebuilds a batch off master only once it is settled.
- batch_pr reads the title and treats a CLOSED PR titled
  "[Merged by Bors] - " as merged (shown as MERGED (Bors), no alert).
- Tests for each case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Deicyde

Deicyde commented Oct 14, 2026

Copy link
Copy Markdown
Owner Author

Fixes pushed (2026-10-14)

This addresses the findings in both earlier reviews: the "Status review (2026-10-14)" comment (Bors false alert, gate note, all three nits) and the PR swarm's review (its three alert defects, the local decide, the main filter and failed reads).

What changed (3a34d9cb)

  • Bors merges count as merged. batch_pr fetches title; a CLOSED [Merged by Bors] PR shows MERGED (Bors) with no alert.
  • The gate is CI-only. The local poll.py --decide and its "can lag CI's" note are gone. Runs are listed with --branch main.
  • Failed reads alert. A failed gh run list or run-log read raises an alert instead of a bare CI ?. A non-404 bot-state read failure alerts and skips the batch-PR check instead of falling back to the local snapshot; a 404 still uses the snapshot, labelled no bot-state branch.
  • Alert wording. The POLL_ERROR alert names the run instead of decide and keeps any decision the run printed. The conflict alert now says the poller rebuilds only a settled batch.
  • The PR body was updated to match.

Verification

  • manage/test_status.py: 23 passed (Python 3.12 and 3.9).
  • 12 mutations of the new behaviour: each caught by at least one test.
  • Read-only live check: a Bors-merged mathlib PR shows MERGED (Bors), no alert.
  • CI on 3a34d9cb: all checks green.

Not changed / still open

Merge notes: merge after #56 (docs/OPERATIONS.md).


🤖 Posted by Claude Code for the repo owner. Each fix was checked by an independent verifier before it was pushed. Head: 3a34d9cb.

@Deicyde
Deicyde merged commit c7febd4 into main Oct 14, 2026
4 checks passed
@Deicyde
Deicyde deleted the claude/status-health-alerts branch October 14, 2026 21:13
Deicyde added a commit that referenced this pull request Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant