# Blast Estimate

> Partial Skill: invoke by name only — blast-estimate's engine that computes a Mission's blast (low/medium/high) from its touch-set plus the project corpus and lines it up against the hand-typed declared blast — the engine for the blast-estimate node, consumed by the mission-graph's writer. Not triggered by users directly.

- Skill: `cyberuni/blast-estimate` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add cyberuni/blast-estimate`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cyberuni/blast-estimate/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: cyberuni (https://skillmd.com/u/cyberuni)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cyberuni/blast-estimate

---


# Blast Estimate

The concrete engine for **blast-estimate** — a read-only derivation that works out how much of the
project a Mission could disturb, instead of trusting the hand-typed guess. Given a Mission's
**touch-set** (the work areas it names) and the project corpus, it computes a **blast** level
(`low` / `medium` / `high`) from three measured inputs and lines that level up against the Mission's
hand-typed **declared** blast (`agrees` / `under-called` / `over-called`). The `blast-estimate` node
of the SDD project spec (repo-only) carries the full contract and vocabulary.

## The three inputs

- **count** — how many of the touch-set's areas resolve to a known work area in the corpus, plus
  **coverage**: whether the touch-set covers *every* work area of a touched project. Together these
  are **breadth** — absolute reach and relative reach.
- **centrality** (dependency fan-in) — for each resolved area, how many *other* work areas hold at
  least one file that **references** it, measured across the area's **full root set** —
  implementation files count exactly as spec prose does. The `max` across the resolved areas is used.
  See *The reference matcher* below: a bare id alone is **not** how real prose references an area.
- **sensitivity** — whether a resolved area is **marked** in the opt-in
  `.agents/sdd/sensitive-paths.toml` — a `sensitive = [ "id", ... ]` string array, parsed
  line-anchored and lenient exactly as `manage-spec-anchors` parses `anchors = [ … ]`, so a leading
  comment, a trailing comment, or a neighbouring key are all ordinary TOML and parse. A file with no
  `sensitive` array at all is malformed and fails loud. **Only an absent file (`ENOENT`) is benign** —
  no area is sensitive, not an error. **Every other read failure fails loud** — unparseable,
  unreadable (permissions), or not a regular file — returning `computed: null` and an `error` naming
  the file. The narrow ENOENT test is deliberate: swallowing the rest into "nothing marked" fails in
  the **dangerous direction**, silently under-calling blast on exactly the areas a project marked as
  needing care. An unreadable marking is not evidence of no markings.

The arithmetic is bucketed and deliberately simple (the rubric fixes only the ordering properties,
not the exact numbers):

- **breadth** = `max(countScore, coverageScore)` — reach measured two ways, whichever says more:
  - `countScore`: 1→0, 2-3→1, 4+→3 (absolute reach — touching many areas is broad even in a large
    project the touch-set nowhere near covers)
  - `coverageScore`: 3 iff the touch-set covers **every** work area of a touched project holding
    **≥2** work areas, else 0 (relative reach — a 3-area project touched entirely *is* project-wide;
    the barrier agreement must hold at every project size **≥2**, not only at 4+)
- **centrality**: 0→0, 1-2→1, 3-6→2, 7+→3 (a genuine hub: touching an area a large share of the
  project leans on is project-scale reach even at count 1). Calibrated against **real** fan-in, which
  spans roughly 0–17 on a corpus of this size.
- **sensitivity**: any marked area →+2

Sum the three, then `score>=3` → `high`, `score>=1` → `medium`, `score==0` → `low`. A touch-set that
resolves to **zero** known areas (including the empty touch-set) computes `unknown` — never `low`.

The **≥2 work areas** guard on coverage is load-bearing, and it is keyed per **project**, not per
corpus: a 1-area project inside a larger multi-project corpus is still never project-wide. The
contract states the precondition itself — the project-wide scenario requires a project holding more
than one work area, and *"a lone work area is its whole project but is not project-wide reach"* pins
the 1-area answer to `low` **for a lone area with no fan-in and no marking**. Coverage is reach
**relative** to a project, and a project of one has none to cover, so coverage never fires there and
**breadth** rests on absolute count alone — centrality and sensitivity still score, so a lone area
that is central or marked computes `medium` or `high`.

Two exclusions are structural, not just documented: there is no compatibility/breaking-change input
at all (no seam for it to enter the score), and centrality is **measured** fan-in only — a work
area's name never enters the computation.

## Work-area recovery — declared layouts, never a path shape

Node recovery is **not this engine's to invent**: it reuses `touch-set-correction`'s pure
**`fileToNode(path, layouts)`** over `discoverLayouts`' declared `ProjectLayout[]` (the same
cross-skill reuse `collision-ladder` does). This is load-bearing — a work area spans **multiple
declared roots**, a spec root *and* an impl root:

```
project "sdd" -> roots [".agents/specs/sdd", "plugins/sdd/skills"]
  .agents/specs/sdd/mission-graph/README.md  -> sdd/mission-graph
  plugins/sdd/skills/mission-graph/SKILL.md  -> sdd/mission-graph   # SAME node, two roots
```

A node's file set is every file under **any** of its roots, so fan-in measures the project's real
lean on an area rather than spec-prose cross-reference. `estimateBlast` takes layouts **injected**
(`fileToNode` is pure, so tests construct them as fixtures); the CLI sources them from
`discoverLayouts`, overridable with `--layout`.

## The reference matcher

Fan-in counts the forms the corpus **actually uses** to name an area — a bare id is what a touch-set
and the ledger write, not how prose references anything. For node `<p>/<cap>`:

| form | example |
|---|---|
| bare id | `sdd/spec-gate` |
| skill-style ref | `sdd:spec-gate` |
| path under any **declared root**, at any depth | `plugins/sdd/skills/spec-gate/`, `.agents/specs/sdd/authoring/spec-gate/` |
| relative sibling link (**same project only**) | `../spec-gate/`, `../../authoring/spec-gate/` |

Path forms are **derived from the project's declared roots**, never hardcoded, so a re-rooted project
keeps working; the any-depth segment matters because a node's spec can sit nested under its root. A
relative link carries no project, so it is only counted within the same project — otherwise two
projects sharing a capability name (`manage`, `design`) would cross-credit each other.

This stays **mention-based and cheap** by design. Real produced/consumed symbol dependency is
`ssa-lowering` / `collision-ladder` territory — the boundary is deliberate. Only the resolved areas
are scored, so a one-area touch-set costs one matcher pass over the corpus rather than one per area.

## Run it

```bash
node "<skill>/scripts/blast-estimate.mts" --root <repo> --touch-set a,b,c \
  [--layout 'sdd:.agents/specs/sdd,plugins/sdd/skills']... \
  [--declared low|medium|high|unknown] [--format toon|json]
```

- `--root` defaults to `.` (the repo root the layouts' roots and
  `.agents/sdd/sensitive-paths.toml` resolve against).
- `--layout` is repeatable (`<project>:<root1>,<root2>`, the same shape `touch-set-correction`
  accepts). Omit it to auto-discover via `discover-specs`.
- `--touch-set` is a comma-separated list of `project/capability` ids.
- `--declared` is optional; omitted or `unknown` lines up as `no-declared` — the computed level still
  returns on its own.
- Default output is **TOON**; `--format json` emits the full `EstimateResult`: `resolved`,
  `unresolved` (surfaced, never dropped), `computed`, `reasons` (`count`, `maxFanIn`,
  `sensitiveAreas`, `projectWide`), and `lineUp`.

## Boundaries

Read-only: it composes a corpus scan and the touch-set it is handed, and **returns** an estimate — it
writes no file and mutates no store. It does **not** judge compatibility/breakage (a separate
dimension), rank by surface location, decide HITL vs AFK, render a self-clear-or-escalate verdict (it
only *modulates* a conductor's judgment), *produce* a touch-set (it consumes one — pre-work declared,
or `touch-set-correction`'s post-work corrected one), or **reimplement node recovery** (it imports
`fileToNode`; it never reinvents or shells out to it).

