Per operator: the level ladder is now the FOUR essential rungs every recipe is
held to — install, upgrade (essential), backup/restore, functional (top = L4).
Integration (SSO/OIDC) and recipe-local are OPTIONAL capabilities: they no longer
appear as level rungs or skip rows and never cap the level. SSO is still enforced
for the run VERDICT (unchanged in run_recipe_ci.py); it just doesn't affect the
level. derive_rungs simplified accordingly (drops declared/deps/sso/repo-local
inputs). custom-html-tiny's EXPECTED_NA is back to just backup_restore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per operator: intentional skips now render like a pass row but labelled
'INTENTIONAL SKIP' (muted green) with the declared reason on the line beneath;
unintentional skips render amber 'UNINTENTIONAL SKIP' with a prompt to add a test
or declare them. The cap line is back to just the level-cap reason (the per-rung
reason now lives in the rows). Labelled, so it never reads as a PASS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per operator: drop the gap-sensitivity / cap-intent-clause / stale-detection
machinery. Model is now dead simple — recipe_meta.EXPECTED_NA = {rung: reason}
lists the rungs a recipe intentionally skips; ANY rung skipped (N/A) and not in
that list is unintentional.
results.json: replace the 'na' block + level_cap_intent with
skips: { intentional: {rung: reason}, unintentional: [rung] }
plus level_cap_rung (which rung capped). Badge/card derive intentional-vs-
unintentional from whether the capping rung is in the intentional list. Skips
still cap the level (never inflate). custom-html-tiny lists all three rungs it
intentionally skips (backup_restore, integration, recipe_local).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The level badge gains a third segment derived from level_cap_intent:
- amber 'gap?' when the climb was capped by an UNDECLARED gap-sensitive N/A
(backup_restore / functional) — a likely-missing test (unexpected skip)
- muted 'expected' when capped by a DECLARED intentional N/A (reviewed, nothing to fix)
- nothing extra for a clean cap, a full climb, or a real failure.
Font-safe text labels (no emoji) so the SVG renders headless/anywhere. Badge
never inflates — it only annotates the cap the level already reflects.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two changes the operator asked for after noticing custom-html-tiny PR #6 has no
backup/restore or functional coverage:
1) Intentional-vs-accidental N/A. A recipe can now declare
recipe_meta.EXPECTED_NA = {rung: reason} to mark a tier as deliberately not
applicable (e.g. a stateless static server has no backup surface). N/A still
caps the level — the harness never claims a rung it did not verify — but the
run is now annotated 'intentional · <reason>' instead of being indistinguishable
from a forgotten test. An *undeclared* N/A on a gap-sensitive rung
(backup_restore, functional) is surfaced as a 'possible coverage gap', and a
stale EXPECTED_NA (declared N/A but actually exercised) is surfaced too. All
non-blocking (R7): results.json gains level_cap_intent + an block, the
summary card shows the clause, and the CI log prints the gap/stale warnings.
(results.classify_na/cap_intent are pure + unit-tested; level.py untouched.)
custom-html-tiny declares backup_restore intentionally N/A.
2) custom-html-tiny functional test: writes a random file into the served content
volume (via the volume mountpoint, like install_steps.sh, since the SWS image
is shell-less), asserts exact-byte round-trip + a real 404 on a missing path —
proving the static-web-server actually serves the volume, not a 200-everything
fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>