Files
cc-ci/machine-docs/STATUS-dash.md
autonomic-bot 3595e80d08
Some checks failed
continuous-integration/drone/push Build is failing
claim(M1): per-recipe history sourced from local /var/lib/cc-ci-runs artifacts (full history, not Drone 100-build slice)
history_for() now enumerates run dirs' results.json, groups by recipe, sorts
newest-first by finished timestamp (mixed numeric+named ids — timestamp is the
only correct key), caps at HISTORY_CAP=30, skips malformed/empty/no-recipe dirs.
Overview + badges + /runs + security guards + stdlib-only unchanged.
Local verify: 13/13 unit tests; full-fixture vs 308 real results.json →
bluesky-pds=8 in exact ts order, plausible capped 30 newest, edge dirs skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:25:39 +00:00

4.0 KiB

STATUS — 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)

Gate: M1 CLAIMED, awaiting Adversary

WHAThistory_for(recipe) in dashboard/dashboard.py now sources the FULL per-recipe run history from the local run artifacts under /var/lib/cc-ci-runs (each run dir's results.json), newest-first by the finished timestamp, display-capped at HISTORY_CAP (default 30). It no longer reads the Drone …/builds?per_page=100 slice (the root cause: that window dropped a recipe's older runs out of view, so most recipes showed 1 run). Overview (/), /badge/<recipe>.svg, /runs/<id>/<file>, security guards, and stdlib-only constraint are unchanged.

WHERE

  • Commit: see git log on origin/main for the claim(M1) commit (this push).
  • Changed files: dashboard/dashboard.py (new _run_status, _numeric_id, _local_history_row, _local_history; rewritten history_for; new HISTORY_CAP; new _LOCAL cache), and tests/unit/test_dashboard.py (new test_history_sourced_from_local_artifacts).
  • Host artifacts the page reads: /var/lib/cc-ci-runs/<id>/results.json (bind-mounted read-only into the dashboard container, unchanged from before).

HOW to verify (cold, from a fresh clone)

  1. Unit suite (stdlib render + new local-sourcing test):
    nix-shell -p 'python3.withPackages(ps:[ps.pytest])' --run \
      'DRONE_TOKEN_FILE=$(mktemp) python3 -m pytest tests/unit/test_dashboard.py -q'
    
    EXPECTED: 13 passed.
  2. Verify against the REAL host artifacts. Build a fixture of every results.json and run history_for against it (no Drone, no network):
    FIX=/tmp/advfix; rm -rf $FIX; mkdir -p $FIX
    ssh cc-ci 'cd /var/lib/cc-ci-runs && tar -cf - */results.json 2>/dev/null' | tar -xf - -C $FIX
    printf x > /tmp/t.tok
    DRONE_TOKEN_FILE=/tmp/t.tok CCCI_RUNS_DIR=$FIX python3 -c '
    import sys; sys.path.insert(0,"dashboard"); import dashboard as d
    r=d.history_for("bluesky-pds")
    print("count", len(r), [x["number"] for x in r])
    print("total parseable", sum(len(v) for v in d._local_history().values()))
    print("plausible cap", len(d.history_for("plausible")))'
    
    EXPECTED:
    • bluesky-pds count 8, order EXACTLY ['753','556','435','427','423','ab-bluesky-pds-oldmain','m2rr-bluesky-pds','m2r-bluesky-pds'] (newest-first by finished; note 423 sorts BELOW 427 though id 423<427, and named ids land in their timestamp positions — the mixed numeric+named id trap).
    • total parseable grouped rows 308 (matches host: 432 dirs, 308 with parseable results.json).
    • plausible capped at 30 (of 33), newest kept.

EXPECTED — invariants the Adversary's break-tests should confirm hold

  • The 124 run dirs with no/malformed results.json are skipped (no 500, no garbage row): _results_for returns {} on miss/malformed/non-dir, _local_history skips any row with no recipe.
  • Security preserved (untouched code paths): /recipe/<name> still gated by _RUN_ID_RE (^[A-Za-z0-9][A-Za-z0-9._-]*$ → rejects ../.., foo/.., spaces, ;); _results_for / serve_run_file still realpath-guarded against escaping /var/lib/cc-ci-runs.
  • stdlib-only: no new imports (still html,json,os,re,sys,time,urllib,http.server).
  • Overview (/) and /badge/<recipe>.svg still sourced from Drone latest-per-recipe (_custom_recipe_builds / latest_per_recipe unchanged) — only the history page changed source.
  • Run-link resolution: numeric id → {DRONE_URL}/{CI_REPO}/<id>; named id (m2r-*, ab-*) → /runs/<id>/summary.html (local, since no Drone build number exists).
  • Status pill derived from the per-stage results map (results.json has no top-level status): any fail/error → failure; all pass/skip → success; else unknown.

Gate: M2 — NOT STARTED (deploy + live verify; begins after M1 PASS)

Blocked

(none)