From 2c9753dfe1eb71ba0ec36db34dfa512150f1e860 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 20:48:18 +0000 Subject: [PATCH] record: hygiene-coverage-drift shipped (PyAutoBrain#197) Advances the prompt active/ -> complete/2026/08/ and removes the active.md entry. Both PRs merged: PyAutoBrain#200 (5cb1c73), PyAutoMind#132 (c0576aa). The record keeps the traps worth re-reading: category alone is the wrong key for deps/docs (PyAutoNerves is an organ that ships a distribution, so a category mapping re-creates the bug); the drift check's leg A guards the PyYAML-free reader, not the manifest content, because both sides read the same file; and the unscanned signal belongs only to the repo-array modes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SzmzZidPBRqQZjEEw1d6ET --- active.md | 23 ---- ...giene_under_reports_debt_by_25x_because.md | 14 --- complete/2026/08/hygiene-coverage-drift.md | 112 ++++++++++++++++++ complete/index.md | 3 +- 4 files changed, 114 insertions(+), 38 deletions(-) delete mode 100644 active/hygiene_under_reports_debt_by_25x_because.md create mode 100644 complete/2026/08/hygiene-coverage-drift.md diff --git a/active.md b/active.md index cfc31d94..3b4dcb22 100644 --- a/active.md +++ b/active.md @@ -74,26 +74,3 @@ - repo-unclaimed: PyAutoReduce is the only affected repo, listed on this single line deliberately and NOT as a 2-space ` - PyAutoReduce` sub-bullet, because worktree_check_conflict reads any such bullet as a live claim — this task holds no claim and must not block other PyAutoReduce work. - ordering: `draft/research/pyautoreduce/acceptance_noise_rebaseline.md` must run AFTER this lands — a bits change moves the IVM weights and therefore the noise maps, so its parity numbers would need redoing otherwise. Related but distinct, do not fold in: #61 (driz_cr flux erosion / LACosmic) and #62 (tier-1 ePSF from the CR-rejected mosaic). - prompt: active/hst_dq_bits_dial.md - -## hygiene-coverage-drift -- issue: https://github.com/PyAutoLabs/PyAutoBrain/issues/197 -- status: library-dev — IMPLEMENTED AND PUSHED to `claude/hygiene-coverage-drift-kso7h1` (PyAutoBrain cb20ecf, PyAutoMind ea0ac81). NO PR opened (not requested). Cloud session: no worktree, no `gh` CLI; issue filed via the GitHub MCP surface. -- verified: PyAutoBrain suite 212 passed (39 pre-existing hygiene tests unchanged + 8 new); PyAutoMind suite 88 passed (9 new). `repos_sync.py --check` gains one passing line and nothing else — diffed against a stashed baseline to prove the 6 tenant-firewall mismatches it still reports are PRE-EXISTING PyAutoHands drift, not ours. -- measured effect (17 repos scanned, was 9): crlf 5 -> 167 cosmetic .py; deps 5 -> 8 pyproject.toml; docs 3 -> 4 repos with docs/api. -- MUTATION-TESTED, not just green: re-hardcoding LIB_REPOS to the stale five makes 2 of the new tests fail; a deliberately narrowed fallback regex makes the coverage check report 4 dropped repos; a re-hardcoded array trips leg B. Each leg was driven with failing input before being trusted. -- LEG A IS NARROWER THAN IT LOOKS, do not oversell it: the conductor reads the same repos.yaml the check does, so a manifest edit moves both sides together and they CANNOT desynchronise. Leg A really guards the READER — specifically the PyYAML-free fallback, used only where PyYAML is absent and verified nowhere else. Both readers are run via a `--parser` flag added for exactly that purpose. -- firewall entry for hygiene.sh DELETED rather than updated: the conductor now names no instance fact at all (verified by running the token scan over both files). Re-adding an entry would re-permit the drift the new check exists to catch. -- SCOPE CORRECTION made mid-implementation: the first cut marked EVERY mode `unscanned` on an empty root, which broke 20 tests and was wrong — docstrings/refs/optdeps/extras/config discover their own targets by walking the root and can legitimately find material the body map never names. `unscanned` is now scoped to the six repo-array modes (tidy/crlf/artifacts/deps/docs/packaging). A real finding still leads the recommendation, with the partial-ranking caveat appended. -- ALSO REVERTED mid-implementation: filtering `packaging` to repos with a pyproject.toml. It narrowed detection semantics for no benefit and broke a valid existing test; the existing ignore/untracked/depth guards already establish a hit. Left as a comment in the source so it is not re-attempted. -- ADJACENT DEFECT SPOTTED, deliberately NOT fixed (separate task): `HYGIENE_PERF_LIBS` still defaults to `autoconf ...`, the pre-rename name of the config package, so perf silently reports n/a for it. Same silent-under-report family, different defect — not repo-array coverage. -- follow-up owed: the widened scan exposes a real backlog (167 cosmetic CRLF, 122 in the CTI library which has an LF-only rule; 41 dep caps across 8 manifests). Triage is the separate task the prompt asked for and has NOT been filed yet. -- what it is: the hygiene conductor hardcodes `LIB_REPOS`/`ORG_REPOS`/`DOC_REPOS` in `agents/conductors/hygiene/hygiene.sh:64-66`. repos.yaml declares 6 libraries and 7 organs, so the conductor skips PyAutoCTI + PyAutoReduce, classes PyAutoNerves as a library where the manifest calls it an organ, and covers 4 of 7 organs. -- reproduced before planning (PYAUTO_ROOT=/home/user): `hygiene crlf` prints `5 library .py w/ CRLF`; `git -C PyAutoCTI grep -Il $'\r$' -- '*.py' | wc -l` = 122, so the true count for the current repo set is 127. deps audits 5 pyproject.toml of 6; tidy scans 9 of ~17 managed checkouts. -- why repos_sync can't catch it: the tenant-firewall entry for hygiene.sh (`scripts/repos_sync.py:525`) pins the drifted set as an ALLOWLIST, so the stale names are permitted rather than checked for coverage. The fix adds a real coverage check beside it. -- DESIGN TRAP, do not "simplify" it away: PyAutoNerves is `category: organ` but ships a pyproject.toml. A straight category mapping would move it out of LIB_REPOS and thereby DROP it from deps/packaging — trading one coverage hole for another. Those two modes key off "repo ships a pyproject.toml", not off the category. Same reasoning makes `docs` key off `docs/api/` presence (which adds PyAutoCTI). -- human decisions 2026-08-05: (1) derive the workspace set from `category: workspace` too, accepting +40 cosmetic CRLF from autocti_workspace (127 -> 167) so no repo name is left hardcoded; (2) deps/packaging cover any managed repo with a pyproject.toml. -- scope: COVERAGE REPAIR ONLY. Widening surfaces a large backlog of genuine findings (CRLF, artifacts, dep caps) — triaging that backlog is a separate task and no finding is fixed here. -- third defect folded in per the prompt: an empty/absent scan root currently reports `clean` across every array-driven mode with no warning. Adds an `unscanned` status + a banner naming the root, covering both "no repos present" and "body map unresolvable". -- prompt: active/hygiene_under_reports_debt_by_25x_because.md -- worktree: (none — cloud session, working in the canonical /home/user checkouts on the mandated branch) -- repos-claimed-on-one-line: PyAutoBrain (primary) and PyAutoMind, named here deliberately and NOT as 2-space ` - Repo` bullets, because worktree_check_conflict reads any such bullet as a live claim. diff --git a/active/hygiene_under_reports_debt_by_25x_because.md b/active/hygiene_under_reports_debt_by_25x_because.md deleted file mode 100644 index d2977a97..00000000 --- a/active/hygiene_under_reports_debt_by_25x_because.md +++ /dev/null @@ -1,14 +0,0 @@ -# Hygiene under-reports debt by 25x because its repo arrays skip - -Type: bug -Target: PyAutoBrain -Repos: -- PyAutoBrain -Difficulty: medium -Autonomy: supervised -Priority: normal -Status: formalised - -Hygiene under-reports debt by 25x because its repo arrays skip two libraries. The hygiene conductor scans a hardcoded list of checkouts in PyAutoBrain agents/conductors/hygiene/hygiene.sh. That list is stale: the LIB_REPOS array holds five entries where the body map (repos.yaml) has six, silently skipping the CTI and Reduce libraries, and it mislabels the config layer as a library; ORG_REPOS covers four of seven organs. The result is wrong output, not stale prose. On a real run the crlf mode printed '5 library .py w/ CRLF' when the true count is 127 — 122 of them in the skipped CTI library, breaking that repo's LF-only rule with nobody watching. The deps mode audits five pyproject.toml instead of six; tidy inspects nine of roughly seventeen managed checkouts. Every clean bill of health the conductor has issued understates reality. This is an internal inconsistency, since the sibling scanners _hygiene_config.py and _hygiene_refs.py already reach the CTI library. The drift checker cannot catch the gap: its tenant-firewall entry for hygiene.sh pins the current broken set as an allowlist instead of verifying coverage. The repair should derive the arrays from the body map rather than re-hardcoding them. Widening coverage will surface a large backlog of genuine new findings, so land the coverage repair and the triage of what it uncovers as separate tasks. - - diff --git a/complete/2026/08/hygiene-coverage-drift.md b/complete/2026/08/hygiene-coverage-drift.md new file mode 100644 index 00000000..bbfdd798 --- /dev/null +++ b/complete/2026/08/hygiene-coverage-drift.md @@ -0,0 +1,112 @@ +**Shipped 2026-08-05.** The hygiene conductor scanned a hardcoded list of repos +that had drifted from the body map, so it under-reported debt and reported +`clean` over repos it never opened. The sets are now derived from `repos.yaml`, +and a coverage check keeps them that way. + +## PRs + +- PyAutoBrain#200 — `fix(hygiene): derive the conductor's repo sets from the body map` (squashed `5cb1c73`) +- PyAutoMind#132 — `feat(repos_sync): check the hygiene conductor's repo coverage` (squashed `c0576aa`) +- Issue: PyAutoBrain#197 + +## What was wrong + +`hygiene.sh` held `LIB_REPOS` (5), `ORG_REPOS` (4) and `DOC_REPOS` (3) as bash +arrays. The body map declares **6 libraries and 7 organs**, so PyAutoCTI and +PyAutoReduce were never scanned, PyAutoNerves was treated as a library where the +map calls it an organ, and 3 organs were uncovered. + +The failure mode is the important part: **an unscanned repo produces no +findings, so the conductor reported `clean` and was believed.** Measured before +planning — `crlf` printed `5` `.py` with CRLF against a true `127`, 122 of them +in PyAutoCTI, which has an LF-only rule nobody was enforcing. + +## Measured effect (17 repos scanned, was 9) + +| mode | before | after | +|---|---|---| +| `crlf` cosmetic `.py` | 5 | 167 | +| `deps` manifests audited | 5 | 8 | +| `docs` repos | 3 | 4 | + +## Traps and findings + +- **Category alone is the wrong key, and getting this wrong re-creates the bug.** + PyAutoNerves is `category: organ` yet ships a distribution, so mapping `deps` + to `category: library` would have *dropped* it. `deps`/`docs` therefore key off + what a checkout **contains** (`pyproject.toml`, `docs/api/`), not its category. + This also picked up PyAutoCTI's docs, which the hardcoded triple never saw. +- **Leg A of the drift check is narrower than it looks.** The conductor reads the + same `repos.yaml` the check does, so a manifest edit moves both sides together + and they can *never* desynchronise — deriving is the whole point. Found by + trying to make leg A fail and watching it pass. What it actually guards is the + **reader**, specifically the PyYAML-free fallback used only where PyYAML is + absent and verified nowhere else; a `--parser` flag was added so both readers + run. A deliberately narrowed fallback regex made it report 4 dropped repos. +- **The tenant firewall could not have caught this.** Its entry for `hygiene.sh` + *allowlisted* the stale names — it asked "are these names permitted here?" when + the question was "does this cover the organism?". The entry is now **deleted**, + not updated: the conductor names no instance fact at all, so re-adding one + would re-permit the drift. +- **`unscanned` vs `clean`.** An empty scan root, or an unreachable body map, + made every repo-array mode report `clean`. A zero from "nothing was examined" + and a zero from "nothing was wrong" are indistinguishable to a reader, and only + one is good news. +- **Scope the `unscanned` signal to the repo-array modes only.** The first cut + applied it to every mode and broke 20 tests. `docstrings`/`refs`/`optdeps`/ + `extras`/`config` discover their own targets by walking the scan root and can + legitimately find material the body map never names — suppressing them hides + real findings. A real finding still leads the recommendation, with a + partial-ranking caveat. +- **Do not filter `packaging` to repos with a `pyproject.toml`.** Tried, reverted: + it narrowed detection for no benefit and broke a valid existing test. The + existing ignore/untracked/depth guards already establish a hit. A comment in + the source records this so it is not re-attempted. +- **Tests must not name repos.** They derive the expected sets from the body map — + a test that hardcodes the list under test can only agree with itself, and a + literal would be an instance fact in an organ test. Mutation-checked: reverting + `LIB_REPOS` to the stale five fails 2 of the new tests. + +## Verification + +- PyAutoBrain 212 passed (3.12 + 3.13, the CI matrix); PyAutoMind 88 passed (3.12). +- Both drift-check legs driven with failing input before being trusted. +- `repos_sync.py --check` gains exactly one line, diffed against a stashed + baseline to prove the tenant-firewall mismatches it still reports are + pre-existing PyAutoHands drift. (A 7th appeared at merged main from + PyAutoBrain#199's `test_health_conductor.py` — also not from this task.) + +## Deliberately left open + +- **The backlog this exposes is unfiled.** 167 cosmetic CRLF (122 in PyAutoCTI), + 41 dep caps across 8 manifests. The prompt scoped triage as a separate task. +- **`HYGIENE_PERF_LIBS` still defaults to `autoconf`**, the pre-rename name of the + config package, so `perf` silently reports `n/a` for it. Same silent-under-report + family, different defect — not repo-array coverage. + +## Environment note + +Cloud session: no worktree and no `gh` CLI, so the issue and both PRs went +through the GitHub MCP surface and work happened in the canonical checkouts on +the mandated branch. The PyAutoMind checkout was **shallow**, which made local +`main` look diverged from `origin/main` by 50 commits with no merge base (and +tripped a stop hook into proposing a 49-commit rebase over the user's own +commits). `git fetch --unshallow` reconnected the history and `main` +fast-forwarded — nothing was rewritten or discarded. + +## Original prompt + +# Hygiene under-reports debt by 25x because its repo arrays skip + +Type: bug +Target: PyAutoBrain +Repos: +- PyAutoBrain +Difficulty: medium +Autonomy: supervised +Priority: normal +Status: formalised + +Hygiene under-reports debt by 25x because its repo arrays skip two libraries. The hygiene conductor scans a hardcoded list of checkouts in PyAutoBrain agents/conductors/hygiene/hygiene.sh. That list is stale: the LIB_REPOS array holds five entries where the body map (repos.yaml) has six, silently skipping the CTI and Reduce libraries, and it mislabels the config layer as a library; ORG_REPOS covers four of seven organs. The result is wrong output, not stale prose. On a real run the crlf mode printed '5 library .py w/ CRLF' when the true count is 127 — 122 of them in the skipped CTI library, breaking that repo's LF-only rule with nobody watching. The deps mode audits five pyproject.toml instead of six; tidy inspects nine of roughly seventeen managed checkouts. Every clean bill of health the conductor has issued understates reality. This is an internal inconsistency, since the sibling scanners _hygiene_config.py and _hygiene_refs.py already reach the CTI library. The drift checker cannot catch the gap: its tenant-firewall entry for hygiene.sh pins the current broken set as an allowlist instead of verifying coverage. The repair should derive the arrays from the body map rather than re-hardcoding them. Widening coverage will surface a large backlog of genuine new findings, so land the coverage repair and the triage of what it uncovers as separate tasks. + + diff --git a/complete/index.md b/complete/index.md index 0693aa29..d3462c8f 100644 --- a/complete/index.md +++ b/complete/index.md @@ -6,7 +6,7 @@ Token-light navigation over the finished-work records (schema: only then grep a dated bucket. Curators: edit the band between the CURATED markers; everything below GENERATED is rebuilt. -892 records across 7 buckets. +893 records across 7 buckets. ## Highlights @@ -25,6 +25,7 @@ _(curate hard-won records here — survives regeneration.)_ - [group-data-preparation-readme](2026/08/group-data-preparation-readme.md) - [health-conductor-stale-verdict](2026/08/health-conductor-stale-verdict.md) - [howto-smoke-all-tutorials](2026/08/howto-smoke-all-tutorials.md) +- [hygiene-coverage-drift](2026/08/hygiene-coverage-drift.md) - [interferometer-start-here-integrate-oom](2026/08/interferometer-start-here-integrate-oom.md) - [intra-family-dep-floors](2026/08/intra-family-dep-floors.md) - [jax-grad-smoke-timeout-budget](2026/08/jax-grad-smoke-timeout-budget.md)