Skip to content

Commit b3c2a18

Browse files
authored
Merge pull request #286 from PyAutoLabs/claude/test-performance-dashboard-y3fdy7
prompt: the two test-performance follow-ups — issues, lifecycle, records
2 parents ff3b289 + e31c6a6 commit b3c2a18

5 files changed

Lines changed: 187 additions & 16 deletions

File tree

draft/bug/pyautoheart/script_timing_baselines_orphaned_and_window_filled.md renamed to complete/2026/08/script-timing-baselines-fix.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,55 @@
1+
# script_timing baselines made real — run-identity dedup + rename-aware slugs
2+
3+
PyAutoHeart#166`ee915f3`, closing PyAutoHeart#165, merged 2026-08-24 on
4+
branch `claude/test-performance-dashboard-y3fdy7` (restarted from main after
5+
the phase-1 board merge). Phase 0 of the test-performance board arc
6+
(`complete/2026/08/test-performance-board.md`) — the per-script trend surface
7+
reads what this fixes.
8+
9+
## What shipped
10+
11+
- **The "every history is one value repeated 7×" defect was not seeding — it
12+
was re-ingestion.** `run()` re-read `run_logs/latest` on every tick and
13+
re-appended the same observation until the window filled with copies of one
14+
run. History entries now carry `{duration_s, run_id, ts}` (run_id = the
15+
resolved timestamped run dir behind the `latest` symlink); a re-tick on the
16+
same run replaces the newest entry, so windows only grow across distinct
17+
runs. Legacy bare-float histories still read; a legacy window of
18+
all-identical values collapses to the single observation it provably was.
19+
- **Classification floor**: the yellow/red ratio fires only against ≥3
20+
distinct-run samples (`MIN_BASELINE_RUNS`); below that a script counts as
21+
`building_count` instead of being judged against a fake-stable median.
22+
`red/yellow/green_count` keep their exact meaning; `dashboard.py` untouched.
23+
- **Rename-aware slugs, loud orphans**: history files untouched by a scan are
24+
orphans; a new slug with no history adopts an orphan on an unambiguous
25+
(workspace, script-name) match — the #216 restructure scenario now heals
26+
itself (`migrated_count`). Ambiguous/unmatched orphans are reported
27+
(`orphaned_count`), never deleted, never silent.
28+
- Histories written via `atomic_write_json` (was a bare `write_text`).
29+
- Tests 573 → 583; `test_script_timing.py` 6 → 16.
30+
31+
## Key traps / findings
32+
33+
- **Four existing tests had encoded the bug itself** — they re-ran `run()` on
34+
the same results dir as a stand-in for repeated runs, which is exactly the
35+
defect. When a test suite's fixture shape mirrors a bug, the tests pass for
36+
the wrong reason; the fixtures now create one dir per run.
37+
- **Legacy empty run_ids each count as a distinct run** in the floor — history
38+
that predates provenance is real accumulation once the identical-window
39+
collapse has run; treating it as one run would have zeroed every surviving
40+
baseline.
41+
- Orphan reporting is scan-relative: pointing Heart at a partial results dir
42+
surfaces other workspaces' histories as "orphaned" (reporting only —
43+
migration matching is workspace-scoped, so nothing can be misattached).
44+
45+
## Follow-ups
46+
47+
- `unit_test_timing` / `workspace_testmode_timing` keep their own history
48+
mechanics — if they share the re-ingestion pattern, the same dedup applies;
49+
not audited here (out of the prompt's scope).
50+
51+
## Original prompt
52+
153
# Heart script_timing baselines are orphaned by path moves and filled with one repeated value
254

355
Type: bug
@@ -9,6 +61,7 @@ Autonomy: supervised
961
Priority: medium
1062
Status: formalised
1163
Filed: 2026-08-04 (backfilled from git)
64+
Issued: 2026-08-24
1265

1366
Two independent defects in `PyAutoHeart/heart/checks/script_timing.py`, both found
1467
while diagnosing the jax_grad smoke timeouts (PyAutoHands#226). Neither is fixed by
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Per-script smoke timings are a standing dataset — one runner change, ten repos inherit
2+
3+
PyAutoHands#265`d2a22f4` (closing PyAutoHands#264) + PyAutoHeart#167
4+
`3df42b5`, merged 2026-08-24. Phase 2 of the test-performance board arc
5+
(`complete/2026/08/test-performance-board.md`); answers item 4 of
6+
`draft/research/ci/smoke_timing_and_profiling.md` — "should the runner record
7+
per-script timings routinely?" — with **yes, shipped**. The smoke-runner
8+
delegation (#260#263) is what made this one change instead of ten repo
9+
sweeps.
10+
11+
## What shipped
12+
13+
- **`smoke_timings.json`** (schema `smoke_timings/1`) emitted from
14+
`RunReport.write()` — one call site covers `run_python.py`, `run.py` and
15+
`generate.py`, so every delegated gate inherits it with zero per-repo
16+
edits. Per entry: `{entry, kind, status, seconds, cap_s, exit_code}`
17+
`seconds` is the runner's own measured duration (the number the
18+
`[PASS] — <n>s` line prints), TIMEOUT entries carry the cap they hit,
19+
skips are `seconds: null`, never a fabricated 0. One merged file per
20+
report directory, rows keyed on entry path (script + notebook legs both
21+
survive; a re-run replaces its own rows; `legs` records contributors).
22+
- **Step summary**: with `$GITHUB_STEP_SUMMARY` set, a slowest-first timing
23+
table per leg — every gate run's timings one click away, no artifact
24+
download.
25+
- **`ScriptResult` gained `cap_seconds`/`exit_code`** at all 8 execution
26+
sites — deliberately absent from `to_dict()` so the per-run JSONs Heart's
27+
`script_timing`/`test_run` and `aggregate_results` read stay
28+
byte-compatible (pinned by a test); `aggregate_results` skips the sidecar
29+
by name so the mega-run surface stays clean.
30+
- **PyAutoHeart `smoke-tests.yml`** uploads the report dir as
31+
`smoke-timings-<python-version>` (`if: always()`,
32+
`if-no-files-found: ignore`, no `retention-days` — full default artifact
33+
retention). The path is a glob (`test-results/` + `**/smoke_timings.json`)
34+
because the reusable workflow never passes `--report-dir` — each
35+
workspace's `run_smoke.py` does.
36+
- Drive-by fix: `run.py` never passed `env_profile`, so every notebook
37+
report claimed `unknown` — threaded through, negative-tested.
38+
- Tests: PyAutoHands 363 → 382 passed (14 pre-existing environmental
39+
failures unchanged — missing `ipynb-py-convert`/`pngquant` locally,
40+
identical set on main); PyAutoHeart 573 → 576.
41+
42+
## Key traps / findings
43+
44+
- **The notebook report leg is `run.py`, not `run_notebook.py`** — the
45+
latter is the single-notebook kernel-cwd shim `build_util` shells out to
46+
and has no report path. Emit from `RunReport.write()` and every leg is
47+
covered.
48+
- **A fixed per-leg filename would clobber across directories in the
49+
mega-run** — one report dir hosts many `(project, directory, run_type)`
50+
invocations; hence the merged file keyed on entry path. Known limit: two
51+
workspaces sharing a relative path in one mega-run dir collapse to one
52+
row (documented in the docstring; that dir is not a consumer).
53+
- **Keep new fields out of `to_dict()` until every consumer is audited**
54+
the per-run JSON shape is load-bearing for three downstream readers;
55+
extending the dataclass without extending the serialization is the
56+
compatible move, pinned by a byte-compat test.
57+
- `aggregate_results` globs `**/*.json` — any new sidecar in the report dir
58+
becomes a phantom run unless excluded by name.
59+
60+
## Follow-ups
61+
62+
- The Heart board ingesting these artifacts into per-script rows with
63+
STALL/SLOW verdicts (reuse `retime.py`'s vocabulary; bimodality
64+
first-class) once a few weeks of data exist — the deferred phase 3 named
65+
in the board record.
66+
67+
## Original prompt
68+
69+
# Per-script smoke timings as a standing dataset — one runner change, ten repos inherit
70+
71+
Type: feature
72+
Target: pyautohands
73+
Repos:
74+
- @PyAutoHands
75+
- @PyAutoHeart
76+
Difficulty: medium
77+
Autonomy: supervised
78+
Priority: high
79+
Status: formalised
80+
Filed: 2026-08-24
81+
Issued: 2026-08-24
82+
83+
Phase 2 of the test-performance board
84+
([`../../../docs/pyautoheart/test_performance_board_assessment.md`](../docs/pyautoheart/test_performance_board_assessment.md);
85+
phase 1 shipped 2026-08-24 as PyAutoHeart#164 + PyAutoBrain#261). Today the
86+
smoke runner's per-entry timings exist only as `[PASS] <name> — <n>s` lines in
87+
job logs, recovered by hand-scraping; the 2026-08-23 slow-vs-stall audit and
88+
the jax_grad budget work both had to rebuild their datasets that way. Since
89+
the smoke-runner delegation (PyAutoHands#260#263) all ten workspace runners
90+
are thin shims over `autohands/run_python.py`**so recording per-script
91+
timings routinely is now one PyAutoHands change, not ten repo sweeps.** This
92+
answers item 4 of `draft/research/ci/smoke_timing_and_profiling.md`
93+
("should the runner record per-script timings routinely?") with yes.
94+
95+
## Task
96+
97+
1. **PyAutoHands** — the report machinery (`result_collector.RunReport`,
98+
already mandatory in the PR gate via `--report-dir`) additionally emits a
99+
consolidated `smoke_timings.json` in the report dir: one entry per
100+
script/notebook — `{entry, kind, status, seconds, cap_s (the cap in force
101+
from build_util.timeout_for), exit_code}` — plus run metadata (project,
102+
env profile, python version). When `$GITHUB_STEP_SUMMARY` is set, append a
103+
compact per-entry timing table (slowest first) so every smoke run's
104+
timings are one click away in the Actions UI with no artifact download.
105+
2. **PyAutoHeart** — the reusable `smoke-tests.yml` uploads the report dir as
106+
a run artifact (`smoke-timings-<python-version>`, `if: always()`,
107+
`if-no-files-found: ignore`), so the dataset persists the full artifact
108+
retention window for every gate run across all ten repos at once.
109+
3. Timing must come from the runner's own measurement (the same clock the
110+
`[PASS] — <n>s` line prints), never re-derived; a TIMEOUT entry records
111+
the cap it hit.
112+
113+
## Acceptance
114+
115+
- A PR-gate smoke run on any workspace produces `smoke_timings.json` with one
116+
timed entry per executed script/notebook and a step-summary table, with no
117+
per-repo changes.
118+
- TIMEOUT entries carry `cap_s`; skipped entries are absent or explicitly
119+
marked, never silently timed as 0.
120+
- Existing report consumers (`run_all.py`, Heart `test_run`/`script_timing`)
121+
are unaffected.
122+
123+
Follow-up (not this task): the Heart board ingesting these artifacts into
124+
per-script rows with STALL/SLOW verdicts (reuse `retime.py`'s vocabulary)
125+
once a few weeks of data exist.

complete/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Token-light navigation over the finished-work records (schema:
66
only then grep a dated bucket. Curators: edit the band between the CURATED
77
markers; everything below GENERATED is rebuilt.
88

9-
1095 records across 7 buckets.
9+
1097 records across 7 buckets.
1010

1111
<!-- CURATED:START -->
1212
## Highlights
@@ -187,6 +187,7 @@ _(curate hard-won records here — survives regeneration.)_
187187
- [samplers-surface-autolens-tiers](2026/08/samplers-surface-autolens-tiers.md) — the samplers faculty's SamplerSurface now scans the findings
188188
- [save-json-numpy-scalar-typeerror](2026/08/save-json-numpy-scalar-typeerror.md) — Adds `NumpyEncoder` in `autofit/tools/util.py` (`np.ndarray` ->
189189
- [script-size-guard-git-based](2026/08/script-size-guard-git-based.md) — replaced the rotting `.script_sizes.json` snapshot with a git-diff truncation
190+
- [script-timing-baselines-fix](2026/08/script-timing-baselines-fix.md)
190191
- [script-title-underline-off-by-one](2026/08/script-title-underline-off-by-one.md)
191192
- [searches-readme-dashboard](2026/08/searches-readme-dashboard.md)
192193
- [should-simulate-capped-branch-reuse](2026/08/should-simulate-capped-branch-reuse.md) — small, single-repo follow-up of the closed PyAutoNerves#153
@@ -196,6 +197,7 @@ _(curate hard-won records here — survives regeneration.)_
196197
- [small-datasets-rmtree-committed-data](2026/08/small-datasets-rmtree-committed-data.md)
197198
- [smoke-runner-delegation](2026/08/smoke-runner-delegation.md)
198199
- [smoke-runner-jupyter-guard](2026/08/smoke-runner-jupyter-guard.md)
200+
- [smoke-timings-dataset](2026/08/smoke-timings-dataset.md)
199201
- [spawn-autonomy-log-generated](2026/08/spawn-autonomy-log-generated.md)
200202
- [spawn-drift-self-heal](2026/08/spawn-drift-self-heal.md)
201203
- [spawn-empty-body-privacy-fix](2026/08/spawn-empty-body-privacy-fix.md)

dashboard.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
</head>
124124
<body>
125125
<header class="hero"><span class="orb">📋</span><h1>PyAuto<b>Mind</b><span class="kind">Dashboard</span></h1><p class="tag">Intent. Priority. Flow.</p></header><p class="lede">Every task the Mind is holding. Tap a task's 📋 and its <code>/start_dev</code> command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. <a href="#recent">Recent</a> is the same work by date — what has been happening rather than what to do next.</p>
126-
<ul class="stats"><li><b>1</b><span>In flight</span></li><li><b>3</b><span>Parked</span></li><li><b>6</b><span>Planned</span></li><li><b>155</b><span>Backlog</span></li></ul>
126+
<ul class="stats"><li><b>1</b><span>In flight</span></li><li><b>3</b><span>Parked</span></li><li><b>6</b><span>Planned</span></li><li><b>154</b><span>Backlog</span></li></ul>
127127
<p class="muted mdsrc"><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/dashboard.md">markdown version</a></p>
128128
<h2>Start here</h2>
129129
<h3>Highest priority <span class="facets">(filed as high) — showing 12 of 17</span></h3>
@@ -163,7 +163,7 @@ <h2>Planned <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/blob
163163
<div class="task"><button class="copy" data-cmd="/route start the planned PyAutoMind task latent-nan-guard-honest-run — its record is in planned.md" aria-label="Copy the Claude command">📋</button><p><b>latent-nan-guard-honest-run</b><span class="facets"> — planned 2026-07-22</span></p></div>
164164
</details>
165165
<h2>Backlog <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/tree/main/draft">markdown version</a></h2>
166-
<p class="muted">155 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.</p>
166+
<p class="muted">154 filed prompts, not started — sorted most-pickable first (priority, then size). 25 of them belong to an epic and are listed only under Epics below.</p>
167167
<details>
168168
<summary>feature — 29</summary>
169169
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/autoarray/numba_cpu_likelihood_mge_convolution_and_caching.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/autoarray/numba_cpu_likelihood_mge_convolution_and_caching.md">Numba CPU likelihood phase 1: batched MGE convolution + operated-matrix caching</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">autoarray</span><span class="pill n">medium</span><span class="pill n">supervised</span><span class="pill r">high</span></span></p></div>
@@ -197,12 +197,11 @@ <h2>Backlog <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoMind/tree
197197
<div class="task"><button class="copy" data-cmd="/start_dev draft/feature/pyautomind/repos-sync-config-stamper.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/feature/pyautomind/repos-sync-config-stamper.md">Teach repos_sync --write to stamp organ config surfaces</a><span class="tags"><span class="pill w">✨ feature</span><span class="pill">pyautomind</span><span class="pill n">hard</span><span class="pill n">supervised</span><span class="pill n">low</span></span></p></div>
198198
</details>
199199
<details>
200-
<summary>bug — 32</summary>
200+
<summary>bug — 31</summary>
201201
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/health_fixes/jax_runtime_and_parity.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/health_fixes/jax_runtime_and_parity.md">Fix release JAX runtime compatibility and likelihood parity</a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">health_fixes</span><span class="pill r">too-large</span><span class="pill n">supervised</span><span class="pill r">high</span></span></p></div>
202202
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/health_fixes/jit_visualization_outputs.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/health_fixes/jit_visualization_outputs.md">Fix JIT quick-update visualization output regressions</a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">health_fixes</span><span class="pill r">too-large</span><span class="pill n">supervised</span><span class="pill r">high</span></span></p></div>
203203
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/health_fixes/samples_parameter_paths.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/health_fixes/samples_parameter_paths.md">Fix release result/sample parameter-path regressions</a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">health_fixes</span><span class="pill r">too-large</span><span class="pill n">supervised</span><span class="pill r">high</span></span></p></div>
204204
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/autofit/jax_011_message_log_partition_tuple_shape.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/autofit/jax_011_message_log_partition_tuple_shape.md">jax 0.11 breaks beta/gamma message log_partition under jit…</a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">autofit</span><span class="pill n">small</span><span class="pill n">supervised</span><span class="pill n">medium</span></span></p></div>
205-
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/pyautoheart/script_timing_baselines_orphaned_and_window_filled.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/pyautoheart/script_timing_baselines_orphaned_and_window_filled.md">Heart script_timing baselines are orphaned by path moves and filled…</a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">pyautoheart</span><span class="pill n">small</span><span class="pill n">supervised</span><span class="pill n">medium</span></span></p></div>
206205
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/autoarray/numba_kernel_shift_axes_swapped.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/autoarray/numba_kernel_shift_axes_swapped.md">Numba PSF gathers derive the y/x kernel shifts from the wrong kernel…</a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">autoarray</span><span class="pill n">low</span><span class="pill n">supervised</span><span class="pill n">medium</span></span></p></div>
207206
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/autofit/loggaussian_prior_declares_own_support.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/autofit/loggaussian_prior_declares_own_support.md"><code>LogGaussianPrior</code> misreports its own support as <code>(-inf, inf)</code></a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">autofit</span><span class="pill n">small</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>
208207
<div class="task"><button class="copy" data-cmd="/start_dev draft/bug/autofit/plot_functions_discard_kwargs.md" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoMind/blob/main/draft/bug/autofit/plot_functions_discard_kwargs.md"><code>autofit.plot</code> functions accept <code>**kwargs</code> and silently discard them</a><span class="tags"><span class="pill w">🐛 bug</span><span class="pill">autofit</span><span class="pill n">small</span><span class="pill n">supervised</span><span class="pill n">normal</span></span></p></div>

0 commit comments

Comments
 (0)