component-sweep
Fast breadth-first TRIAGE across many Certus components — property inventory and spec-vs-code divergence list only, no proving. Produces the three property YAMLs per component (the same files `compone
SKILL.md.claude/skills/component-sweep/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>.
Why this exists, and what it deliberately is NOT
Verifying one component properly costs days, dominated by Kani. Measured on eviction-policy-optimized: a Kani stage of 5h17m, against a Creusot stage of minutes. Spending that per component before knowing which components matter is how a programme gets lost in its first few.
The key observation this skill is built on: every one of the 10 Certus defects found so far was first identified by the BLIND CODE EXTRACTION and the spec-vs-code reconciliation — Role 1. The provers then confirmed them and produced witnesses. Role 1 is agent work with essentially no prover time, so the defect-finding half of the value is available at a small fraction of the cost.
This skill is TRIAGE, not delivery. It is not a faster component-verify:
- it produces no proofs, so every tool column is empty and there is no scoring page — the deliverable renderer correctly refuses to emit one, and you must not hand-make it;
- a component is not verified because it has been swept. Saying so would be exactly the "not attempted ≠ a rating" error the pipeline exists to prevent;
- it writes nothing to any
verif/*branch and pushes nothing.
component-verify remains the only path to a verified component. Do not modify it, its Role-2
skills, or the gate to speed up a sweep. If a sweep needs something the full pipeline has, copy the
behaviour here; never weaken the gate.
What it produces, per component
Under <run-dir>/<component>/verif/ — exactly the files component-verify Step 1 produces, so a
sweep is a completed Step 1 and the full pipeline can pick up at Step 2 with no rework:
spec_properties.yaml,code_properties.yaml— the two blind extractionsunified_properties.yaml— reconciled, with emptycreusot: {}/kani: {}blocks
Plus, once per sweep, <run-dir>/DIVERGENCES.md: every origin: divergent obligation across all
components, with its spec pointer, code pointer and the disagreement in one line. This is the
output that earns the sweep — it is the ranked list of defect candidates.
Step 0 — cheap preflight, per component, FAIL FAST (seconds)
Do this before spending any agent time. Each check is seconds, and each one has cost a real run:
components/<c>/exists and holdssrc/*.rs.- Resolve the interface: the component's own
use interfaces::{…}andimpl I<X> for, then confirm thecomponent_macros::define_interface!block incomponents/interfaces/src/i<x>.rs. The filename is not always a transform of the component name.N= thefncount in that block. If you cannot locate the block, skip the component and say so — never guess. - Full
cargo metadatawith cwd =components/<c>/— not--no-deps, which returns 0 on the fault this catches. A component cargo cannot read is not swept; it is reported. - Note the spec of record (
components/<c>/specs/**/spec.mdor the interface doc if there is none). A component with no spec can still be swept — the extraction is then code-only and every property lands ascode-only, which is itself a finding worth reporting.
Print one line per component: <c>: interface I<X> N=<n> | spec <path|none> | cargo ok | SWEEP|SKIP(reason).
Step 1 — two BLIND extractions, then reconcile (per component)
🔴 The code agent MUST also read the declarations of every interface the component CONSUMES
Not the implementations — the declarations, as contracts. Without them the blind code extraction manufactures phantom divergences, in proportion to how much the component delegates.
Measured on memory-tier, 2026-09-30, and caught by the agent that had itself produced the sweep:
it reported the component's "sharpest divergence" as the spec requires eviction to untrack the victim;
the code never does. That is false. identify_next_to_evict is contracted at
interfaces/src/ieviction_policy.rs:101-104 to "remove it from tracking", and
eviction-policy-lru/src/lib.rs:133-138 implements it via pop_front() →
lru_list.rs:99-104 → remove(). The untracking happens INSIDE the callee. The code agent read only
memory-tier/src/** plus memory-tier's own interface, saw no ep.remove() beside the handle drop,
and inferred the victim stayed tracked. Reconciliation then promoted that gap to origin: divergent.
So the allow-list is: src/**, tests/**, the component's own i<component>.rs, and the
define_interface! declarations of every interface it imports (find them from the component's own
use interfaces::{…}). Still forbidden: those interfaces' implementations in other components, and
anything under specs/**.
This does not weaken blindness. A consumed interface's declaration is part of the contract the code is written against — the same artefact the spec agent is allowed to read for the method surface. What blindness protects is the separation of specification prose from implementation, not ignorance of the types a function is called through.
When reporting, say so: a divergence on a delegating call is suspect until the callee's contract has been read. On that one component the correction turned a claimed "coherent three-part defect" into one withdrawn, one that stands on its face, and one whose reachability turns on a separate invariant.
Same discipline as build-property-inventory; read that skill and follow it. The blindness is the
whole point and is not negotiable:
- the spec agent may read the spec bundle and the interface declaration, and must not open
src/**; - the code agent may read
src/**,tests/**and the interface declaration, and must not openspecs/**,README.md,info/— nor the other agent'sspec_properties.yaml, which will already be sitting in the same directory.
If either agent peeks, the divergence list — the only reason to run a sweep — becomes worthless.
Reconcile with the two-stage protocol (it is in build-property-inventory; use it, it was built
after two agents were lost to timeouts): digest both lists into one compact file, have the agent emit
an incremental JSONL pairing table appended after each subject group, then assemble
unified_properties.yaml deterministically with a script. Then run the mechanical completeness
check and FAIL on any violation:
- every spec id and every code id appears in exactly one
paired_from; - no id used twice, no id that exists in neither source;
paired + spec_only + code_only + divergent == counts.unified;- no record carries a non-empty
creusot:/kani:block or anystatus.
That last one matters beyond tidiness: a sweep that wrote a status would poison the gate when the component is later verified for real.
Step 2 — validate EARLY, not at the end
The rule this skill adds, learned from a 5-hour run whose setup was wrong from the first minute: check the result is sound in the first minutes, on a sample, before committing the batch. For a sweep that means running the completeness check on the FIRST component before launching the rest. If it fails there, the recipe is wrong and every later component would fail the same way.
Step 3 — the divergence report
Append to <run-dir>/DIVERGENCES.md, grouped by component, one line per divergent obligation:
<id> | <subject> | spec: <FR-…> | code: <file:line> | <what the spec promises vs what the code does>
Then a short ranking: which components have the most divergences, and which divergences share a root
cause across components. That cross-component pattern is the highest-value output a sweep can
produce — two independent implementations of IEvictionPolicy shared one handle-design flaw, which
pointed at the shared interface rather than at either implementation. A single-component run cannot
see that.
Step 4 — hand-off report
Per component: N, spec_total, code_total, M, and the split
paired/spec-only/code-only/divergent. Then, across the sweep: total divergences, shared root causes,
and a recommendation of which components to send through component-verify first and why.
State plainly, in the report: no component in this sweep is verified. The YAMLs are a completed Step 1 awaiting Role 2.
Anti-patterns
- ❌ Rendering a scoring page from a sweep. There are no proofs; the page would be all-pending and the renderer refuses it. Do not hand-make one.
- ❌ Writing any
status,symbol,evidenceor fidelity. Scorers own those, and a swept YAML is a future gate's input. - ❌ Committing sweep output to a
verif/*branch, or pushing anything. - ❌ Calling a swept component "verified", or counting it in a verified total.
- ❌ Letting an extraction agent see the other source to "improve" the pairing.
- ❌ Weakening
component-verify, a Role-2 skill, or the gate so a sweep runs faster.