fix(usage): the middle wildcard is a read too — usage_collect + activity_collect (DIVE-3419) - #648
Open
lodar wants to merge 2 commits into
Open
fix(usage): the middle wildcard is a read too — usage_collect + activity_collect (DIVE-3419)#648lodar wants to merge 2 commits into
lodar wants to merge 2 commits into
Conversation
…ity_collect (DIVE-3419) usage_collect was guarded at the TOP (probe_readable on projects/) and the BOTTOM (per-file except OSError) of a three-level path. The middle `*` of `projects/*/*.jsonl` is a directory listing that glob.glob() performs itself and swallows every OSError from, so an unreadable project subdir left the agent in the READABLE set with a FRACTION of its total and coverage.complete still true — and every '⚠ N NOT checked — burn is unknown (not 0)' surface built on that flag stayed quiet on it. Replaced with per-level os.listdir (which may raise); the agent now lands in coverage.unreadable with a named cause, where an unreadable FILE already put it. ENOENT/ENOTDIR skips kept so a rolling session and a stray file in projects/ do not over-fire. activity_collect had no readability contract at ANY level and still resolved home through the guessed /home/agent-<name> fallback DIVE-3345 deleted from the sibling scanner. Decision recorded at the site: it DOES owe a contract, but a REPORTING one, not the fail-closed NOT-REACHED its spend-scanning sibling owes — nothing persists this trail over durable state, it is read by one human next to the trail itself, and refusing to render 40 commands over one mode-000 dir destroys the surface. So it emits the trail and NAMES what is short of it (.partial, printed above the counts). The guessed path is gone: an unresolvable agent now exits non-zero with an empty stdout and a named cause. Grade, uid 1011, unprivileged, same harness both sides: 9/14 pre-fix (78b6a6a), 23/0 after. Headline red: total=30000 against an expected 60000, shipped with coverage.complete=true. Siblings unmoved on the fixed tree: usage_coverage 11/0, usage_dispatch_flag 15/0, usage_enumeration_completeness 9/0, usage_presenter_coverage 26/0, heartbeat_usage_heal 27/0, loop_spend_not_reached 17/0, loop_ceiling_enforce 5/0, task_budget_enforce 41/0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both transcript readers in
cmd_usage.shreadprojects/*/*.jsonl. Found by dev3 while fixing DIVE-3417 (the same shape in_spend_scan_task_ids).usage_collect — guarded top and bottom, of a THREE-level path
probe_readablecoversprojects/; the per-fileexcept OSErrorcovers each*.jsonl. Between them sat a third read nothing covered: the middle*is a directory listing thatglob.glob()performs itself and swallows everyOSErrorfrom, yielding nothing for that entry. An unreadable project subdir therefore left the agent in the READABLE set with a total that is a fraction of the truth andcoverage.completestilltrue— sousage.sh's "cannot read" and "⚠ N NOT checked — burn is unknown (not 0)" surfaces stayed quiet on a short number.5dive usageis what a human reads when deciding whether a seat is burning.Milder than DIVE-3417 (nothing here persists over durable state) but the same defect. No third probe could fix it — there is no exception to catch. Each level is enumerated with
os.listdir, which may raise. The agent now lands incoverage.unreadablewith a named cause, exactly where an unreadable FILE already put it.ENOENTandENOTDIRskips are kept deliberately — both mean "nothing unread here", and re-reporting them as blindness over-fires on every idle agent, which disables coverage as thoroughly as the fail-open did.activity_collect — the decision acceptance 4 asked for, recorded at the site
It had no readability contract at any level (bare
except OSError: continuethroughout) and still resolvedhomethrough the guessed/home/agent-<name>fallback DIVE-3345 deleted from the sibling.Decision: it owes a contract, but a REPORTING one, not the fail-closed NOT-REACHED one
_spend_scan_task_idsowes. That function must emit no number when blind because its caller PERSISTS what it returns over durable state and a short total silently becomes the record. Nothing consumes this trail that way — one human, one agent, read next to the trail itself. Refusing to render 40 commands because one project dir is mode 000 destroys the surface to protect a token count5dive usagereports properly anyway. So: emit the trail, and name what is missing from it (.partial, printed above the counts bycmd_activity). What is refused is the old rendering, where "unreadable" and "did nothing" were the same output. The guessed path is gone — an unresolvable agent exits non-zero, empty stdout, named cause.Widening
globhid dot-prefixed names at both levels. A dot-prefixed session file is still burn; not counting it silently is the shape being refused.Grade —
tests/usage_middle_wildcard_unit.sh, uid 1011, unprivileged, same harness both sides9 passed / 14 failed pre-fix (
78b6a6a) · 23 / 0 after.Headline red, which is dev3's reported shape reproduced exactly:
Arms, built to DIVE-3345/3417's rules: anchor first (a healthy non-zero total on the same path — every refusal arm is vacuous while it is red); paired sick/healed (each sick fixture re-run healed must recompute the real 60000 through the same code, proving the arm read blindness and not emptiness); an ANY-UID arm —
ENOTDIRdoes not work at this level (it is a legitimate skip here), so the vehicle isELOOP, a self-referential symlink where a project dir belongs, which root cannot resolve either, so a uid-0 CI run cannot be a vacuous green; and over-fire controls (never-run agent, stray file inprojects/).Honest caveat on the pre-fix reds: the
activity_collectarms are red pre-fix partly becauseA_HOME_ROOT(the test seam) does not exist there, so those fixtures resolved nowhere. That is itself acceptance 4's defect firing — pre-fixactivity_collectguessed/home/agent-alpha, read nothing, and returned a well-formed empty trail with rc 0 — but it conflates two defects. The unambiguous middle-wildcard reproduction is theusage_collectblock above, which usesUSAGE_HOME_ROOTand exists on both trees.Siblings unmoved on the fixed tree:
usage_coverage11/0 ·usage_dispatch_flag15/0 ·usage_enumeration_completeness9/0 ·usage_presenter_coverage26/0 ·heartbeat_usage_heal27/0 ·loop_spend_not_reached17/0 ·loop_ceiling_enforce5/0 ·task_budget_enforce41/0.Not in scope, named so nobody re-derives it
The shell globs in
cmd_memory.sh/cmd_doctor.shshare the unguarded middle wildcard but fail LOUD ("no memory stores found") rather than producing a number. A skipped level that cannot be mistaken for a measurement is not this defect.5dive-clidoes not auto-deploy (deliberaterelease-cut), so merge is not a ship.🤖 Generated with Claude Code