tools-aggregate-coverage-by-interface
Render the ONE combined per-component scoring page (`<component>_scoring.html`) from the reconciled `unified_properties.yaml` — per-tool KPI cards, a combined Creusot|Kani scorecard over the public-me
Files1
View on GitHubSKILL.md.claude/skills/tools-aggregate-coverage-by-interface/SKILL.md
GitHubFrozen 2026-10-09. This skill moved to ai-native-storage-workbench (
fv/skills/). Edit it there; this copy is not updated. Install the current version withfv/install.sh <this-repo>.
Purpose
Given a component whose verifiable properties are extracted, agreed (Role 1), and proved/attempted
(Role 2) — all captured in unified_properties.yaml — produce one presentation-ready scoring page whose
scorecard aggregation unit is fixed: the public methods of the I<component> trait (the
define_interface! block). The page folds the earlier three thin slides into a single superset:
- the scorecard ("How many public methods did we verify?") — one row per public method,
Creusot | Kani, headline "Out of N public methods, Creusot proved X and Kani proved Y"; - the bundle view ("to verify a method, we verify every property in its bundle") — per-
idrows; - the assumptions / spec-vs-tool view — what is proved vs. proved-against-an-abstraction (
★), where the Creusot-ghost-mirror ↔ Kani-real-type distinction is disclosed from each property'sfidelity.
This skill does not extract properties and does not run a prover. It renders unified_properties.yaml.
An incomplete input (some properties not yet scored — their tool blocks carry no status key) is
not an error: render it, mark the unscored cells pending ·, and show a prominent INCOMPLETE
banner. Only a structurally inconsistent input — a tool block citing an id absent from the property
set, or a scored value that contradicts the shared obligation — makes it stop and ask for reconciliation.
This lets a dry run or a partial (one-tool, mid-flight) run render honestly without crashing and without
overclaiming.
The model (match the deck exactly)
- Unit of coverage = public method. The scorecard denominator is the number of public methods
(
counts.methods, e.g. 16/20, 12/20), NOT a property count. - A method is
✓for a tool when every property in its bundle reaches a terminal covered state and that tool proved (✓/★) at least one of them;–when every property is scored but the tool proved nothing for it, or a scored proof-lane property of its bundle is⊘; and·(pending) when any bundle property is not yet scored. (Full rule under Method verdict.) - Terminal covered states come from
unified_properties.yaml:proved(✓),proved-against-abstraction(★— ghost-mirror / trusted-boundary), ordelegated(⤴— owned by another component, or covered by a non-proof lane such as Loom/Spin concurrency or a targeted fault-injection/hardware test named in the property'snote). A⤴-delegated property does not block a proof tool's✓iff its delegation names a real owner/test — exactly ascreate_memory_tier_entrystays✓for Creusot while its SSD-atomicity property is delegated to a targeted test. - A Creusot/Kani-lane property that is a scored
⊘(tool boundary), or still pending (unscored), DOES block that method's✓. A scored-⊘bundle is unfinished, reported honestly as–; a bundle with any unscored property is pending, reported as·— never relabel either one covered. - Footnotes (keep them):
*= this tool is the better-fit prover for the method;**= the method's logic lives in other components (those rows are– / –). - The scorecard is two terminal states
✓/–plus a pending marker·— no◐.✓and–are the only scored verdicts (★still reads as✓at the method level; the abstraction/fidelity detail surfaces in the bundle & spec-vs-tool sections, never as a third scored state).·is not a third verdict — it is the "not yet scored" placeholder for a method whose bundle still has properties the scorer hasn't reached.·never counts toward✓and never toward the definitive–; it just means the run is incomplete for that cell. A fully scored run shows no·. - Display grouping by capability area is allowed (Reference-counting, Tier-transitions, …) — it is
visual only. The counting unit stays the public method. The renderer keeps one column per tool in
counts.tools, so adding Loom/Spin later is a column, not a rewrite.
Inputs (consume; do NOT re-extract, do NOT re-bundle)
Primary input: components/<component>/verif/unified_properties.yaml — the reconciled single source of truth
Role 1 built and Role 2 filled in. It carries, per property: the id-keyed obligation with its methods
bundle, object_fn, kind, global, attachments, origin, source, statement (owned by Role 1),
and the per-tool creusot:/kani: blocks (status, symbol, fidelity, evidence, miss_class,
note) written by Role 2. It also carries counts: (N methods, spec_total, code_total, unified M,
attachments, not_verifiable) and the NV-* ledger. This skill renders from unified_properties.yaml; it does not
re-derive any of those fields. There is no .md backing file — this HTML is the only human-readable
form, and every value it shows comes from unified_properties.yaml.
Two distinct conditions, two distinct responses:
- Not-yet-scored (pending) is normal, render it. A tool block with no
statuskey means the scorer has not reached that property. Render it as pending·and raise the INCOMPLETE banner — do not stop. - Structural inconsistency STOPS. A
creusot:/kani:block that cites anidabsent from the property set, or a scored status that disagrees with the shared obligation, is a real defect — STOP and flag it; never paper over divergence, invent a property, or re-bundle. Reconciliation of the property set already happened in Role 1; the status is written by the scorer, and a run where the scorer has not yet covered every property is a legitimate partial render, not a failure.
The property record (all fields owned upstream; this skill renders, it does not overlay)
Every field comes from unified_properties.yaml — read, never re-derive. From Role 1:
id, subject, methods (the bundle), object_fn, kind, global, attachments, origin, source,
statement. The per-tool creusot:/kani: block has two classes of field with two different writers
under the reproduction gate — the renderer must respect the split:
# SCORER-WRITTEN (by scorer_{creusot,kani}.py, by reproducing the proof — never by an agent):
status: proved | delegated | tool-boundary # NOTE: no "not-attempted" — see below
symbol: "✓" | "★" | "⤴" | "⊘" # ✓ native · ★ ghost-mirror/trusted-boundary · ⤴ delegated · ⊘ tool boundary
evidence: {vcs, coma, wall_clock_s, peak_rss_mb} (Creusot) | {harness, result, unwind, wall_clock_s, peak_rss_mb} (Kani)
_scored_by: scorer_creusot | scorer_kani # provenance stamp the scorer leaves
# AGENT-SUPPLIED (advisory only; Role 2 may fill; the renderer shows them but they never set the verdict):
fidelity: <real-type | ghost-mirror | trusted-boundary> (Creusot) | <real-type-bounded | representative | arithmetic-core> (Kani)
miss_class: TOOL | AGENT | null
note: <what the proof actually covers>
delegate_to: <the owning component/test, when the agent proposes a delegation>
Key-absence is the pending state. A tool block with no status key (the null-placeholder Role 1
wrote, {evidence: null, fidelity: null, note: null}) means the scorer has not scored this property yet —
render it as pending ·, never as covered and never as a failure. There is no not-attempted status
anymore; "not yet scored" is signalled by the absence of status. This skill computes only the derived
rollups (per-method verdicts, counts, headlines) from status/symbol; it writes nothing back into
unified_properties.yaml.
Bundle ownership (read from the inventory — do not recompute)
- The
methodsbundle of each property, the per-method bundle sizes, and both counts (distinct properties M, method→property attachments) are already computed in the inventory. Read them. - A shared-helper property (e.g.
align_up) is already attached to every dependent method there — carry that through unchanged; it is expected for one property to sit in many bundles. - The counting unit stays the public method. If you feel a need to reassign an owner, that is an inventory fix (Role 1), not something this skill does silently.
Method verdict (per tool)
For public method m and tool T (Creusot or Kani), reading each property's T block from unified_properties.yaml.
First classify each property in m's bundle for T: scored (has a status) or pending (no
status key yet). Then:
·(pending) if any property inm's bundle is still pending forT— the run has not finished scoring this method, so no terminal verdict is honest yet. Pending dominates: never resolve a method to✓or–while a bundle property is unscored.✓if every property inm's bundle is scored to a terminal covered state — proved (✓/★) by some tool, ordelegated(⤴) across a real interface boundary — andTitself proved (✓/★) at least one ofm's properties.–otherwise: every property is scored (none pending) butTproved nothing form, or some property ofm's bundle is a scoredtool-boundary(⊘). A⊘proof-lane property blocks the method's✓— report it honestly, never relabel; and never render a scored–for a method that is really still pending.
Component score: Creusot X / N, Kani Y / N, where N = number of public methods (counts.methods).
When any method is pending, also report the pending count and keep the INCOMPLETE banner up — X and Y
count only methods that are fully scored to ✓, never pending ones.
Also report the net: methods whose whole bundle is covered (done) vs incomplete. ★ counts toward
✓ at the method level but its fidelity (ghost-mirror / trusted-boundary) is disclosed in the scorecard
and the assumptions/spec-vs-tool sections — never hidden.
Required output — ONE combined scoring page
Emit a single self-contained <component>_scoring.html rendered from unified_properties.yaml — the superset
page that replaces the old three thin slides (8/9/11). One page per component, extensible to more tool
columns (Loom/Spin can be added one at a time later — the renderer is N-tool-ready, not hard-wired to two).
Sections, in order (this mirrors the hand-curated SEPT_2026/SEPT_14/*_scoring.html reference layout):
- Title + sub-line — interface, N,
.coma/VC totals (Creusot), Kani harness count, run pin + date. - Banner callout — the three end-states (Proved / Delegated ⤴ / Tool-boundary ⊘) and the anti-vacuity note.
- FROM_SPEC callout — links
spec_properties.yaml/code_properties.yaml, statesspec_total/code_total/ unifiedM, and explains they differ from M (spec∩code). - Per-tool KPI cards — Creusot and Kani each:
X/Nmethods, VCs/.coma(Creusot) or harness count (Kani), wall-clock + peak RSS. - Combined scorecard — the one table with columns Props | Method | Creusot | Kani | Boundaries/notes;
per-method rows grouped by capability area; per-tool cell shows the method verdict symbol;
*/**footnotes (*= better-fit prover;**= logic lives in other components →–/–). - Property-bundles table — Property | What it requires | Creusot | Kani | Owner/note, per
id, each tool cell showing itssymboland (on hover/'★') itsfidelity. - Spec-vs-each-tool table — Spec property | What Kani proves | What Creusot proves | The weakening —
this is where the abstraction↔real finding surfaces from the
fidelityfields (Creusot ghost-mirror/★ vs Kani real-type-bounded). - Unproved rows and their causes — narrative over the
⊘properties with theirmiss_class(TOOL vs AGENT) and reproducible signatures fromnote/evidence. - Measurement table — Tool/run | Wall-clock | Peak RSS | Discharged (VCs/
.comaor harnesses). - Anti-vacuity statement and Provenance (pin, sources, generator).
HTML must be self-contained and theme-aware; deliverables are read in-repo and may be cut onto plain
white slides (keep backgrounds light, avoid heavy colored bands). Reference implementation: the
hand-curated SEPT_2026/SEPT_14/<component>_scoring.html.
(Optional) if a machine-readable rollup is wanted, it is already unified_properties.yaml — do not re-emit a
hand-formatted .md rollup; the YAML is the structured artifact and the HTML is the presentation.
Honesty rules (non-negotiable)
- A method is
✓only when its whole bundle is covered. A scored⊘proof-lane property makes the method–; a still-pending (unscored) one makes it·. Either way it is not✓— do not inflate the method count by ignoring unfinished or unscored properties. - N = number of public methods (
counts.methods), fixed by the trait. Never shrink N to improve the ratio. - Every
provedclaim traces to a real artifact (VC/.comaor harness) via the propertyidandevidence. ★is disclosed, not hidden. A method whose✓rests on a ghost-mirror/trusted-boundary property still shows✓at the method level, but the scorecard/bundle/spec-vs-tool sections must surface thefidelityso the abstraction is visible. Under-claim, never over-claim.
Procedure
- Read
unified_properties.yaml: the public methods, N (counts.methods), themethodsbundles, and each property'screusot:/kani:blocks (do not recompute any Role-1/Role-2 field). - Consistency gate (not a completeness gate): stop and flag ONLY on a structural defect — a tool block
citing an
idabsent from the property set, or a scored status contradicting the shared obligation. Properties with nostatuskey are pending, not errors — render them·and raise the INCOMPLETE banner; do not stop. (A dry run or a one-tool partial run is expected to be all- or mostly-pending.) - Compute each method's
✓/–/·per tool (verdict rule above — pending dominates), the score X/N and Y/N over fully-scored methods, the pending count, and the done/incomplete split. Pullfidelity,evidence,miss_class, andnotestraight through for display. - Render the single
<component>_scoring.html(sections above) fromunified_properties.yaml. - Report:
<component> — Creusot X/N, Kani Y/N public methods proved; Z methods have complete bundles, P methods still pending (unscored), (N−Z−P) scored-but-incomplete (list the blocking ⊘ properties with their signatures). If P > 0, say so plainly — the run is a partial render, not a final score.
Anti-patterns (the rabbit holes)
- ❌ Counting properties as the scorecard denominator. (Denominator = public methods.)
- ❌ Using
◐on the scorecard. (Scored verdicts are two-state ✓/–;★/fidelity detail lives in the bundle & spec-vs-tool sections. Pending·is legitimate but is not a verdict — it marks a not-yet-scored cell.) - ❌ Marking a method
✓while a proof-lane property is still⊘, or resolving it to✓/–while any bundle property is still pending (unscored). Pending dominates — render·and keep the banner up. - ❌ Treating a not-yet-scored property (no
statuskey) as a failure and STOPping. Absence ofstatusis pending, not an error; only a structural inconsistency stops the render. - ❌ Re-extracting, re-bundling, or re-deriving status. (Everything comes from
unified_properties.yaml.) - ❌ Emitting the old three thin slide files or a hand-formatted
.mdrollup. (One combined HTML; YAML is the data.) - ❌ Guessing a round property/method count to fit a table.
Routing
General methodology → unstable (promote via PR; don't leave siloed on a verif branch).
Deliverables (spec_properties.yaml, code_properties.yaml, unified_properties.yaml, <component>_scoring.html) → written under
formal-verification/<component>/ and shipped to unstable via PR. Under the component-verify
orchestrator, the orchestrator performs that commit/PR (gated to formal-verification/<component>/); run
standalone, open the PR yourself. No Box, no scp — the repo folder is the shareable home.