8.8 KiB
REVIEW-dash — Adversary verdicts for phase dash (per-recipe run history fix)
SSOT: /srv/cc-ci/cc-ci-plan/plan-phase-dash-recipe-history.md Gates: M1 (fix implemented + locally verified), M2 (deployed + verified live).
Pre-claim independent ground truth (Adversary, @2026-06-17T16:20Z, cold)
Gathered directly from the host (ssh cc-ci), BEFORE any Builder claim — this is my own
baseline to verify the fix against, not the Builder's narrative.
Run artifacts on host /var/lib/cc-ci-runs:
- 432 run dirs total; 308 have a parseable
results.json; 124 dirs have NO parseableresults.json(in-flight / failed-early — contain onlyjunit/,screenshot.png,abra/). The fix MUST skip these 124 gracefully (no 500). results.jsonschema 2 keys:customization, finished, flags, level, lint, pr, recipe, ref, results, run_id, rungs, schema, screenshot, skips, stages, summary_card, version. Fields the history needs ARE present:recipe,version,level,ref,finished(epoch float timestamp),run_id. Status is derivable fromresults/rungs(per-stage pass/fail).
Per-recipe run counts (from parseable results.json):
33 plausible 24 ghost 9 mailu 6 cryptpad
33 custom-html 24 custom-html-tiny 8 lasuite-drive 3 drone
28 immich 15 mattermost-lts 8 lasuite-docs 3 custom-html-rst-bad
25 discourse 12 uptime-kuma 8 gitea
24 (ghost) 12 mumble 8 bluesky-pds
11 matrix-synapse 7 custom-html-bkp-bad
10 lasuite-meet 6 keycloak
9 n8n 6 hedgedoc
bluesky-pds(named M2 target) → 8 runs.plausible/custom-html→ 33 (exceed a 30 cap → good cap test). A ~30 display cap should show 8 for bluesky-pds, 30 for plausible/custom-html.
bluesky-pds runs — newest-first BY finished timestamp (the correct order):
run_id ref level finished
753 dcf933813df9 5 1781663348
556 f7b6c8dfb81c 5 1781301301
435 f7b6c8dfb81c 5 1781192858
427 f7b6c8dfb81c 5 1781178768
423 f7b6c8dfb81c 0 1781178063
ab-bluesky-pds-oldmain b2d86efba3f1 0 1781126338
m2rr-bluesky-pds b2d86efba3f1 0 1781123524
m2r-bluesky-pds b2d86efba3f1 0 1781121610
ADVERSARIAL TRAP TO CHECK: run ids are MIXED numeric (753,556,…) AND named
(m2rr-bluesky-pds, ab-bluesky-pds-oldmain). Sorting by int(run_id) would crash or misorder
the named runs; sorting lexically would put 9... after 7... wrongly and scatter named ones.
Only a finished-timestamp sort yields the correct newest-first order. I will verify the
deployed page matches the timestamp order above, and that 423 (older, finished 1781178063) sorts
BELOW 427 (1781178768) even though 423<427 numerically-close — and that the named runs land in
their timestamp positions, not bunched at top/bottom.
Current (buggy) code (dashboard/dashboard.py): history_for(recipe) returns
[_build_row(b) for b in _custom_recipe_builds() …]; _custom_recipe_builds fetches a single
Drone page …/builds?per_page=100. So history is capped at whatever recipe runs fall in the
latest-100 Drone window → most recipes show 1 row. Confirmed root cause matches plan §1.
Things I will break-test on the fix:
- Count + order per recipe match the host artifacts (esp. bluesky-pds 8, timestamp order above).
- The 124 unparseable dirs don't 500 and don't appear as garbage rows.
- Path-traversal guard +
/recipe/<name>validation preserved (try/recipe/../..,/recipe/foo%2f.., arg injection in recipe name). - Overview (
/),/badge/<recipe>.svg,/runs/<id>/<file>unchanged. - stdlib-only (no new imports/deps); mount stays read-only.
- Display cap actually bounds (plausible/custom-html show cap, not 33) AND newest are kept (not oldest) when capped.
- Run links resolve — for named run ids too (no Drone build number for m2r*/ab-*).
Verdicts
M1: PASS @2026-06-17T16:30Z (claim 3595e80, cold-verified)
history_for rewritten to source per-recipe history from local /var/lib/cc-ci-runs artifacts
(_local_history scans dirs → _results_for → groups by recipe → sorts newest-first by finished,
caps at HISTORY_CAP=30). All checks done COLD from my own fixture (tarred the 308 real
results.json off the host), against my own pre-claim baseline — not the Builder's word:
- Count + order match host exactly.
history_for("bluesky-pds")→ 8 rows in order['753','556','435','427','423','ab-bluesky-pds-oldmain','m2rr-bluesky-pds','m2r-bluesky-pds']— IDENTICAL to my independent timestamp-derived baseline. The mixed numeric+named id trap is handled correctly: sort key is(finished, _numeric_id)reverse;_numeric_idreturns -1 for named ids (noint()crash); 423 (older) sorts below 427 though numerically close; named runs land in their timestamp positions, not bunched. Total parseable grouped rows 308, 23 recipes — match. - Display cap bounds AND keeps newest. plausible 33→30, custom-html 33→30; verified
min(finished in capped) >= max(finished dropped)(oldest 3 dropped, not newest). - Malformed/empty dirs skipped, no 500. Injected EMPTYDIR / dir-with-junit-no-json /
malformed-json dir into fixture → total stayed 308, no exception, none appear as rows
(
_results_forreturns{}on miss/malformed;_local_historyskips no-recipe rows). - Security preserved.
_RUN_ID_RErejects../..,foo/..,a b,x;rm,..%2f, ``,.,foo;,<script>; acceptsbluesky-pds._results_for("../../etc/passwd")→{}(realpath guard intact). Unchanged from before. - No regression to other routes.
latest_per_recipe/_custom_recipe_builds(overview + badge source) untouched; only the history page changed source. Row-key parity:_local_history_rowemits the IDENTICAL 10 keys as_build_row, sorender_historyis unchanged. - stdlib-only. Imports unchanged: html, json, os, re, sys, time, urllib, http.server. No new deps.
- Renders.
render_history("bluesky-pds", …)→ 5384 bytes, 8 data rows; numeric ids link to Drone build, named ids link to/runs/<id>/summary.html— all four checked artifacts exist on host. - Unit suite: 13 passed (incl. new
test_history_sourced_from_local_artifacts).
No defects. M1 verified. (Consulted JOURNAL-dash.md only AFTER writing this verdict — no new concerns.) M2 (deploy + live verify) not yet claimed.
M2: PASS @2026-06-17T16:40Z (claim 4c0b289, cold-verified live)
Dashboard redeployed with the M1 fix; per-recipe history verified on the LIVE site
(https://ci.commoninternet.net). All probes run cold against the live service + re-derived host
ground truth (host now 439 dirs / 23 recipes — re-counted fresh, not trusting the claim):
- Deployed image rolled + healthy.
docker service ls→1/1 cc-ci-dashboard:11ac2a1e6c07(the M1 content-hash tag, rolled from15addbc7bf45). The live page serving 8 bluesky-pds rows incl. named ids is conclusive proof the NEW code is live (the old Drone-slice code could not). - Live counts = host counts. bluesky-pds 8=8, ghost 24=24, immich 28=28, discourse 25=25; plausible 30 and custom-html 30 correctly capped from 33. All match my freshly re-derived host per-recipe counts.
- Live order matches host timestamp order (mixed-id trap).
/recipe/bluesky-pdsrows in exact order753 556 435 427 423 ab-bluesky-pds-oldmain m2rr-bluesky-pds m2r-bluesky-pds— identical to my baseline. Per-row status/level/version also match: 753/556/435/427 = success L5; 423 + the three named runs = failure L0; refs correct. - Cap keeps NEWEST live.
/recipe/plausibletop row = run 758, which IS the host's newest plausible run byfinished(1781665203). Oldest dropped, not newest. - Other routes intact. overview
/→ 200,/badge/bluesky-pds.svg→ 200; overview still latest-per-recipe (Drone-sourced, unchanged). - Security intact live. Traversal/injection rejected at the live edge:
..%2f..%2fetc%2fpasswd→ 404,%2e%2e%2f%2e%2e→ 404 (noroot:leak);;-injection → 404. The only 200s are harmless:../../%2e%2enormalize to/(overview, no file content), and a valid-format-but-unknown name renders an empty history (0 rows)._RUN_ID_RE+ realpath guards hold. - Retention adequate (independently confirmed).
grep -rniE cc-ci-runs nix/shows NO rm/find-delete/prune/maxage/tmpfiles trim — nothing reaps/var/lib/cc-ci-runs. 439 dirs span 2026-05-31 → 2026-06-17. No growth cap needed now (recorded in DECISIONS).
No defects. M1 + M2 both fresh PASS, no VETO → Builder may write ## DONE.