{e(t["title"])}{issue}
{e(t["next"])}
diff --git a/.github/workflows/dashboard_refresh.yml b/.github/workflows/dashboard_refresh.yml index 8cb7632..3624dee 100644 --- a/.github/workflows/dashboard_refresh.yml +++ b/.github/workflows/dashboard_refresh.yml @@ -67,6 +67,12 @@ jobs: repository: ${{ github.repository_owner }}/PyAutoMind path: _mind + - name: PyAutoBrain (shared dashboard theme) + uses: actions/checkout@v4 + with: + repository: ${{ github.repository_owner }}/PyAutoBrain + path: _brain + - name: Dependencies run: python3 -m pip install --quiet pyyaml @@ -74,6 +80,7 @@ jobs: if: github.event_name == 'pull_request' env: GH_TOKEN: ${{ github.token }} + PYAUTO_BRAIN: ${{ github.workspace }}/_brain run: | bin/pyauto-pulse board --mind _mind git status --short @@ -82,6 +89,7 @@ jobs: if: github.event_name != 'pull_request' env: GH_TOKEN: ${{ github.token }} + PYAUTO_BRAIN: ${{ github.workspace }}/_brain run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 229c39f..52f73c3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ # built from those receipts, and state.json meets the Brain contract. # Offline on purpose: a project commit landing mid-PR must not redden an # unrelated organ PR (dashboard_refresh.yml re-reads the network nightly). -# 4. lychee link-rot over the prose *.md. raw.githubusercontent.com, +# 4. lychee link-rot over repository-owned prose *.md (tracked files only). raw.githubusercontent.com, # github.com blob pages (GitHub answers non-browser page views with 503; # a token does not help — the links stay for humans, only the gate skips them), # dashboard.md (hundreds of commit-pinned evidence links), the organ's own @@ -31,7 +31,7 @@ permissions: jobs: lint: runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 15 steps: - uses: actions/checkout@v4 @@ -69,6 +69,18 @@ jobs: PYAUTO_BRAIN: ${{ github.workspace }}/_brain run: python -m pytest tests -q + - uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Chromium interaction checks + env: + PYAUTO_BRAIN: ${{ github.workspace }}/_brain + run: | + npm install --no-save --no-package-lock playwright@1.63.0 + npx playwright install --with-deps chromium + node tests/browser_setup.cjs + - name: pyauto-pulse check --offline env: PYAUTO_BRAIN: ${{ github.workspace }}/_brain @@ -80,6 +92,11 @@ jobs: # with a token lychee checks github.com links through the API instead. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + # Dependency installs and sibling checkouts are outside this repo's prose. + # Keep filenames intact, including spaces, when passing tracked Markdown. + mapfile -d '' -t markdown_files < <( + git ls-files -z -- '*.md' ':!:dashboard.md' ':!:.claude/**' ':!:_mind/**' ':!:_brain/**' + ) lychee \ --github-token "${GITHUB_TOKEN}" \ --exclude '^https://raw\.githubusercontent\.com/' \ @@ -89,4 +106,4 @@ jobs: --no-progress \ --accept '200..=299,429' \ --max-redirects 5 \ - $(find . -name '*.md' -not -name 'dashboard.md' -not -path './.claude/*' -not -path './.git/*' -not -path './_mind/*' -not -path './_brain/*') + "${markdown_files[@]}" diff --git a/.gitignore b/.gitignore index ace699e..d2e3c69 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ _mind/ # Half-written receipt/snapshot files (written then renamed into place) receipts/*.tmp snapshots/*.tmp + +# Shared theme checkout and browser test dependencies +_brain/ +node_modules/ diff --git a/AGENTS.md b/AGENTS.md index 73d3bd3..89d7366 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -121,10 +121,8 @@ catalogue of repositories. The reader accepts `profiling-summary` v1 and v2; registry rows still pin one exact version. V2 adds setup identities, typed evidence and explicit reference -selections (`pulse/catalogue.py`, `REFERENCE.md`). Keep the live registry on v1 -until the project exporter migration. Accepting a schema is not scientific -acceptance and does not launch a baseline campaign. The setup browser follows -in its own phase. +selections (`pulse/catalogue.py`, `REFERENCE.md`). The live lens registry now reads the project v2 catalogue. Accepting a schema is not scientific +acceptance and does not launch a baseline campaign. The setup browser reads the captured index and verifies same-commit detail shards. ## Boundary (what this organ never does) diff --git a/README.md b/README.md index 232199f..0427199 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ It owns: - the **instance registry** (`registry.yaml`) — which projects publish a profiling summary, where, under which contract version; each project is named by its PyAutoMind body-map identity, never a second repository catalogue; -- the versioned **`profiling-summary` read contract** — v1 is live at - `autolens_profiling/dashboard/summary.json`, documented in that repo's +- the versioned **`profiling-summary` read contract** — v2 is live at + `autolens_profiling/dashboard/catalogue.json`, documented in that repo's [dashboard/README.md](https://github.com/PyAutoLabs/autolens_profiling/blob/main/dashboard/README.md); - the **ingest receipts** (`receipts/`) — the one commit each project was read at, per render — and the last-good **snapshots** (`snapshots/`), shown as @@ -54,3 +54,31 @@ Boundaries: [AGENTS.md](AGENTS.md). Design: ## License MIT — see [LICENSE](LICENSE). + +## Setup browser + +The front page uses the shared Heart-family theme from `PyAutoBrain/board/_theme.py`. +Set `PYAUTO_BRAIN` to that checkout (or place it beside this repo / in `_brain`). +The refresh workflow checks it out explicitly. Open AutoLens, choose a dataset and +model, then select the instrument and recorded configuration. Runtime is expanded; +breakdown, compilation, memory, settings, qualification and source evidence are +available underneath. Values use linear bars within matching measurement groups. +Campaign tasks live inside their campaign rows; each row has separate evidence +and task links. Both top actions copy the current editable prompt. + +Pulse embeds the validated captured v2 index and loads a chosen evidence shard +from that **same captured commit**, verifying its SHA-256 and setup identity. +A failed or cached fetch remains visible; a missing deep link never substitutes a +different run. Local reads cannot load remote shards as if they were published. +Inline v2 records and v1 diagnostics remain supported. No rendering action accepts +a baseline, changes campaign state, runs profiling, or selects the fastest run. +Temporal diagnostics remain collapsed while the front page focuses on setup +choices. Existing archive evidence still requires scientific review. + +Browser regression checks (synthetic evidence; no project network dependency): + +```bash +npm install --no-save --no-package-lock playwright@1.63.0 +npx playwright install chromium +node tests/browser_setup.cjs +``` diff --git a/badge.json b/badge.json index 3af2113..0fd715a 100644 --- a/badge.json +++ b/badge.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "pulse", - "message": "1 project \u00b7 159 records \u00b7 0 cached", + "message": "1 project \u00b7 98 records \u00b7 0 cached", "color": "yellow" } diff --git a/dashboard.html b/dashboard.html index a0854e4..5c4c798 100644 --- a/dashboard.html +++ b/dashboard.html @@ -1,63 +1,1207 @@ -
Measure. Trace. Compare.
One prompt, one ongoing chat. Add a campaign focus or idea here, or tell the chat before or afterwards.
Last check-in: 2026-10-04T17:18:38Z. Ledger dates are review dates, not measurement freshness.
| Campaign | Status | Open tasks | Next step | Reviewed |
|---|---|---|---|---|
| Point-source image plane · CPU | active | 1 | Phases 1–4 shipped; PyAutoLens#764 (extent warning) released in 2026.10.4.1. Next: docs-only autolens_profiling PR writing campaign completion evidence and fixing stale wiki/index lines (#763 'in flight' → #764 merged). RAL leftover folders present; mirror sync unverified. | 2026-10-04 |
| Point-source image plane · A100 | needs-decision | 1 | Phase 0+1 shipped (profiling#353 merged 2026-09-30; wiki stale). Forward-mode gradient NaN is unfixed and live in 2026.10.4.1 but only a Mind draft. Human decisions: issue that bug; authorize the autolens_inference image-plane fit measurement. | 2026-10-04 |
| Point-source source plane | parked | 2 | Core complete. Nautilus leaf autolens_inference#17 merged 2026-10-02 (wiki still says open). Blackjax parked: gradient-sampler admission not met. Warm-up task awaits the human's option (a)/(b)/(c). | 2026-10-04 |
| Cluster PointSolver robustness and performance | active | 2 | Unchanged since 2026-09-27; nothing issued or claimed. Wiki still points at the removed Mind draft (now Pulse tasks/). Pick one bounded phase: observable-overflow invalid-result policy + CI witness, or a representative-data baseline cell. | 2026-10-04 |
| Interferometer likelihood and streaming | active | 5 | Last CPU cell ran (RAL job 375978_3 COMPLETED 1:11:36, JSON on RAL, not on main) but misses the ≤1e-3 nat CPU/A100 witness: human decision needed. Streaming phases 3–5 shipped; the scaling measurement needs compute authorization. Curvature, memo and FFT tasks unstarted. | 2026-10-04 |
| Linear-solver accuracy and cost | blocked | 1 | Release half met: PyAutoArray#595 is in 2026.10.2.1 and 2026.10.4.1. RAL mirror sync (HPCPullPyAuto, plus a dependency-floor diff) unverified; phase 3 GPU/vmap parity and timing waits on it. | 2026-10-04 |
| Production imaging and likelihood breakdown | active | 3 | No new results since the pin. Over-sampling phase 1: add --instrument production presets to the JAX Delaunay cells (CPU smoke only); PyAutoArray#606 S/N over-sampling helper (in 2026.10.4.1) is new context. Post-certified Blocked-by is stale (resolved 2026-09-27). | 2026-10-04 |
| Measurement reliability and profiling tools | active | 9 | Only timing_noise_audit is issued (autolens_profiling#362: open, no comments, unassigned); start its audit phase via start_dev. Other nine tasks unchanged and unissued; mass_field superseded confirmed. | 2026-10-04 |
| PyAutoFit profiling bootstrap | needs-decision | 1 | PyAutoLabs/autofit_profiling does not exist (gh, 2026-10-04). Human decision pending on repo creation; no producer registration until it exists with ported baselines. | 2026-10-04 |
| Critical curves and evaluation grids | needs-decision | 0 | Release gate met: PyAutoGalaxy#646 first released in 2026.10.4.1. 2026-10-04: /prm of autolens_workspace_test#343 was denied by the auto-mode classifier ("Merge Without Review"); the human runs it outside auto mode to close the evaluation-grid-cap-field task. | 2026-10-04 |
| Certified positive solver | parked | 0 | Parked by choice; wiki unchanged since ab1e4fd (2026-09-27). Its three library/workspace Mind drafts remain in Mind. Retain parked; read the project ledger before proposing new work. | 2026-10-04 |
Open means tracked, not necessarily running. Blockers and decisions still apply.
| Task | Campaign | Status | Priority | Next step |
|---|---|---|---|---|
| Runtime cells' A100 `single_jit` includes the post-compile warm-up — source-plane 0.642 ms vs a steady 0.267 ms on the same node · issue | point-source-source | active | low | Execution 2026-10-04: human chose option (a). Branch feature/runtime-single-jit-median @87a7fcc (issue autolens_profiling#371) parked at Heart RED. Local CPU witness: single_jit 0.417 ms, median 0.388 ms (p10 0.320, p90 0.545). Imaging cells not yet wired. |
| Audit timing tests and profiling gates for measurement noise · issue | measurement-tools | ready | high | Issued as autolens_profiling#362 (open, 0 comments, unassigned). Start its read-and-classify audit phase via start_dev. |
| MGE likelihood_breakdown steps are cumulative and `linear_gaussians` is reported as 0 | measurement-tools | ready | low | Unchanged and unissued (no GitHub refs, 2026-10-04). Select one bounded step when measurement-tools is prioritised. |
| A gradient-cost probe: forward vs `value_and_grad` ms/eval and a strict FD check, on any registry cell | measurement-tools | ready | low | Unchanged and unissued (workspace_developer#117 closed 2026-07-28). Select one bounded step when prioritised. |
| Numba breakdown harness: perturb the instance so the operated-matrix memo cannot hide a step | measurement-tools | ready | medium | Unchanged and unissued (PyAutoArray#496 closed 2026-08-27). Select one bounded step when prioritised. |
| Search settings-estimation + profiling infrastructure (n_starts / batch_size / n_batch) | measurement-tools | ready | normal | Unchanged and unissued (autolens_profiling#82 closed 2026-08-18). Select one bounded step when prioritised. |
| jax_compile/probe.py lost its cell builder with the searches tier — give profiling its own | measurement-tools | ready | normal | Unchanged and unissued; scripts/misc/jax_compile/probe.py still on main (run state unverified). Select one bounded step when prioritised. |
| Linear-solver programme phase 3: GPU/vmap/A100 timing and parity rows for the solver corpus | linear-solver | blocked | medium | RAL sync SKIPPED 2026-10-04: 180 euclid_dr1 tasks RUNNING on the shared base; mirror at 2026-09-27 state, 16–35 commits behind tag 2026.10.4.1; a sync pulls MAINS and also needs a jax reinstall (Nerves main excludes jax 0.10.2). Next: sync when `squeue -u jnightin -t R` shows no euclid_dr1 jobs on the shared base, or the human accepts the risk. |
| autofit_profiling: bootstrap the repo + general PyAutoFit profiling epic | autofit | needs-decision | normal | Human answers the single repo-creation question (PyAutoLabs/autofit_profiling absent, 2026-10-04) before any porting. |
| Cluster PointSolver — robustness, analysis settings and performance | cluster-pointsolver | ready | normal | Unissued, no claim. Pick one bounded phase per Mind epics.md: observable-overflow invalid-result policy + CI witness, or a representative-data baseline cell. |
| Profile imaging pixelizations at production over-sampling | imaging | ready | normal | Phase 1 (workspace-only): --instrument euclid|hst presets for the JAX Delaunay runtime/breakdown cells, keeping flat-1 selectable; CPU smoke only. A100 re-measure is a separate compute-authorized phase. |
| Interferometer decision matrix — fill the last cell (CPU rect 39² at alma_high r5.0) and flip the wiki row to shipped · issue | interferometer | active | normal | Execution 2026-10-04: human decision ACCEPT the 1.59e-3 nat CPU/A100 gap (precedent r3.5 at 1.5e-3). Branch feature/interferometer-decision-matrix-last-cell @93a002a committed locally; ship PARKED at Heart RED (release validation FAILED, stage integrate). Caveat: the A100 row's own A/B shows certified solver = PDIP to 0.0 nat, so the gap may be F/D on older revisions rather than solver. Correction: PyAutoArray#582 first released 2026.9.27.2. Next: ship once Heart clears. |
| Interferometer fixed-mapper searches: reuse the W~ curvature matrix across likelihood calls via `preloads.curvature_matrix` | interferometer | ready | normal | Phase 1 sliced 2026-10-04 into a Mind draft (PyAutoArray preload plumbing + CPU breakdown lever, no GPU); run it through start_dev. Human science call first - no production SLaM stage holds the mapper fixed today. |
| fnnls warm-start memo: stop it slowing scattered evaluation streams (interferometer CPU, autolens_profiling#332) | interferometer | ready | medium | Unstarted (no PR/issue/branch/results, 2026-10-04). Select one bounded step when prioritised. |
| Campaign: interferometer streaming (array-free) vs in-memory — memory and time scaling to 2e8 visibilities · issue | interferometer | active | high | Execution 2026-10-04: branch feature/interferometer-streaming-scaling @4a0ef46 (issue autolens_profiling#368) parked at Heart RED. Laptop CPU 8-thread indicative rows recorded (chunk 65536 to 5e7 vis; in-memory first failure 1e6 under a 10 GB cap; parity 1.2e-9 nats at 5e5; 1e8 skipped). Go/no-go overtaken; recorded as release evidence for 2026.10.4.1. nufft_chunk_size arm hits a second memory wall in transformer.image_from (library candidate, no issue filed). |
| Interferometer W~ curvature matrix is FFT-bound on the mask extent: pruned padded FFT and real-space pixel scale on the A100 | interferometer | ready | normal | Unstarted (no PR/issue/branch/results, 2026-10-04). Select one bounded step when prioritised. |
| PointSolver profiling cells: lensed quasar → cluster runtime tier → single/multi-source → multiplane | cluster-pointsolver | ready | normal | Unissued, no claim. Candidate first cell: a representative-data cluster likelihood_runtime baseline (single-source preset). |
| Point-source A100 speed-up campaign: profile and optimize with the shared breakdown | point-source-image-gpu | needs-decision | high | Execution 2026-10-04: forward-mode NaN issued as PyAutoLens#767 (reproduced on CPU fp64: forward [nan]x5, reverse finite; stack 2026.10.4.1+3 b695e57b6); Mind registry 0112ccba. The autolens_inference image-plane fit measurement still awaits human authorisation. |
| Point-source source-plane chi-squared speed-up campaign — remaining candidates (phases 1–2e shipped) | point-source-source | blocked | low | Still blocked: gradient-sampler admission not met (autolens_inference has only the Nautilus source-plane leaf, #17 merged 2026-10-02). Blackjax stays parked. |
| Point-source (single-source) CPU campaign — carried leftovers and completion evidence · issue | point-source-image-cpu | ready | low | Execution 2026-10-04: docs-only branch feature/point-source-wiki-reconcile @0560284 (issue autolens_profiling#370), parked at Heart RED. Still owed: RAL cleanup, register_model grad-zero prompt, test move, CI smoke cells, nopad deletion. |
| Assess remaining likelihood bottlenecks after certified solver integration on CPU and GPUs | imaging | needs-slicing | normal | Blocked-by is stale: resolved 2026-09-27 (PyAutoArray#567 in 2026.9.26.1). Unissued; scope one bounded phase from the full task contract. |
| profile_lens_aggregator.py cannot run from the autolens_workspace_developer root: no config/ directory | measurement-tools | ready | low | Unchanged: workspace_developer main still has no root config/ (bug presumed live, not re-run). Select one bounded step when prioritised. |
| jax_profiling/gradient/imaging/pixelization.py: 3.2% of its pin move is unattributed | measurement-tools | ready | normal | Unchanged and unissued (PyAutoArray#490 merged 2026-08-26). Select one bounded step when prioritised. |
| Pair JAX/XLA env vars with measured compile and run times, per backend | measurement-tools | ready | high | Unchanged and unissued. Context: jax-version policy changes merged to library mains 2026-10-04, unreleased. Select one bounded step when prioritised. |
| Quick-update plotting cost — minutes per update, and it is not JAX compile | imaging | ready | medium | Unchanged; targets PyAutoLens/PyAutoGalaxy (PyAutoLens#680 closed 2026-07-31, no follow-up issue). Select one bounded step when prioritised. |
Completed and superseded tasks remain in the ledger.
The cross-project view of the organism's profiling. Each <lib>_profiling project repo runs its producers, keeps its results and drift policy, and publishes a profiling-summary file. This page reads each file at one resolved commit, validates the exchange contract and shows what the producer says: it recomputes no ratio, combines no timings whose axis or hardware differs, ranks nothing across projects and issues no verdict. Judgment belongs to the Brain's profiling conductor (/profiling triage).
| Project | Scope | Evidence | Last fetch | Coverage | Integrity | Freshness | Qualification | Links |
|---|---|---|---|---|---|---|---|---|
| lens autolens_profiling | release-runtime | 2026-08-17 | 2026-10-05 @ 4832679a | 145 series · 159 points · 4 releases · 61 cells · 6 excluded | ok | freshness policy unspecified · age 49 d at fetch 2026-10-05 | 2/159 records · 0/145 comparisons qualified | dashboard · repo · receipt |
Three notions are kept apart: integrity is whether the file was fetched and meets the contract; freshness is the evidence's age against the producer's own valid_until (none declared → "freshness policy unspecified"); qualification is the producer's own flag on each record and comparison. Healthy transport never makes unknown quality green.
Project autolens_profiling, scope release-runtime, read from PyAutoLabs/autolens_profiling dashboard/summary.json at 4832679a; producer revision a93f37a7; generated 2026-10-04T18:28:06Z; policy runtime-drift-2x-1ms. Project dashboard · receipt.
breakdown:interferometer/sdp81/delaunay/sdp81:hpc_a100_fp64@2026.8.17.1: loadavg 65.7 above the cap 8 (evidence)breakdown:interferometer/sdp81/delaunay_numba/sdp81:hpc_ral_cpu_fp64@2026.8.17.1: loadavg 68.3 above the cap 8 (evidence)breakdown:interferometer/sdp81/mge/sdp81:hpc_a100_fp64@2026.8.17.1: loadavg 55.7 above the cap 8 (evidence)breakdown:interferometer/sdp81/mge/sdp81:hpc_a100_fp64_sparse@2026.8.17.1: loadavg 55.7 above the cap 8 (evidence)breakdown:interferometer/sdp81/pixelization/sdp81:hpc_a100_fp64@2026.8.17.1: loadavg 65.7 above the cap 8 (evidence)breakdown:interferometer/sdp81/pixelization_numba/sdp81:hpc_ral_cpu_fp64@2026.8.17.1: loadavg 68.3 above the cap 8 (evidence)| Comparison | Policy | Axis · metric | Baseline → candidate | Ratio (producer) | Status | Qualified | Reasons | Triage |
|---|---|---|---|---|---|---|---|---|
runtime:imaging/delaunay/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.6.649 [local_cpu_fp64] → 2026.8.17.1 | 0.27 | improved | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
runtime:imaging/delaunay/hst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay/jwst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay/jwst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay_nn/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay_numba/euclid:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay_numba/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/ao:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.6.649 [local_cpu_fp64] → 2026.8.17.1 | 0.671 | flat | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
runtime:imaging/mge/hst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/jwst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/jwst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge_mass_jax/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.6.649 [local_cpu_fp64] → 2026.8.17.1 | 0.232 | improved | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
runtime:imaging/pixelization/hst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/jwst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/jwst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization_numba/euclid:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization_numba/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization_numba_mge_mass/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:interferometer/delaunay/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:interferometer/mge/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:interferometer/pixelization/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:cluster/image_plane:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.23.1 → 2026.8.17.1 | 0.469 | improved | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
breakdown:cluster/source_plane:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.6.649 → 2026.7.23.1 | 1.469 | flat | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
breakdown:imaging/delaunay/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.6.649 → 2026.8.17.1 | 0.331 | improved | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
breakdown:imaging/delaunay/hst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.5.29.4 → 2026.7.6.649 | 1.108 | flat | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
breakdown:imaging/delaunay_nn/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay_numba/euclid:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay_numba/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/mge/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.6.649 → 2026.8.17.1 | 0.225 | improved | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
breakdown:imaging/pixelization/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.7.6.649 → 2026.8.17.1 | 0.393 | improved | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
breakdown:imaging/pixelization/hst:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | 2026.5.29.4 → 2026.7.6.649 | 1.139 | flat | no | baseline row unqualified: no provenance block (pre-#342 row); candidate row unqualified: no provenance block (pre-#342 row) | |
breakdown:imaging/pixelization_numba/euclid:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/pixelization_numba/hst:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_direct_conv/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_direct_conv/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_jax/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_jax/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_reference/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_reference/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_source_loop/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_symmetric/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba_two_stage/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge/alma:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge/alma_high:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge/sma:local_cpu_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge_dft/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba_direct_conv/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba_direct_conv/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba_jax/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba_jax/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba_reference/alma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba_reference/sma:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:point_source_image/image_plane/simple:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:point_source_source/source_plane/simple:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only |
| Comparison | Policy | Axis · metric | Baseline → candidate | Ratio (producer) | Status | Qualified | Reasons | Triage |
|---|---|---|---|---|---|---|---|---|
runtime:imaging/delaunay/hst:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay/hst:local_cpu_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay/jwst:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay/jwst:local_cpu_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/ao:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/hst:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/hst:local_cpu_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/jwst:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/mge/jwst:local_cpu_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/hst:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/hst:local_cpu_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/jwst:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/jwst:local_cpu_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:interferometer/delaunay/alma:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:interferometer/delaunay/sma:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:interferometer/mge/sma:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:interferometer/pixelization/sma:local_cpu_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only |
| Comparison | Policy | Axis · metric | Baseline → candidate | Ratio (producer) | Status | Qualified | Reasons | Triage |
|---|---|---|---|---|---|---|---|---|
runtime:imaging/delaunay/hst:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay/hst:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay_nn/hst:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/delaunay_nn/hst:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/hst:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:imaging/pixelization/hst:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:point_source_source/source_plane_solved:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:datacube/delaunay/alma_high:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay/hst:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay/hst:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay_nn/hst:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay_nn/hst:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/mge/hst:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/pixelization/hst:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/pixelization/hst:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/delaunay/alma_high:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/mge/alma_high:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/mge/alma_high:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/pixelization/alma_high:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay/alma:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/jvla/delaunay/jvla:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/jvla/mge/jvla:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/jvla/mge/jvla:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/jvla/pixelization/jvla:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge/alma:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/mge/alma:hpc_a100_fp64_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization/alma:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/delaunay/sma:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/mge/sma:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/pixelization/sma:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:point_source_source/source_plane/simple:hpc_a100_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only |
| Comparison | Policy | Axis · metric | Baseline → candidate | Ratio (producer) | Status | Qualified | Reasons | Triage |
|---|---|---|---|---|---|---|---|---|
runtime:point_source_image/image_plane_solved:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
runtime:point_source_source/source_plane_solved:local_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only |
| Comparison | Policy | Axis · metric | Baseline → candidate | Ratio (producer) | Status | Qualified | Reasons | Triage |
|---|---|---|---|---|---|---|---|---|
runtime:point_source_source/source_plane_solved:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:cluster/image_plane:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/delaunay/alma_high:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/delaunay_numba/alma_high:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/pixelization/alma_high:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/pixelization_numba/alma_high:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay/alma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay_numba/alma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization/alma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization_numba/alma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/delaunay/sma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/delaunay_numba/sma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/pixelization/sma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/pixelization_numba/sma:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:point_source_image/image_plane/simple:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:point_source_source/source_plane/simple:hpc_ral_cpu_fp64 | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only |
| Comparison | Policy | Axis · metric | Baseline → candidate | Ratio (producer) | Status | Qualified | Reasons | Triage |
|---|---|---|---|---|---|---|---|---|
breakdown:datacube/delaunay/alma_high:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay/hst:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/delaunay/hst:hpc_a100_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/pixelization/hst:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:imaging/pixelization/hst:hpc_a100_mp_sparse | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/delaunay/alma_high:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/mge/alma_high:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/alma_high/pixelization/alma_high:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/delaunay/alma:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/jvla/delaunay/jvla:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/jvla/mge/jvla:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/jvla/pixelization/jvla:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/pixelization/alma:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/delaunay/sma:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only | |
breakdown:interferometer/sma/pixelization/sma:hpc_a100_mp | runtime-drift-2x-1ms | runtime · single_jit_s | — → — | — | insufficient | no | one release only |
Project autolens_profiling, scope setup-catalogue, read from PyAutoLabs/autolens_profiling dashboard/catalogue.json at 4832679a; producer revision a93f37a7; generated 2026-10-04T18:28:06Z; policy no-temporal-comparisons. Project dashboard · receipt.
{_e(name)}: {_e(integrity(snapshot))}. Displayed evidence retains its captured source.
' + ) + content.append(setup_browser.render(snapshot)) + if (snapshot.doc or {}).get("version") != 2: + content.append( + f"{_e(name)}: setup catalogue unavailable; original project evidence remains below.
" + ) + content.append( + '/profiling triage")
- lede = re.sub(r"`(.+?)`", r"\1", lede)
- notions = re.sub(r"\*\*(.+?)\*\*", r"\1", _e(NOTIONS))
- notions = re.sub(r"`(.+?)`", r"\1", notions)
return (
- "\n"
- ""
+ '\n'
+ ''
"Measure. Trace. Compare.
{lede}
" - f"| Project | Scope | " - "Evidence | Last fetch | Coverage | Integrity | " - "Freshness | Qualification | Links |
|---|
{notions}
" - f"{''.join(_html_detail(s, now) for s in views)}| Campaign | Status | Links | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
{e(c["title"])}{len(tasks)} {"task" if len(tasks) == 1 else "tasks"} · next step{e(c["next"])} Active tasks' ) - campaigns.append( - f"{title} | {e(c['status'])} | {n} | {e(c['next'])} | {e(c['updated'])} | {e(t["title"])}{issue} | {e(t["campaign"])} | {e(t["status"])} | {e(t.get("priority", "normal"))} | {e(t["next"])} | No open tasks. ') + for t in tasks: + issue = ( + f'↗' + if t.get("issue") + else "" + ) + parts.append( + f'{e(t["title"])}{issue}{e(t["next"])} | {e(c["status"])} |
Choose a project, dataset and model. Qualification belongs to each recorded setup.
', + ] + return "".join(parts) diff --git a/pulse/setup_browser.css b/pulse/setup_browser.css new file mode 100644 index 0000000..8829acf --- /dev/null +++ b/pulse/setup_browser.css @@ -0,0 +1,218 @@ +/* Layout additions to Brain's shared Pulse/Heart-family theme. */ +.skip { + position: absolute; + left: -9999px; +} +.skip:focus { + position: static; +} +.intro h2 { + margin-bottom: 0.5rem; +} +.intro p { + margin: 0.5rem 0; +} +.qualification { + font-weight: 650; + color: var(--warn); +} +.quick-links { + display: flex; + flex-wrap: wrap; + gap: 0.5rem 1.2rem; + margin: 1.2rem 0 1.6rem; + font-size: 0.85rem; +} +[data-id="navigation"] details { + border: 1px solid var(--line); + border-radius: 10px; + padding: 0.6rem 0.85rem; + margin: 0.5rem 0; +} +[data-id="navigation"] details details { + margin: 0.5rem 0 0.3rem 1rem; +} +[data-id="navigation"] summary { + cursor: pointer; + font-weight: 650; + padding: 0.35rem 0; +} +.model-choice { + display: block; + text-align: left; + padding: 0.55rem 0.75rem; + margin: 0.3rem 0; + width: 100%; + font: inherit; + cursor: pointer; + border: 1px solid var(--line); + border-radius: 7px; + background: var(--bg); + color: var(--fg); +} +.model-choice[aria-current="true"] { + border-color: var(--accent); + font-weight: 700; +} +[data-id="results"] { + scroll-margin-top: 1rem; + margin-top: 1.6rem; + border-top: 2px solid var(--line); + padding-top: 0.4rem; +} +[data-id="results"] h2 { + font-size: 1.25rem; +} +.selectors { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 0.8rem; + margin: 1rem 0; +} +.selectors label { + display: block; + font-weight: 600; + font-size: 0.85rem; +} +.selectors select { + display: block; + width: 100%; + min-width: 0; + margin-top: 0.35rem; + padding: 0.65rem; + border: 1px solid var(--line); + border-radius: 7px; + background: var(--bg); + color: var(--fg); + font: inherit; +} +.context { + padding: 0.75rem; + border-left: 3px solid var(--accent); + background: var(--btn); + margin: 1rem 0; +} +.context p { + margin: 0.2rem 0; +} +[data-id="results"] details { + margin: 1rem 0; +} +[data-id="results"] summary { + cursor: pointer; + font-weight: 600; +} +.metric-panel { + margin: 1.5rem 0; +} +.metric-panel h3 { + font-size: 1.05rem; +} +.metric-list { + list-style: none; + padding: 0; +} +.metric-row { + padding: 0.65rem 0; + border-bottom: 1px solid var(--line); +} +.metric-title { + display: flex; + justify-content: space-between; + gap: 1rem; + align-items: baseline; +} +.metric-title span { + overflow-wrap: anywhere; +} +.metric-value { + white-space: nowrap; + font-variant-numeric: tabular-nums; + font-weight: 700; +} +.bar-track { + height: 0.45rem; + margin: 0.45rem 0; + border-radius: 4px; + background: var(--line); + overflow: hidden; +} +.bar { + height: 100%; + background: var(--accent); + border-radius: 4px; +} +.metric-meta { + font-size: 0.75rem; + color: var(--muted); +} +.empty { + padding: 0.8rem; + border: 1px dashed var(--line); + border-radius: 8px; + color: var(--muted); +} +.config { + display: grid; + grid-template-columns: minmax(9rem, 1fr) minmax(0, 2fr); + gap: 0.3rem 1rem; + margin: 1rem 0; +} +.config dt { + font-weight: 600; +} +.config dd { + margin: 0; + overflow-wrap: anywhere; +} +.config code { + white-space: pre-wrap; + font-size: 0.8rem; +} +.evidence-list { + padding-left: 1.2rem; +} +.evidence-list li { + overflow-wrap: anywhere; + margin: 0.5rem 0; +} +.status-error { + color: var(--bad); +} +button.retry { + font: inherit; + cursor: pointer; + padding: 0.5rem 0.75rem; +} +[data-id="load-status"]:empty { + display: none; +} +.axis-note { + font-size: 0.85rem; + color: var(--muted); +} +@media (max-width: 40rem) { + .selectors { + grid-template-columns: minmax(0, 1fr); + } + .config { + grid-template-columns: minmax(0, 1fr); + } + .config dd { + margin-bottom: 0.5rem; + } + .metric-title { + align-items: start; + } + [data-id="navigation"] details details { + margin-left: 0.3rem; + } +} + +.metric-panel > summary { + font-size: 1.05rem; +} +.metric-panel { + border-top: 1px solid var(--line); + padding-top: 0.8rem; +} diff --git a/pulse/setup_browser.js b/pulse/setup_browser.js new file mode 100644 index 0000000..9e1611f --- /dev/null +++ b/pulse/setup_browser.js @@ -0,0 +1,744 @@ +/* Pulse v2 reader UI; conventions shared with the project setup browser. + * Consumes captured producer data only. No cross-setup joins or scientific judgement. */ +(() => { + "use strict"; + for (const root of document.querySelectorAll("[data-setup-browser]")) { + const capture = JSON.parse( + root.querySelector(".setup-payload").textContent, + ); + const prefix = "setup-" + capture.instance + "-"; + const $ = (id) => root.querySelector('[data-id="' + id + '"]'); + const status = $("load-status"), + nav = $("navigation"), + results = $("results"); + const repo = capture.repo; + let catalogue = capture.catalogue, + state = {}, + generation = 0; + const cache = new Map(); + const names = { + imaging: "Imaging", + interferometer: "Interferometer", + datacube: "Datacube", + point_source_image: "Point source · image plane", + point_source_source: "Point source · source plane", + multi_dataset: "Multiple datasets", + cluster: "Cluster", + lens: "Lens components", + experiments: "Other experiments", + }; + const models = { + delaunay: "Delaunay", + rectangular: "Rectangular", + mge: "MGE", + delaunay_nn: "Delaunay natural neighbour", + }; + const label = (value) => + models[value] || names[value] || String(value).replaceAll("_", " "); + function node(tag, text, cls) { + const el = document.createElement(tag); + if (text !== undefined) el.textContent = text; + if (cls) el.className = cls; + return el; + } + function append(parent, tag, text, cls) { + const el = node(tag, text, cls); + parent.append(el); + return el; + } + function source(path) { + if ( + typeof path !== "string" || + path.split("/").some((p) => !p || p === "." || p === "..") || + /[\\:\x00-\x1f]/.test(path) + ) + throw Error("Unsafe evidence path"); + return ( + repo + + "/blob/" + + encodeURIComponent(capture.commit || "main") + + "/" + + path.split("/").map(encodeURIComponent).join("/") + ); + } + function link(parent, path, text) { + const a = append(parent, "a", text); + a.href = source(path); + return a; + } + function disclosure(parent, title) { + const d = append(parent, "details"); + append(d, "summary", title); + return d; + } + const instrument = (s) => s.instrument || "unspecified"; + function configurationLabel(s) { + if (s.role === "planned_baseline") return "Baseline · not measured"; + const info = (catalogue.evidence_shards || []).find( + (item) => item.setup_id === s.id, + ); + const axes = { + runtime: "Runtime", + breakdown: "Breakdown", + compile: "Compile", + memory: "Memory", + }; + const recorded = catalogue.records.filter((r) => r.setup_id === s.id); + const axisNames = info?.axes || [...new Set(recorded.map((r) => r.axis))]; + const devices = info?.devices || [ + ...new Set(recorded.map((r) => r.identity.device).filter(Boolean)), + ]; + const precisions = info?.precisions || [ + ...new Set(recorded.map((r) => r.identity.precision).filter(Boolean)), + ]; + const pixels = s.configuration.source_pixels?.value; + return [ + s.role === "reference_candidate" ? "Reference candidate" : "Archive", + axisNames.map((a) => axes[a] || a).join(" + ") || + "Recorded configuration", + devices.map((d) => d.toUpperCase()).join(" / "), + precisions.join(" / "), + pixels == null ? null : pixels + " source pixels", + (s.configuration_id || s.id).slice(-6), + ] + .filter(Boolean) + .join(" · "); + } + + async function verified(path, expected) { + const response = await fetch(path); + if (!response.ok) + throw Error("Could not load evidence (HTTP " + response.status + ")."); + const bytes = await response.arrayBuffer(); + if (!crypto.subtle) + throw Error( + "Evidence verification needs HTTPS or localhost. Original evidence links remain available.", + ); + const actual = Array.from( + new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)), + (x) => x.toString(16).padStart(2, "0"), + ).join(""); + if (actual !== expected) + throw Error( + "Evidence changed since this page was published. Reload the page to get a matching catalogue.", + ); + return JSON.parse(new TextDecoder().decode(bytes)); + } + function route(next, push = true) { + next = Object.keys(next).length + ? { ...next, instance: capture.instance } + : {}; + state = next; + const hash = new URLSearchParams( + Object.entries(next).filter(([, v]) => v), + ); + if (push && location.hash.slice(1) !== hash.toString()) + history.pushState(null, "", "#" + hash); + render(); + if (push) + window.dispatchEvent( + new CustomEvent("setup-route", { detail: capture.instance }), + ); + } + function fromURL() { + const incoming = Object.fromEntries( + new URLSearchParams(location.hash.slice(1)), + ); + route(incoming.instance === capture.instance ? incoming : {}, false); + } + function navigation() { + nav.replaceChildren(); + const project = disclosure(nav, capture.label); + project.dataset.id = "project-picker"; + project.open = false; + const families = [ + ...new Set([ + ...catalogue.setups.map((s) => s.dataset), + ...(catalogue.navigation || []) + .filter((s) => s.category === "scientific_entrypoint") + .map((s) => s.dataset), + ]), + ]; + families.sort( + (a, b) => Object.keys(names).indexOf(a) - Object.keys(names).indexOf(b), + ); + for (const dataset of families) { + const family = disclosure(project, label(dataset)); + family.dataset.family = dataset; + const choices = [ + ...new Set([ + ...catalogue.setups + .filter((s) => s.dataset === dataset) + .map((s) => s.model), + ...(catalogue.navigation || []) + .filter( + (s) => + s.category === "scientific_entrypoint" && + s.dataset === dataset, + ) + .map((s) => s.model), + ]), + ].sort(); + for (const model of choices) { + const button = append(family, "button", label(model), "model-choice"); + button.type = "button"; + button.dataset.dataset = dataset; + button.dataset.model = model; + button.onclick = () => { + route({ dataset, model }); + $("results").scrollIntoView({ behavior: "smooth", block: "start" }); + }; + } + } + const shared = disclosure(project, "Shared measurement tools"); + const tools = [ + ...new Set( + (catalogue.navigation || []) + .filter((s) => s.category === "shared_measurement_tools") + .map((s) => s.path.split("/")[2]), + ), + ]; + for (const tool of tools) { + const details = disclosure(shared, label(tool)); + const ul = append(details, "ul", "", "evidence-list"); + (catalogue.navigation || []) + .filter( + (s) => + s.category === "shared_measurement_tools" && + s.path.split("/")[2] === tool, + ) + .forEach((s) => + link(append(ul, "li"), s.path, s.path.split("/").at(-1)), + ); + } + } + function selector(parent, id, title, options, value, change) { + const l = append(parent, "label", title); + l.htmlFor = prefix + id; + const select = append(l, "select"); + select.id = prefix + id; + select.dataset.id = id; + options.forEach(([v, t]) => { + const o = append(select, "option", t); + o.value = v; + }); + select.value = value; + select.onchange = () => change(select.value); + return select; + } + function metadata(setup) { + const details = disclosure(results, "Configuration details"); + const dl = append(details, "dl", "", "config"); + const entries = Object.entries(setup.configuration || {}); + const priority = [ + "source_pixels", + "psf_shape", + "image_shape", + "image_pixels_masked", + "n_vis", + "regularization", + "transformer", + "oversampling", + ]; + entries.sort( + ([a], [b]) => + (priority.includes(a) ? priority.indexOf(a) : 99) - + (priority.includes(b) ? priority.indexOf(b) : 99), + ); + for (const [key, entry] of entries) { + append(dl, "dt", label(key)); + const dd = append(dl, "dd"); + if (entry.value === null) { + append(dd, "span", "Not recorded", "muted"); + dd.title = entry.reason || "Unknown"; + } else + append( + dd, + typeof entry.value === "object" ? "code" : "span", + typeof entry.value === "object" + ? JSON.stringify(entry.value) + : String(entry.value), + ); + } + } + function evidence(setup, records) { + const details = disclosure(results, "Profiling evidence and scripts"); + const ul = append(details, "ul", "", "evidence-list"); + const anchors = new Map(); + if (setup.evidence) + anchors.set(JSON.stringify(setup.evidence), setup.evidence); + records.forEach((r) => + anchors.set(JSON.stringify(r.evidence), r.evidence), + ); + for (const ev of anchors.values()) { + const li = append(ul, "li"); + link(li, ev.path, ev.path); + if (ev.fragment) append(li, "code", " · " + ev.fragment); + } + const scripts = (catalogue.navigation || []).filter( + (s) => s.dataset === setup.dataset && s.model === setup.model, + ); + scripts.forEach((s) => link(append(ul, "li"), s.path, s.path)); + if (!anchors.size && !scripts.length) + append(details, "p", "No evidence has been recorded for this setup."); + } + function qualification(records) { + const heading = results.querySelector(".context .qualification"); + if (heading) + heading.textContent = + "Evidence status: " + + [...new Set(records.map((r) => r.validation.status))].join(" / "); + const d = disclosure(results, "Qualification and measurement method"); + for (const r of records) { + const p = append(d, "p"); + append(p, "strong", r.metric + ": "); + append( + p, + "span", + "Exact recorded value: " + + r.measurement[r.metric] + + " " + + r.unit + + ". ", + ); + append(p, "span", r.validation.status + " — " + r.validation.reason); + append( + d, + "p", + "Host: " + + (r.provenance.host || "not recorded") + + " · precision: " + + (r.identity.precision || "not recorded") + + " · software: " + + JSON.stringify(r.identity.software), + "metric-meta", + ); + append(d, "p", "Method: " + JSON.stringify(r.method), "metric-meta"); + } + } + function panels(records) { + const axes = { + runtime: [ + "Likelihood runtime", + "Full likelihood observations. Single-JIT blocks can include first-call effects; batch timings are not single-call latency.", + ], + breakdown: [ + "Likelihood breakdown", + "Instrumented component costs are not the full compiled likelihood time. Bars compare values only within this setup and unit.", + ], + compile: [ + "Compilation and setup", + "Trace, compile, first call and setup wall time are distinct measurements; do not add them together.", + ], + memory: [ + "Memory", + "Observed host RSS is not GPU VRAM. Static compiler estimates are shown separately.", + ], + }; + for (const [axis, [title, note]] of Object.entries(axes)) { + const section = disclosure(results, title); + section.className = "metric-panel"; + section.open = axis === "runtime"; + append(section, "p", note, "axis-note"); + const rows = records.filter((r) => r.axis === axis); + if (!rows.length) { + append(section, "p", "Not measured for this configuration.", "empty"); + continue; + } + const groups = new Map(); + for (const r of rows) { + const scope = + { + "vmap.batch_time": "Batch wall time", + batch_wall: "Batch wall time", + "vmap.per_call": "Per-replica batch cost", + batch_per_call: "Per-replica batch cost", + single_jit_block: "Single-call observations", + single_call: "Single-call observations", + }[r.metric] || + (axis === "runtime" + ? label(r.metric) + " observations" + : "Component observations"); + const caption = [ + scope, + r.identity.device || "device unknown", + r.identity.backend || "backend unknown", + r.identity.precision || "precision unknown", + r.provenance.host || "host unknown", + r.unit, + ].join(" · "); + const key = JSON.stringify([caption, r.identity, r.method]); + if (!groups.has(key)) groups.set(key, { caption, rows: [] }); + groups.get(key).rows.push(r); + } + for (const { caption, rows: group } of groups.values()) { + append(section, "p", caption, "metric-meta"); + const list = append(section, "ul", "", "metric-list"); + const max = Math.max(...group.map((r) => r.measurement[r.metric])); + for (const r of group) { + const value = r.measurement[r.metric]; + const li = append(list, "li", "", "metric-row"); + const line = append(li, "div", "", "metric-title"); + append(line, "span", label(r.metric)); + const displayUnit = + r.unit === "s" && value > 0 && value < 1 ? "ms" : r.unit; + const displayValue = displayUnit === "ms" ? value * 1000 : value; + const measured = append( + line, + "strong", + Number(displayValue.toPrecision(4)) + " " + displayUnit, + "metric-value", + ); + measured.dataset.value = String(value); + measured.dataset.unit = r.unit; + measured.title = "Exact recorded value: " + value + " " + r.unit; + const track = append(li, "div", "", "bar-track"); + track.setAttribute("aria-hidden", "true"); + const bar = append(track, "div", "", "bar"); + bar.style.width = (max > 0 ? (100 * value) / max : 0) + "%"; + append( + li, + "p", + (r.method.statistic || "Statistic not recorded") + + " · " + + (r.method.repetitions === null + ? "repetitions not recorded" + : r.method.repetitions + " repetitions"), + "metric-meta", + ); + } + } + } + } + function advice(setup) { + const items = [ + ...(catalogue.hazards || []), + ...(catalogue.recommendations || []), + ].filter((x) => x.applies_to.setup_ids.includes(setup.id)); + const d = disclosure(results, "Hazards and setup guidance"); + if (!items.length) + append( + d, + "p", + "No version-qualified hazard or recommendation is bound to this configuration. This is not evidence that it is hazard-free.", + ); + for (const item of items) { + append(d, "h4", item.title); + append(d, "p", item.description); + append(d, "p", item.applies_to.limitations); + const scope = disclosure(d, "Qualification and version applicability"); + append( + scope, + "pre", + JSON.stringify( + { validation: item.validation, applies_to: item.applies_to }, + null, + 2, + ), + ); + item.evidence.forEach((ev) => link(append(d, "p"), ev.path, ev.path)); + } + const unbound = (catalogue.unbound_findings || []).filter((f) => + f.evidence.path.includes("/" + setup.dataset + "/"), + ); + if (unbound.length) { + const other = disclosure( + d, + "Related findings · applicability unverified", + ); + unbound.forEach((f) => { + append(other, "p", f.title + " — " + f.reason); + link(append(other, "p"), f.evidence.path, f.evidence.path); + }); + } + const estimates = (catalogue.static_memory_estimates || []).filter( + (e) => setup.evidence && e.evidence.path === setup.evidence.path, + ); + if (estimates.length) { + const other = disclosure(d, "Static compiler memory estimates"); + estimates.forEach((e) => + append( + other, + "p", + e.per_replica.value + " " + e.per_replica.unit + " · " + e.reason, + ), + ); + } + } + async function render() { + const ticket = ++generation; + results.replaceChildren(); + results.removeAttribute("aria-busy"); + status.textContent = ""; + status.className = ""; + const choices = catalogue.setups.filter( + (s) => s.dataset === state.dataset && s.model === state.model, + ); + nav + .querySelectorAll(".model-choice") + .forEach((b) => + b.setAttribute( + "aria-current", + String( + b.dataset.dataset === state.dataset && + b.dataset.model === state.model, + ), + ), + ); + nav.querySelectorAll("[data-family]").forEach((d) => { + if (d.dataset.family === state.dataset) d.open = true; + }); + if (!state.dataset || !state.model) { + results.hidden = true; + $("project-picker").querySelector("summary").textContent = + capture.label; + return; + } + results.hidden = false; + const picker = $("project-picker"); + picker.open = false; + picker.querySelector("summary").textContent = + capture.label + + " / " + + label(state.dataset) + + " / " + + label(state.model) + + " · change setup"; + append(results, "h2", label(state.dataset) + " / " + label(state.model)); + if (!choices.length) { + append( + results, + "p", + "No catalogue measurements for this model yet.", + "empty", + ); + evidence({ dataset: state.dataset, model: state.model }, []); + return; + } + const instruments = [...new Set(choices.map(instrument))].sort(); + if ( + (state.setup && !choices.some((s) => s.id === state.setup)) || + (state.instrument && !instruments.includes(state.instrument)) + ) { + append( + results, + "p", + "This linked configuration is not in the published catalogue. No substitute measurements are shown.", + "empty", + ); + const choose = append( + results, + "button", + "Choose an available configuration", + "retry", + ); + choose.type = "button"; + choose.onclick = () => + route({ dataset: state.dataset, model: state.model }); + return; + } + const requested = choices.find((s) => s.id === state.setup); + const selectedInstrument = requested + ? instrument(requested) + : instruments.includes(state.instrument) + ? state.instrument + : instrument( + choices.find((s) => s.role === "reference_candidate") || + choices[0], + ); + const rank = (s) => + s.role === "reference_candidate" + ? catalogue.records.some( + (r) => r.setup_id === s.id && r.axis === "runtime", + ) + ? 0 + : 1 + : s.role === "planned_baseline" + ? 3 + : 2; + const variants = choices + .filter((s) => instrument(s) === selectedInstrument) + .sort( + (a, b) => + rank(a) - rank(b) || + configurationLabel(a).localeCompare(configurationLabel(b)), + ); + // Baseline is a coverage placeholder, not a fallback claim that archive evidence is accepted. + const setup = requested || variants[0]; + state = { + instance: capture.instance, + dataset: state.dataset, + model: state.model, + instrument: selectedInstrument, + setup: setup.id, + }; + history.replaceState(null, "", "#" + new URLSearchParams(state)); + const fields = append(results, "div", "", "selectors"); + selector( + fields, + "instrument", + "Instrument", + instruments.map((i) => [ + i, + i === "unspecified" ? "Not specified" : i.toUpperCase(), + ]), + selectedInstrument, + (value) => + route({ + dataset: state.dataset, + model: state.model, + instrument: value, + }), + ); + selector( + fields, + "configuration", + "Configuration / evidence run", + variants.map((s) => [s.id, configurationLabel(s)]), + setup.id, + (value) => route({ ...state, setup: value }), + ); + const context = append(results, "div", "", "context"); + append( + context, + "p", + setup.role === "planned_baseline" + ? "Baseline pending · no accepted measurement" + : "Recorded evidence · inspect qualification below", + "qualification", + ); + append( + context, + "p", + "Other devices or runs may have different settings. This view keeps each recorded configuration separate.", + "muted", + ); + const keySettings = [ + "source_pixels", + "psf_shape", + "image_pixels_masked", + "n_vis", + ]; + const facts = append(context, "p", "", "metric-meta"); + facts.textContent = keySettings + .map( + (k) => + label(k) + + ": " + + (setup.configuration[k]?.value == null + ? "not recorded" + : JSON.stringify(setup.configuration[k].value)), + ) + .join(" · "); + if (setup.role === "planned_baseline") { + const slots = (catalogue.planned_cells || []).filter( + (c) => c.setup_id === setup.id, + ); + append( + context, + "p", + [...new Set(slots.map((s) => s.device.toUpperCase()))].join(" / ") + + " · " + + slots.length + + " measurement slots not measured.", + ); + panels([]); + metadata(setup); + advice(setup); + evidence(setup, []); + return; + } + const manifest = (catalogue.evidence_shards || []).find( + (s) => s.setup_id === setup.id, + ); + if (!manifest) { + const inline = catalogue.records.filter((r) => r.setup_id === setup.id); + if (inline.length) { + panels(inline); + metadata(setup); + qualification(inline); + advice(setup); + evidence(setup, inline); + return; + } + + const failed = catalogue.selections.filter( + (s) => s.setup_id === setup.id, + ); + append( + context, + "p", + failed.map((s) => s.status + ": " + s.reason).join("; ") || + "No measured evidence is available.", + ); + panels([]); + metadata(setup); + advice(setup); + evidence(setup, []); + return; + } + status.textContent = "Loading selected setup…"; + results.setAttribute("aria-busy", "true"); + try { + if (!/^catalogue\/shards\/[a-f0-9]{20}\.json$/.test(manifest.path)) + throw Error("Invalid evidence shard path."); + let shard = cache.get(manifest.sha256); + if (!shard) { + if (!capture.remote_shards) + throw Error( + "Remote detail is unavailable for a local checkout; use the project dashboard or original evidence.", + ); + shard = await verified( + capture.shard_base + manifest.path, + manifest.sha256, + ); + cache.set(manifest.sha256, shard); + } + if (ticket !== generation) return; + if ( + shard.version !== 2 || + shard.schema !== "profiling-summary" || + shard.setups.length !== 1 || + shard.setups[0].id !== setup.id || + JSON.stringify(shard.setups[0]) !== JSON.stringify(setup) || + shard.records.length !== manifest.records || + new Set(shard.records.map((r) => r.id)).size !== + shard.records.length || + shard.records.some( + (r) => + r.setup_id !== setup.id || + !Number.isFinite(r.measurement[r.metric]) || + r.measurement[r.metric] < 0, + ) + ) + throw Error("Evidence does not match the selected setup."); + panels(shard.records); + metadata(setup); + qualification(shard.records); + advice(setup); + evidence(setup, shard.records); + status.textContent = ""; + } catch (error) { + if (ticket !== generation) return; + status.textContent = "Evidence unavailable. " + error.message; + status.className = "status-error"; + const retry = append(results, "button", "Retry evidence", "retry"); + retry.type = "button"; + retry.onclick = () => { + cache.delete(manifest.sha256); + render(); + }; + metadata(setup); + evidence(setup, []); + } finally { + if (ticket === generation) results.removeAttribute("aria-busy"); + } + } + navigation(); + window.addEventListener("hashchange", fromURL); + window.addEventListener("setup-route", (event) => { + if (event.detail !== capture.instance) fromURL(); + }); + fromURL(); + } +})(); diff --git a/pulse/setup_browser.py b/pulse/setup_browser.py new file mode 100644 index 0000000..6cdd780 --- /dev/null +++ b/pulse/setup_browser.py @@ -0,0 +1,100 @@ +"""Render setup navigation from captured v2 evidence; never select scientific truth.""" + +from __future__ import annotations + +import html +import importlib.util +import json +import os +from pathlib import Path +from urllib.parse import quote + +from pulse import ORGAN_ROOT + + +def theme(): + candidates = [Path(os.environ["PYAUTO_BRAIN"])] if os.environ.get("PYAUTO_BRAIN") else [] + candidates += [ORGAN_ROOT.parent / "PyAutoBrain", ORGAN_ROOT / "_brain"] + for root in candidates: + path = root / "board/_theme.py" + if path.is_file(): + spec = importlib.util.spec_from_file_location("pulse_board_theme", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + raise RuntimeError("Shared theme unavailable: set PYAUTO_BRAIN to a PyAutoBrain checkout.") + + +def assets(): + return ( + (ORGAN_ROOT / "pulse/setup_browser.css").read_text(), + (ORGAN_ROOT / "pulse/setup_browser.js").read_text(), + ) + + +def render(snapshot): + doc = snapshot.doc or {} + if doc.get("version") != 2: + return "" + instance = snapshot.instance + payload = { + "catalogue": doc, + "instance": instance.instance, + "label": instance.repo.removesuffix("_profiling").replace("autolens", "AutoLens"), + "repo": instance.github_url, + "commit": snapshot.commit, + "shard_base": f"https://raw.githubusercontent.com/{instance.github}/{snapshot.commit}/{instance.summary_path.rsplit('/', 1)[0]}/", + "local": snapshot.source == "local", + } + # A local checkout's dirty files have no immutable remote representation. + payload["remote_shards"] = bool(snapshot.commit and snapshot.source == "remote") + encoded = ( + json.dumps(payload, separators=(",", ":"), ensure_ascii=False) + .replace("<", "\\u003c") + .replace("&", "\\u0026") + ) + fallback = [] + for setup in doc.get("setups", []): + ev = setup.get("evidence") + if ev: + url = instance.blob_url(snapshot.commit, quote(ev["path"], safe="/")) + fallback.append( + f'