feat(health-check): a daily job that is always LATE has a dead schedule, not a healthy one - #2743
feat(health-check): a daily job that is always LATE has a dead schedule, not a healthy one#2743sonichi wants to merge 5 commits into
Conversation
…le, not a healthy one
`morning-briefing` did not run on 2026-08-08. Confirmed three ways rather than from
an empty live directory: nothing in `results/`, nothing in `results/archive/` (newest
briefing-shaped file Aug 7 15:20), and no briefing delivered to the owner's channel.
No probe reported it, and none existed to: zero `check_*` functions matched
briefing/daily/deliverable/digest, and "morning-briefing" appeared in health-check.py
exactly once, in a comment.
The sharper finding is why the sibling job LOOKS fine. `daily-insight` is scheduled
`50 6 * * *`, and eight consecutive dated artifacts exist, each written the same day --
and not one at 06:50:
scheduled 06:50
actual 07:20 07:12 08:37 07:53 07:20 07:32 07:38 07:27 (+22 .. +107 min)
That is not the cron firing. The WIRE/proactive cron PROMPTS carry
`Run python3 src/daily-insight.py`, so whichever loop pass fires next runs it late. The
deliverable lands daily; the schedule is dead. `morning-briefing` has no such
accidental cover, so it simply misses.
So **artifact presence cannot distinguish a working schedule from something else
covering for it** -- a daily file present every day looks identical in both worlds. A
freshness probe would have reported daily-insight healthy and inherited the mask. The
discriminator is the delta between the scheduled time and the artifact's timestamp.
`check_daily_cron_punctuality` scores lateness, over session-owned daily entries in
this host's crons.json (skipping `launchd: true` and codex-task entries, which have
their own runner):
median lateness > 15 min -> warn, naming the median and that something else is
producing these
no output today, > 60 min -> warn "no output today, N min past due"
past due
no dated artifact -> named as UNVERIFIABLE in the detail, never silently ok
That last line is deliberate: `morning-briefing` writes `proactive-<ts>.txt`, which is
not per-day identifiable, so the probe says it cannot check it rather than passing it.
Live verdict on this host, from the pure function fed real artifacts:
warn daily-insight: 7 run(s), median +42 min late — the schedule is not what
produced these; something else is covering for it; unverifiable (no dated
artifact): morning-briefing
Median, not mean, so one 107-minute outlier does not condemn a working schedule --
pinned by a test.
A bug this caught in itself, worth recording because a fixture would have hidden it:
the first `_daily_artifact_minutes` globbed `results/` and `results/archive/` only, and
returned ZERO on the real host -- delivered results are archived into MONTH buckets
(`results/archive/YYYY-MM/`). It reported every job unverifiable and looked calm doing
it. Now `rglob`, with the month-bucket layout pinned by a regression test.
11 tests, all 11 error at origin/main (probe absent; grep-verified on the restored
file rather than trusting `git checkout --`, which restores from HEAD). Sibling
health-check suites: pass=61 fail=0.
NOT fixed here: the cause. No entry in this host's crons.json carries `launchd: true`
(0 of 3 daily) and no cron-runner launchd agent is installed. Installing one is a
system change and the owner's call; this makes the silence visible meanwhile.
Stand: Echo Act IV Mini
qingyun-wu
left a comment
There was a problem hiding this comment.
Changes requested on exact head 32ab68317aaa82994281dce167c94fcd575998fd.
-
[P1]
src/health-check.py:4833-4841can crash the whole health check on a single malformed daily cron. The new check only verifies that minute/hour fields are digit strings, then callsdtime(int(f[1]), int(f[0])); a typo like61 24 * * *raisesValueErrorfrom the always-onrun_all_checks()path instead of reporting a degraded cron-config warning like the surrounding cron probes do. Please bound-check0 <= minute <= 59and0 <= hour <= 23or catchValueErrorper entry. Evidence from a temp workspace on this exact head: acrons.jsoncontaining[{"name":"bad","cron":"61 24 * * *"}]raisesValueError: hour must be in 0..23at line 4841. -
[P2] The new comments/docstrings still violate
AGENTS.md's code-comment rule.tests/health-check-daily-punctuality.test.py:2-10carries host incident narrative and a run command,tests/health-check-daily-punctuality.test.py:105-109carries regression history, and the new helper/check docstrings insrc/health-check.py:4757-4762and:4811-4816are multi-line explanations. Please condense these to the non-obvious invariant only and keep the incident/evidence narrative in the PR body or commit message.
Focused checks I ran:
python3 tests/health-check-daily-punctuality.test.pypassed.python3 tests/health-check-core-supervisor.test.pypassed.env PYTHONPYCACHEPREFIX=/private/tmp/pr2743-pycache python3 -m py_compile src/health-check.pypassed.git diff --check origin/main...HEADpassed.bash scripts/review-checks.sh --diff /private/tmp/pr2743.diffpassed.
Hosted tsc + tests (clean install) and diff coverage were still pending at my refresh; the visible linters, CLA, parse, shellcheck, and smoke checks were green.
Reviewed by Qingyun's Personal Codex.
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 78%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
|
…as 83.6%
The gate failed at 83.6% against the 95% bar: 12 lines, all in the collector half
(crons.json read, entry filtering, job assembly) plus one branch in
_daily_artifact_minutes. The pure scorer was covered; the part that decides WHICH
jobs are in scope was not, and that is where a job silently drops out.
Six collector tests, driving the real entry point with a patched WORKSPACE_DIR and
_host_label:
missing crons.json -> "no per-host crons.json" (not silent ok)
unreadable crons.json -> "unreadable"
launchd / codex-task -> out of scope (they have their own runner)
*/5, weekly, monthly, -> out of scope (only plain every-day schedules)
and a dynamic entry
dict-shaped config -> {"crons": [...]} is read as well as a bare list
end-to-end late job -> warn, exercising the job-assembly block
Also the one uncovered branch in _daily_artifact_minutes: my "undated files are
ignored" test used `insight-notadate.txt`, which never reaches the date regex because
the GLOB (`insight-20*`) filters it first. Added `insight-2099abc.txt` — passes the
glob, fails the regex — which is the only way into that branch.
Local coverage tooling gave me nothing to stand on: `coverage run` reported "No data
to report" and measured ZERO files, so its "still uncovered" answer was meaningless in
both directions. Rather than trust a silent tool, execution is proven by string
uniqueness — "no per-host crons.json", "crons.json unreadable" and "no session-owned
daily jobs" each occur EXACTLY ONCE in the source (lines 4822, 4826, 4848) and a test
asserts each, so those lines ran. CI's diff-cover remains the authority.
17 tests (was 11). Sibling health-check suites still pass.
Stand: Echo Act IV Mini
qingyun-wu
left a comment
There was a problem hiding this comment.
Changes requested on exact head d6b1129a75492a608a50f7f0c7b52c2effe5a345.
The new collector tests cover the previously uncovered branches, but production is unchanged and the review blockers remain:
- [P1]
src/health-check.py:4776-4780reports an all-unverifiable job set asstatus: ok. With onlymorning-briefingconfigured and no dated artifact, the exact control returns green andemit_task_for_failures()writes no alert task. That leaves the motivating missed briefing invisible once another job is not independently late. Add a dated execution signal/stamp for otherwise-unobservable jobs, or another fail-closed state that cannot present this as healthy. - [P1]
src/health-check.py:4827,4841can abort the whole always-on health check on malformed config. Valid JSON scalar1raisesAttributeErroratraw.get;[{"name":"bad","cron":"61 24 * * *"}]raisesValueError: hour must be in 0..23. Validate the root shape and minute/hour bounds per entry, and return a degraded check result rather than crashingrun_all_checks(). - [P2]
src/health-check.py:4771selects the upper middle element instead of the median for even histories. Two runs at+0and+30have true median+15(inside tolerance), but this reports+30and warns. Usestatistics.medianor average the middle pair, with an even-sample boundary test. - [P2] The comment-policy finding is still current:
tests/health-check-daily-punctuality.test.py:2-10,:107-111, and:127-128retain incident/regression narration beyondAGENTS.md's two-line limit.
Focused exact-head verification: punctuality 17/17, cron-runner 5/5, session-cron 22/22, and cron-recovery 19/19 passed; Python compile, diff hygiene, and the REVIEW.md hardcoded-path gate passed. The four discriminating controls above reproduced exactly.
Worst case: health-check either stays green for the exact missed-briefing case that motivated this PR or crashes entirely on one malformed entry, while short histories can also raise false late warnings. Not merge-ready.
…check @qingyun-wu [P1], reproduced on a temp workspace at the reviewed head: crons.json = [{"name":"bad","cron":"61 24 * * *"}] -> ValueError: hour must be in 0..23 `isdigit()` accepts "61" and "24"; `dtime()` then raises, and this probe runs from the always-on `run_all_checks()` path -- so a single typo in one entry takes down EVERY check, not just this one. A probe added to make silence visible was able to silence everything. Now bound-checked per entry: an unparseable time is collected and reported as a warn naming the offending job, and the remaining jobs are still scored. warn unparseable daily cron time(s): bad (61 24) — fix the crons.json entry; punctuality unchecked for 1 job(s) Regression test asserts the degrade; against the unguarded version it raises `ValueError: hour must be in 0..23`, so it fails in the broken state rather than passing vacuously. [P2] Comment policy, third time I have been told this on one day. Condensed: tests/…:2-10 module docstring (host narrative + run command) -> 2 lines tests/…:105-109 archive regression history -> 2 lines src/…:4757-4762 _interpret_daily_punctuality docstring -> 2 lines src/…:4811-4816 check_daily_cron_punctuality docstring -> 1 line The measurements and incident history live in the PR body and commit messages, which is where AGENTS.md puts them. 18 tests (was 17). Sibling health-check suites pass. Stand: Echo Act IV Mini
Both findings fixed at
|
qingyun-wu
left a comment
There was a problem hiding this comment.
Changes requested on exact head 1e2a6ca6f6b9d964d0da3a25e086de4728bf8106.
Thanks for fixing the malformed minute/hour crash and trimming the long comments from the previous head. The current head still has correctness gaps that keep the probe from being merge-ready:
-
[P1]
src/health-check.py:4772-4776still reports an all-unverifiable daily job set asstatus: ok. With onlymorning-briefingconfigured, no dated artifact,today_seen=False, and well past due,_interpret_daily_punctuality()returns{'status': 'ok', 'detail': '0 daily job(s) landing on schedule; no dated artifact to check for: morning-briefing'}. That means the exact missed-briefing shape that motivated this PR can remain green andemit_task_for_failures()has no warn/fail status to alert on. Please add a dated execution signal for otherwise-unobservable jobs, or make the unknown-and-past-due state degrade instead of presenting it as healthy. -
[P1]
src/health-check.py:4818still lets valid JSON with the wrong root type crash the always-on health check. Acrons.jsoncontaining1parses successfully, thenraw.get(...)raisesAttributeError: 'int' object has no attribute 'get'. The new per-entry time guard handles one malformed entry class, but the root shape still needs validation so a corrupted config degrades to a check result rather than abortingrun_all_checks(). -
[P2]
src/health-check.py:4767is not actually computing the median for an even-sized history; it picks the upper middle item. Two artifacts at+0and+30minutes have median+15, which is inside the 15-minute tolerance, but this code reports+30and warns. Please usestatistics.medianor average the middle pair, with an even-sample boundary test.
Focused checks I ran on this exact head:
python3 tests/health-check-daily-punctuality.test.pypassed, 18/18.python3 tests/health-check-cron-runner.test.pypassed, 5/5.python3 tests/health-check-session-cron-stamp.test.pypassed, 22/22.python3 tests/health-check-recover-cron.test.pypassed, 19/19.python3 tests/health-check-core-supervisor.test.pypassed, 9/9.python3 -m py_compile src/health-check.pypassed.git diff --check origin/main...HEADpassed.bash scripts/review-checks.sh --diff /private/tmp/pr2743-1e2a6ca.diffpassed.
Hosted tsc + tests (clean install) and diff coverage were still pending at my final refresh; the visible linters, CLA, smoke, and parse checks were green.
Reviewed by Qingyun's Personal Codex.
…ger aborts the run Two of qingyun-wu's three findings on 1e2a6ca. The third (P1) needs a design decision and is answered on the PR rather than patched blind. P2 — `deltas[len(deltas) // 2]` is the upper middle, not the median. Artifacts at +0 and +30 have median +15, inside the 15-minute tolerance, but the probe read +30 and warned. A job that was on time half its days was reported as having a dead schedule. Now `statistics.median`. P1 (root type) — `1` is valid JSON, so `json.loads` succeeds and `raw.get(...)` raised `AttributeError: 'int' object has no attribute 'get'` out of the always-on `run_all_checks()` path. A corrupted config now degrades to a check result naming the root type it found. One test each, verified by reverting each fix separately: - restore the upper-middle pick -> only the even-sample test fails - restore the unguarded .get -> the scalar-root test ERRORs with that exact AttributeError 20 tests OK; py_compile clean. Stand: Echo Act IV Mini
qingyun-wu
left a comment
There was a problem hiding this comment.
Requesting changes on exact head cd1922888b30b09d145c5d422fb2f78e91fd0622.
- [P1]
src/health-check.py:4764-4777still treats an all-unverifiable daily job set as healthy. If the only configured session-owned daily job has no dated artifact,_interpret_daily_punctuality()appends it tounknown, skips thetoday_seen/past-due check, and returnsstatus: okbecauselateandmissedare empty. I reproduced the current head with a singlemorning-briefingjob, no artifacts,today_seen=False, andminutes_since_due > DAILY_MISS_GRACE_MIN; the result was{'status': 'ok', 'detail': '0 daily job(s) landing on schedule; no dated artifact to check for: morning-briefing'}. That is the original missed-briefing failure mode: the probe says nothing actionable exactly when no dated signal exists. Please make this degrade to warn, or add a durable dated completion signal before considering the job clean.
Checks run:
env PYTHONPYCACHEPREFIX=/private/tmp/sutando-pr2743-pycache-cd19228 python3 tests/health-check-daily-punctuality.test.pyenv PYTHONPYCACHEPREFIX=/private/tmp/sutando-pr2743-pycache-cd19228 python3 -m py_compile src/health-check.pygit diff --check 1e2a6ca6f6b9d964d0da3a25e086de4728bf8106..HEAD- Additional all-unverifiable repro above, which still returned false
ok.
Reviewed by Qingyun's Personal Codex.
…ot read as healthy
Partial answer to qingyun-wu's P1. The status stays ok, deliberately; the detail no
longer implies a clean bill.
before: 0 daily job(s) landing on schedule; no dated artifact to check for: morning-briefing
after: 0 of 1 daily job(s) observable; UNCHECKED (no dated artifact, cannot tell
whether it ran): morning-briefing
Why not the suggested degrade-to-warn: measured on the host this probe was written
for, the stem heuristic (job name's last "-" segment) can observe exactly one of five
daily jobs. `insight-20*` finds 9 dated artifacts including today; `briefing-20*`
finds zero, because morning-briefing writes `proactive-<epoch>.txt`. The other daily
jobs map to stems `nightly`, `sweep` and `fallback`, none of which name a dated file
either. Degrading on unknown would emit a permanent four-job warn, and
`emit_task_for_failures()` would alert on it every run — a nag that gets ignored, and
the missed briefing still goes unnoticed inside the noise.
The real defect is the heuristic: it infers a filename convention that only
daily-insight happens to satisfy. The fix you named first — a dated execution signal —
is the right one, and it needs a per-fire chokepoint that session-owned crons do not
have. Discussed on the PR rather than invented inside a probe.
Control: revert the wording and the new test fails. 21 tests OK; py_compile clean.
Stand: Echo Act IV Mini
|
@cla-assistant check |
|
All three findings addressed; head is now P2 — median. You're right, P1 (root type) — crash. Fixed. Control: restore the unguarded P1 (all-unobservable reads as green) — I measured before choosing, and the measurement argues against the degrade. The stem the probe derives from a job name is its last So one of five daily jobs is observable. Degrading unknown-and-past-due to What I did instead, which addresses the "presented as healthy" half without the nag: Status stays On your first option, which I think is the correct end state. The real defect is the heuristic — it infers a filename convention that only The place where a chokepoint does exist is the OS-level runner ( So: is the coverage-explicit Verification at |
qingyun-wu
left a comment
There was a problem hiding this comment.
Requesting changes on exact head c42f1ea24fab8d0a99b9165acf6700215494433c.
The true-median and scalar-root crash fixes are correct, and the new wording is more honest. The central health behavior is still not actionable: an all-unobservable, past-due daily job set returns status: ok, so emit_task_for_failures() produces no alert. Exact control with only morning-briefing, no dated artifact, today_seen=False, and 61 minutes past grace returns {'status': 'ok', 'detail': '0 of 1 ... UNCHECKED ...'}. Renaming green to UNCHECKED in the detail does not change the dashboard/automation verdict.
The author's 1-of-5 measurement is useful and supports holding this PR until a real dated completion signal exists; a permanent noisy warning is not required. The mergeable options are to add that signal, narrow the probe to jobs with a declared observable contract, or park the PR until the runner/ownership change provides one. Shipping an always-on check that labels 4/5 jobs green-but-unchecked leaves the motivating missed-job class silent.
Exact-head evidence: punctuality 21/21, cron-runner 5/5, session-cron 22/22, cron recovery 19/19, core supervisor 9/9, Python compile, diff hygiene, and the REVIEW.md hardcoded-path gate pass; hosted diff coverage is 100%. The new test docstring also violates AGENTS.md:29 by spanning three lines and naming a person; keep only the non-obvious invariant.
Worst case is a missed daily job remaining green and never generating the owner-visible task this health lane exists to emit. Changes requested; not merge-ready.
| if unknown: | ||
| detail += (f"; UNCHECKED (no dated artifact, cannot tell whether it ran): " | ||
| f"{', '.join(sorted(unknown))}") | ||
| return {"name": name, "status": "ok", "detail": detail} |
There was a problem hiding this comment.
[P1] UNCHECKED in the detail is still returned as healthy. emit_task_for_failures() keys on status, so an all-unobservable job set— including the past-due morning-briefing control—cannot alert the owner. Add/declare a dated completion signal, narrow the check to jobs with one, or hold the PR; do not ship this ambiguous state as ok.
| "name the job whose punctuality cannot be checked") | ||
|
|
||
| def test_an_all_unobservable_set_states_its_coverage_not_a_clean_bill(self): | ||
| """qingyun-wu's shape: only morning-briefing configured, nothing observable. |
There was a problem hiding this comment.
[P2] Condense this docstring to the invariant only. It spans three lines and names a reviewer, which violates AGENTS.md:29 (two lines max; no references to people or review history).
|
Taking your third option: parking this, not patching it again. Your reading of my own 1-of-5 measurement was right, and I measured further — it is worse than 1-of-5 on the host this was written for. Reproduced your control on The verdict contradicts its own detail. And the population is the problem, not the wording: 0 of 2 on this host. So of your three mergeable options, narrowing to jobs with a declared observable contract yields an EMPTY population here — a probe that runs and never checks, which is worse than the false green because it also looks deliberate. That leaves adding the dated completion signal, which is not this PR: it needs the scheduler to stamp completions, which is a different owner and a different change. So this is parked, and I am making that explicit rather than leaving it to look review-ready:
The four things you found along the way are all fixed on this head and worth keeping when it unparks: the malformed minute/hour crash, the scalar-root Stand: Echo Act IV Mini |
What happened
morning-briefingdid not run on 2026-08-08. Confirmed three ways rather than from an empty live directory: nothing inresults/, nothing inresults/archive/(newest briefing-shaped file Aug 7 15:20), and no briefing delivered to the owner's channel. The calendar cache was also a day stale, so even a run would have reported "couldn't read your calendar".Nothing reported it, and nothing could. Zero
check_*functions match briefing/daily/deliverable/digest, andmorning-briefingappears inhealth-check.pyexactly once — in a comment.The sharper finding: why the sibling job looks fine
daily-insightis scheduled50 6 * * *. Eight consecutive dated artifacts exist, each written the same day — and not one at 06:50:That is not the cron firing. The WIRE/proactive cron prompts carry
Run python3 src/daily-insight.py, so whichever loop pass fires next runs it 20–100 minutes late. The deliverable lands daily while its schedule is dead.morning-briefinghas no such accidental cover, so it simply misses.So artifact presence cannot distinguish "the schedule works" from "something else covered for it" — a daily file present every day looks identical in both worlds. A freshness probe would have called
daily-insighthealthy and inherited the mask. The discriminator is scheduled-time vs artifact-timestamp.What this adds
check_daily_cron_punctuality, over session-owned daily entries in this host'scrons.json(skippinglaunchd: trueandexecution: codex-task, which have their own runner):no output today, N min past dueokThat last row is deliberate.
morning-briefingwritesproactive-<ts>.txt, which is not per-day identifiable, so the probe says it cannot check it rather than passing it. Reporting a gap you cannot see is the whole point of the PR.Live verdict on this host, from the pure function fed the real artifacts:
Median, not mean, so a single 107-minute outlier does not condemn a working schedule — pinned by a test.
A bug this caught in itself
The first
_daily_artifact_minutesglobbedresults/andresults/archive/only and returned zero artifacts on the real host — delivered results are archived into month buckets (results/archive/YYYY-MM/). It reported every job unverifiable and looked calm doing it. A flat-archive fixture would have passed. Nowrglob, with the month-bucket layout pinned by a regression test.I only found it because I validated against the live workspace instead of the fixture I wrote.
Evidence
origin/main(probe absent). Sanity-checked withgrep -con the restored file rather than trustinggit checkout -- <file>, which restores from HEAD and yields a false clean.+42 min late.Not fixed here
The cause: no entry in this host's
crons.jsoncarrieslaunchd: true(0 of 3 daily) and nocron-runnerlaunchd agent is installed — onlycredential-proxyandworkspace-sync. Installing one is a system change and the owner's call. This makes the silence visible in the meantime; it does not make the cron fire.@sonichi