Skip to content

feat: per-script smoke timings as a standing dataset (#264) - #265

Merged
Jammy2211 merged 1 commit into
mainfrom
claude/test-performance-dashboard-y3fdy7
Aug 24, 2026
Merged

feat: per-script smoke timings as a standing dataset (#264)#265
Jammy2211 merged 1 commit into
mainfrom
claude/test-performance-dashboard-y3fdy7

Conversation

@Jammy2211

Copy link
Copy Markdown
Collaborator

Closes #264. Phase 2 of the test-performance board arc (PyAutoLabs/PyAutoHeart#163): the runner records per-script timings routinely, so "what is slow" stops being a job-log archaeology session — one change here, all ten delegated workspace runners inherit it. Sibling PR: PyAutoLabs/PyAutoHeart#167 (artifact upload in the reusable workflow).

  • smoke_timings.json emitted from RunReport.write() — one call site covers run_python.py, run.py and generate.py. One merged file per report directory (rows keyed on entry path, so the script and notebook legs both survive and a re-run replaces its own rows; legs records every contributor). Schema smoke_timings/1: per entry {entry, kind, status, seconds, cap_s, exit_code}seconds is the runner's own measured duration (the number the [PASS] — <n>s line prints), TIMEOUT entries carry the cap they hit, skips are seconds: null, never a fabricated 0.
  • Step summary: when $GITHUB_STEP_SUMMARY is set, a slowest-first timing table per leg — every gate run's timings one click away with no artifact download.
  • ScriptResult gained cap_seconds/exit_code at all 8 execution sites, deliberately absent from to_dict() so the per-run JSONs Heart's script_timing/test_run and aggregate_results read stay byte-compatible (pinned by a test). aggregate_results skips the sidecar by name so the mega-run surface stays clean.
  • Drive-by fix: run.py never passed env_profile, so every notebook report claimed unknown — now threaded through (negative-tested).

Tests: 382 passed (+19), 14 pre-existing environmental failures unchanged (missing ipynb-py-convert/pngquant locally; identical set on main). Tenant firewall green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EoDPz2LevKeBaDwqFKtZrU


Generated by Claude Code

The runner's per-entry timings existed only as `PASS <name> — <n>s` lines in
job logs, recovered by hand-scraping every time a timing question came up.
Since the smoke-runner delegation (#260-#263) all ten workspace runners are
thin shims over `run_python.py`, so recording them routinely is one change
here rather than ten repo sweeps.

`RunReport.write()` now also emits `smoke_timings.json` into the report dir:
one row per entry — `{entry, kind, status, seconds, cap_s, exit_code}` — plus
the run metadata (project, directory, run_type, env_profile, python, ts) and a
`legs` list. `seconds` is the runner's own `time.time()` measurement, the same
number the console line prints, never re-derived. `cap_s` is `timeout_for`'s
resolved cap, so a TIMEOUT row records the cap it hit.

`ScriptResult` gained `cap_seconds` and `exit_code`, set by `build_util` at
every execution site — durations were already recorded, caps and exit codes
were not. They are deliberately absent from `to_dict()`: that dict is the
per-run JSON PyAutoHeart's `script_timing`/`test_run` and `aggregate_results`
read, and its shape stays byte-compatible.

One timings file per report DIRECTORY, not per leg. A report dir receives
several runner invocations (a gate's script and notebook legs; every directory
of every workspace in the `run_all` mega-run), so each write merges its rows
into the existing file keyed on the entry path: both legs survive, re-running
a leg replaces its own rows, `legs` records every contributor. An unreadable
file is replaced rather than merged. `aggregate_results` skips it by name — it
is a sidecar, not a run report, and would otherwise enter `runs` as an empty
phantom.

An entry that never ran carries `"seconds": null`, never a fabricated zero:
that is what `was_timed` is for, and it covers both a skipped entry and one
listed but missing.

With `$GITHUB_STEP_SUMMARY` set, each write appends a slowest-first table of
its own entries to the job summary, so a run's timings are one click away in
the Actions UI. Off CI nothing is appended, and a write failure is reported
and swallowed — losing the summary must not fail a run whose scripts passed.

Also fixes the notebook leg's surface statement: `run.py` never passed
`env_profile` to its report, so every notebook report claimed "unknown" while
running under a resolved profile. The timing dataset inherits that field,
where an unknown surface makes two runs incomparable for exactly the reason
`_surface` exists.

PyAutoHeart's `smoke-tests.yml` uploads the report dir as the companion
change, which is what makes the dataset outlive the job.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EoDPz2LevKeBaDwqFKtZrU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: per-script smoke timings as a standing dataset (smoke_timings.json)

2 participants