testsAndMisc/docs/superpowers/evidence/usage-report-render-split-2026-08-17.json
Krzysztof kuhy Rudnicki ad646541ad
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
usage_report: split _usage_report_render.py 310 -> 181 + 123 + 24
Verified by the 100%-coverage tests from the previous commit, written
before this seam existed.

_cpu_table/_dedupe_ram/_ram_table/_gpu_table move to
_usage_report_tables.py with the two constants they own. _md_escape and
_fmt_h move to _usage_report_format.py: both the tables module and the
render module need them, so a third module is what avoids a cycle.
Import sites name the owning module; no re-export barrel.

usage_report.py needed no edit - it imports _fmt_h and _render_report
from the render module, and both names still resolve there.

Evidence the move was verbatim: an AST comparison of all 10 functions
against HEAD reports 10 identical, 0 mismatches. That matters because I
retyped rather than cut-and-pasted, and did briefly introduce a literal
× where the original had × - ruff's RUF001 caught it.

Coverage stays 100% line+branch on all three (15/55/54 stmts, 4/10/14
branches, 0 missed). Test count 43 before and after, summed across the
five files it now lives in - the check that matters for a test split,
since a file outside the runner's glob is silently never collected.
Directory total 143 passed, unchanged.

Ran the real program: a full report from today's atop + pmon logs, every
section rendering through the new boundaries including the RAM sibling
dedupe and its (+20 more) truncation.

Over-cap 41 -> 39. No suppressions; a shared test-helper module was tried
and abandoned because name-tests-test would have needed an exclusion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-17 11:34:08 +02:00

59 lines
5.8 KiB
JSON

{
"intent": "Split _usage_report_render.py (310 lines, over the 250 cap) into three modules along a functional seam, verified by the 100%-coverage tests that landed in the preceding commit.",
"scope": [
"_usage_report_render.py 310 -> 181 (host profile, methodology, report assembly)",
"New _usage_report_tables.py 123 (CPU/RAM/GPU Markdown tables and the RSS dedupe)",
"New _usage_report_format.py 24 (_md_escape and _fmt_h, shared by the other two)",
"The 43 tests were re-split to mirror the source: test_usage_report_render.py 573 -> 146, plus new tables (216), host (150), gpu_table (56) and format (40) files",
"Non-goal: any behaviour change. usage_report.py's import line is unchanged because both symbols it names stayed in the render module."
],
"changes": [
"Moved _cpu_table, _dedupe_ram, _ram_table and _gpu_table into _usage_report_tables.py, along with the _RAM_BUCKET_MIB and _MAX_SIBLINGS_SHOWN constants they own.",
"Moved _md_escape and _fmt_h into _usage_report_format.py. Both the tables module and the render module need them, so a third module avoids a circular import between the two.",
"Import sites name the owning module directly (from _usage_report_tables import ...); no re-export barrel was added to _usage_report_render.",
"usage_report.py needed no edit at all: it imports _fmt_h and _render_report from _usage_report_render, and _render_report stayed there while _fmt_h is re-imported into that module from the format module under its original name.",
"Split the test files along the same seam so none exceeds the cap, and so each test module sits next to the source module it exercises."
],
"verification": [
{
"command": "python3 scratchpad/verify_split.py - AST comparison of every moved function against HEAD",
"result": "pass",
"evidence": "All 10 functions (_cpu_table, _dedupe_ram, _ram_table, _gpu_table, _md_escape, _fmt_h, _host_profile, _fingerprint_section, _methodology_section, _render_report) report IDENTICAL: 'functions compared: 10, mismatches: 0'. The comparison normalises through ast.parse(ast.unparse(node)), so it is insensitive to formatting but catches any change in logic. This is the direct evidence that the code moved verbatim."
},
{
"command": "pytest linux_configuration/tests/ --cov of the three modules --cov-branch",
"result": "pass",
"evidence": "_usage_report_format.py 15 stmts / 4 branches 100%; _usage_report_render.py 55 / 10 100%; _usage_report_tables.py 54 / 14 100%. Zero missed statements and zero partial branches in all three, matching the single-file 100% established in the previous commit."
},
{
"command": "pytest --collect-only per test file, summed",
"result": "pass",
"evidence": "format 10 + gpu_table 5 + host 6 + render 6 + tables 16 = 43, exactly the pre-split count. This is the discriminating check for a test-file split: a new file whose name fell outside the runner's glob would be silently uncollected and the suite would still pass green. The whole directory reports 143 passed, unchanged."
},
{
"command": "python usage_report.py --date <today> --top 5 --no-clipboard --no-update-state --quiet",
"result": "pass",
"evidence": "Generated a real report from this machine's atop and pmon logs (28 atop samples, 9113 pmon samples). Every section rendered through the new module boundaries: host fingerprint with CPU model and GPU, methodology, CPU table, RAM table including the sibling dedupe and its '(+20 more)' truncation, and the GPU table. Running the program is the check that the import rewiring actually resolves at runtime, which no unit test proves."
},
{
"command": "bash ~/utils/scripts/check_file_length.sh --all",
"result": "pass",
"evidence": "41 over-cap files before this commit, 39 after. _usage_report_render.py (310) and the 573-line test file are both off the list; none of the five resulting source/test files exceeds 250."
},
{
"command": "pre-commit run --files <all 8 changed files>",
"result": "pass",
"evidence": "Every hook passes, including ruff, ruff format, mypy, pylint, bandit, name-tests-test and the two noqa blockers. grep for 'noqa|type: ignore' across all eight files returns 0."
}
],
"risks": [
"Three modules where there was one means three import headers to keep in sync if the seam moves again. The format module is deliberately tiny (24 lines) rather than folded into types, because folding it there would make the tables module import a types module for formatting helpers that have nothing to do with the dataclasses.",
"While rewriting the render module I initially typed a literal 'x' multiplication sign where the original used the \\u00d7 escape. ruff's RUF001 caught it and the escape was restored, so the rendered output is byte-identical - but it is a reminder that retyping a file is riskier than moving it, which is why the AST comparison above exists.",
"A shared test-helper module was tried first and abandoned: name-tests-test rejects any .py under tests/ not named test_*.py, and working around it would have meant a hook exclusion. The two helpers are now defined in the one test file that needs them, with a single inlined ProcAgg construction in another - no duplication worth deduplicating, and no config suppression."
],
"rollback": [
"git revert the commit. _usage_report_render.py returns to 310 lines with all ten functions, the two new source modules and four new test files disappear, and test_usage_report_render.py returns to its 573-line single-file form.",
"After rollback, confirm pytest linux_configuration/tests still reports 143 passed and that _usage_report_render.py is back at 100% coverage as a single module. usage_report.py needs no attention either way, since this commit never touched it."
]
}