fix(disk-hygiene): qualify unknown and non-reclaimable byte counts - #1870
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Claude finished @kyle-sexton's task in 5m 14s —— View job Reviewing PR #1870
This repo has a Findings🔴 Important — 🔴 Important — permission-denied subtrees still collapse to 🟡 Suggestion — target's own 🟡 Suggestion — PR title/description scope doesn't match the diff Not reviewed here (by design)Security-relevant aspects (e.g. the Test executionSandbox restrictions in this session blocked running |
|
Claude finished @kyle-sexton's task in 1m 56s —— View job Security Review — PR #1870
Scope reviewed: Findings: none. This diff is confined to reporting/accounting arithmetic in the disk-hygiene engine:
I reviewed No findings to report for this PR. |
Manifest must match the changelog entry; max_depth=2 keeps a walked empty directory distinguishable from a truncated sibling. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Claude finished @kyle-sexton's task in 1m 3s —— View job Security Review — PR #1870 (HEAD
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 056dad90a6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Claude has reviewed this PR 5 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
|
Claude finished @kyle-sexton's task in 2m 38s —— View job Reviewing PR #1870 (HEAD
|
Preview/apply must notice hard-link count changes after scan so reclaimable bytes stay accurate.
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
|
Claude finished @kyle-sexton's task in 1m 41s —— View job Security Review — PR #1870 (HEAD
|
07d6b57 added nlink to same_stat_identity but nothing exercised the scenario that made it necessary, so a later simplification could drop the check silently. The test links a candidate after its scan, then asserts inline that every field the pre-fix comparison used — size, mtime, device, inode, mode — is unchanged, and that only nlink differs. That framing is the proof: it fails against any identity function that omits nlink, and cannot pass by accident. 246 tests pass. Ruff reports the same 13 findings before and after this change (the branch predates the repo-local ruff.toml on main), so it adds none. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Claude finished @kyle-sexton's task in 3m 34s —— View job Reviewing PR #1870 (HEAD
|
|
Claude finished @kyle-sexton's task in 47s —— View job Security Review — PR #1870 (HEAD
|
…s probe reachable (#2016) Three stranded review findings against `disk-hygiene`, all in normative prose. No behavior change to any script; the diff is four files and the plugin manifest bump. ## What was wrong **The confirmation gate applied one acceptance bar to four different questions.** The bar demanded an answer "naming exactly the tier and path list just shown". Two of the four questions it governs have shown neither at the point they are asked — the no-target prompt asks the user to *supply* a directory, and §1's large-scan confirmation is asked *before* the walk that would produce a tier or a path list. Neither could be cleared by any reply a human could give, and one of them gates an unbounded whole-volume walk. The surface rule and the answer floor are genuinely common to all four, so they stay shared; what an answer must *name* is now stated per question in a table. The gate also now states the obligation that produced the defect in the first place: ask each question so it shows what its row requires the answer to name. **The gate fell back to an inline question only when `AskUserQuestion` was *absent*.** Per [permissions](https://code.claude.com/docs/en/permissions) (fetched 2026-08-08), a bare tool name in `permissions.deny` "removes the tool from Claude's context entirely", while `dontAsk` mode denies `AskUserQuestion` "even if you've allowed them" — the tool stays visible and every call fails. Absence and denial are distinct states, and the skill named `dontAsk` as a cause while keying the trigger on the one state it does not produce. An agent that checks its tool list would see the tool, skip the inline branch, call it, be denied, and find no recovery in the text. The fallback now triggers on absent, denied, or otherwise unusable, including a denial discovered only by calling it. **The `python3` alias probe was routed through an interpreter that may be unable to run it.** `setup` step 1(b) launches the bundled inspect-only probe with "any interpreter already proven real" and gates the PowerShell escape hatch on no such interpreter existing. But the probe carries `from __future__ import annotations` (optional release 3.7.0b1) and PEP 604 unions, and CPython raises `SyntaxError` at compile time for a future feature it does not define — so on Python 3.6 the probe dies before emitting any JSON. The same section already names 3.6 as an interpreter that rejects guard-shaped code, then routed an identically-shaped probe through it. A 3.6 interpreter is not absent, so the literal reading never reached PowerShell and the check failed closed with no `detail` to remediate from. The fallback now also covers a chosen interpreter that emits no verdict. ## Scope A fourth finding on this branch (`_discard_stream` re-closing the descriptor it just repaired) was already discharged on `main` before this branch was reopened: the module tail is `os._exit(_exit_code)`, so the exit-status leak the thread reported no longer reproduces regardless of what the fallback does. The commit that had added a fix and a test for it is reverted here rather than carried — the residual descriptor defect is real but maps to no finding this branch owns. The `dontAsk` defect has an exact twin at `plugins/repo-hygiene/skills/clean/SKILL.md:85`, from the same original PR. It is deliberately **not** fixed here: changelog parity is per-plugin, and folding a second plugin in would require its own bump and entry. Reported separately for its own change. ## Verification - Both permissions-doc quotes read verbatim off the live page this session, not from recall. - The 3.7 floor and CPython's compile-time rejection of an undefined future feature were read off a live interpreter (`__future__.annotations.getOptionalRelease()`), not asserted. - `check-changelog-parity.sh` `--check` / `--check-order` / `--check-bump`, `check-changed-skills.sh`, `check-shell-portability.sh`, and `markdownlint-cli2` all pass locally. - `test_hygiene.py` 242 pass, `python3_alias_probe.test.sh` 10 pass — unchanged, as expected for a prose-only diff. - `0.15.0` steps over `0.14.0`, which is claimed by open PR #1870 against the same manifest. ## Related Addresses review threads on #1124, #1738. No linked issue --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two conflicts, both from concurrent version bumps on the same plugin. plugin.json and CHANGELOG: main shipped 0.15.0 while this branch held 0.14.0, and main's entry carried a note reserving 0.14.0 for this PR. Since 0.15.0 landed first, this entry takes 0.16.0 and states that 0.14.0 is never published, so the changelog reflects the order things actually merged rather than the order they were numbered. Both entries are kept in full — they fix unrelated defects. 246 disk-hygiene tests pass after the merge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 11s —— View job Security Review — PR #1870 (HEAD
|
…lution MD012 from composing both version entries by hand during the origin/main merge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 53s —— View job Reviewing PR #1870 (HEAD
|
|
Claude finished @kyle-sexton's task in 45s —— View job Security Review — PR #1870 (HEAD
|
…size rule Review: "not-walked implies logical_size is null" holds for every entry `metadata()` produces, but not for the target's own record — `scan_tree` appends the qualifier while keeping the partial walked sum, so a reader of either surface in isolation would take the invariant as universal. Both surfaces now state the exemption and where it is applied. SKILL.md tells a consumer to read that number as a floor, which is what it is. 246 tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 39s —— View job Reviewing PR #1870 (HEAD
|
|
Claude finished @kyle-sexton's task in 51s —— View job Security Review — PR #1870 (HEAD
|
Summary
Completes finding 2 of #1806 (byte accounting cannot express "unknown" / "not reclaimable local bytes"). Findings 1 and 5 already shipped in 0.13.0 via PR #1820.
logical_size: nullwith anot-walkedqualifier instead of looking empty (0).nlinkand cheapallocated_size(st_blocks * 512on POSIX; null on Windows).st_nlink > 1carryhardlinked; sparse files carrysparsewhen the platform exposes the signal.reclaimable_local_bytes*as a figure distinct from the walked logical roll-up; qualified entries are excluded.Closes #1806
Related
summarizesurface), 4 (Stop-detector marker amortisation), 6 (probe path provenance vs trusted settings), and 7 (run-state retention / snapshot path containment) need design or coupled-grammar calls; left as follow-ups, not in this PR.size_qualifiers/file_attributesmechanism this builds on.Test plan
python -m unittest -v plugins/disk-hygiene/skills/clean/scripts/test_hygiene.py— 245 tests, OK (4 skipped)nlink/allocated_sizeshape, protected sync-rootnot-walked