simulation-plan · git:20260904.b0fbf80 · 2026-09-04 · sha256 c556b7d8e0314dff
simulation-plan git:20260904.b0fbf80A
Immutable. This exact content is served forever at /api/v1/blob/c556b7d8e0314dff.
---
name: simulation-plan
description: Use when generating or evolving the verification plan, scaffold specification, testpoints, and power scenarios for a module; not for materializing UVM TB or running EDA tools.
---
# Verification Planning
Your sole responsibility: from `specification`, produce `verification-plan.md` — the human-readable
review anchor — plus its machine half, three sidecars split so each downstream consumer declares
only what it reads.
## Iron Rule
- Write only under `{workdir}`; never another module's artifacts.
- Do not read RTL source, do not invoke EDA tools. A plan written from the implementation verifies
the implementation against itself.
## Artifacts
`<skill>` is this skill's own base directory, named on the first line of this file.
Read `{workdir}/dispatch.json` for this round's inputs: its `inputs` table maps each upstream key
to a location, so `<key>/<subpath>` is how you address one. `design` / `manifest` / `children` /
`clocks` / `requirements` / `check_hints` / `top_io` all resolve to the specification stage root.
| Path | What it is |
|---|---|
| `<design>/design.md` | §1 behavior, §1.4 boundary, and §1.5 timing scenarios with their waveforms — the only home for the scenarios you author sequences from, so you read it |
| `<children>/<child>.md` | Per-child implementation constraints a testpoint may have to verify: register side effects, exceptions, concurrency, back-pressure, reset, state-machine boundaries |
| `<requirements>/requirements.json` | The engineer's requirements, one row each with the stage that judges it. The rows judged by simulation are what the testpoints exist to establish; the rows judged by simulation-plan are requirements on this plan itself: a stimulus distribution, a seed, a scope |
| `<intent>/` | The intent tree: the engineer's container — `brainstorm.md` plus whatever they delivered with it. Open a file here only when a requirements row points at it, and read it there rather than from any copy |
| `<check_hints>/` (per child, one file) | Per child, how simulation observes each requirement row it realizes — `check_id` is unique across all of them, and each names the rows it establishes |
| `<design>/clocks.json`, `<design>/top-io.json` | The clock and the DUT boundary `materialize-scaffold` derives from |
| `<manifest>/manifest.json` | `.module` is the Top field in plan §1; `children[]` is the roster the check hints are aggregated over |
Everything below is produced under `{workdir}`.
| Path | What it is |
|---|---|
| `verification-plan.md` | The review anchor the human gate is held over (template: `references/verification-plan-template.md`) |
| `tb-scaffold.json` | What simulation builds the TB from: `agents` / `tests` / `testpoints[]` / `rm` / `scoreboard` / `skipped_checks[]` |
| `sequences.json` | The sequence roster — the one part both simulation and power-analysis read |
| `power-scenarios.json` | The power scenarios, read by power-analysis alone. Its own file so a scenario-only edit does not invalidate simulation's proof |
| `plan-review/review.md`, `plan-review/decisions.md` | The reviewer's findings, and the user's resolution of anything it called blocking |
| `result.json` | The status envelope |
Each sidecar's shape, and per field whether it is yours to author or script-injected, is
[`references/tb-scaffold.schema.json`](references/tb-scaffold.schema.json) /
[`sequences.schema.json`](references/sequences.schema.json) /
[`power-scenarios.schema.json`](references/power-scenarios.schema.json). Read them before authoring:
they carry the judgment no validator can express, and `simplan check-scaffold` enforces the rest.
## Workflow
Two phases, each closed by its own gate, then finalize.
### Which round is this
The kernel writes `scope` / `caused_by` / `reasons` into `dispatch.json` **only when they carry
something**, so their presence is what tells you. Either way you run both phases; the branch
decides how much of the plan you touch.
- **`caused_by` present — failures are waiting on this stage.** Each entry is one failing run's own `result.json`, and a round scheduled for some other reason carries them too: answer them in this round. Scope is the union of `dispatch.json`'s `scope` — the
module-relative inputs whose change invalidated the plan — and what the
`caused_by` records attribute; read each envelope once, and amend only the plan sections those
paths map to. It is a pointer, not a boundary: if the gap sits elsewhere, widen and record why in
`result.json`. `reasons`, when present, is a human's judgment on this repair and outranks your own
reading of the files.
- **`caused_by` absent — a first delivery.** Generate the plan and all three sidecars.
Your previous round, if any, is already in `{workdir}`: edit it in place, touching only what this
round requires. Rewriting a sidecar this round did not change still changes its bytes, and
`simulation` and `power-analysis` both declare those files as inputs, so a cosmetic rewrite costs a
full TB recompile and regression downstream for no change in content.
A `{workdir}` already holding part of a round means the session was compacted or interrupted: that
work is yours to continue or redo, and artifacts on disk are not a gate you already passed.
### Author the plan and its sidecars
Author the judgment fields into the three sidecars and write `verification-plan.md` per
[`references/verification-plan-template.md`](references/verification-plan-template.md). How the spec
fields map to the scaffold objects — agents from interface groups, sequences from §1.5 scenarios,
tests from the requirements, RM / scoreboard from the check hints — is
[`references/spec-input-contract.md`](references/spec-input-contract.md).
Every `check_hints[]` check_id must end up in some `testpoints[].covers[]` or in `skipped_checks[]`
with a reason; the gate below enforces that matrix. Power scenarios come from
[`references/power-scenarios-template.md`](references/power-scenarios-template.md) — load the table
before authoring, because no gate checks that a scenario came from the standard set. Every
`sequence_ref` must resolve to a `sequences[].name`; add the `sequences[]` entry first when a
scenario needs its own stimulus.
When amending, keep testpoint IDs / sequence names / `sequence_ref` stable: downstream coverage /
scaffold / SAIF caches key off them, so renumbering one silently breaks the cache. One amendment is
counter-intuitive enough to name: when triage attributes a coverage hole to plan over-spec — a bin
the RTL cannot legally reach — **narrow** `bins` rather than chasing the hole, and delete the
testpoint outright if the whole thing is unreachable, recording the over-spec attribution in §5.
Coverage does not fall; a bin that could never be hit was never coverage.
**Gate, script.** Run `materialize-scaffold` to fill the script-injected fields, then
`check-scaffold` to validate the sidecars' structure, semantics, and coverage matrix:
```bash
python3 <skill>/scripts/simplan/__main__.py materialize-scaffold --plan {workdir} --spec <design>
python3 <skill>/scripts/simplan/__main__.py check-scaffold --plan {workdir} --spec <design>
```
Both run on every pass. Each names the defect on stderr; `materialize-scaffold` also names the
upstream stage to re-run when what is missing is the spec's. Fix and re-run until clean, or finalize
a failure when the defect is not yours to fix.
### Review the plan
Dispatch ONE Level-1 reviewer per
[`references/plan-review-task-contract.md`](references/plan-review-task-contract.md), passing paths.
It writes its own `{workdir}/plan-review/review.md`. After dispatching, send a brief status and end
the turn; reap before proceeding. A `STATUS: BLOCKED` reviewer is a crash, not a verdict: the review
did not happen, so finalize a failure with that as the reason.
**Gate, human.** Path-handoff: present the `verification-plan.md` path and the
`plan-review/review.md` path, echoing no body.
You do not summarize the findings, rank them, or decide which ones matter: a review relayed through
your summary is your judgment wearing the reviewer's name.
Then the user approves, requests changes, or rejects:
- **approve**: if the user accepts a finding the reviewer called blocking, write their reason —
**their words, not yours** — to `{workdir}/plan-review/decisions.md`, so the override travels
with the review it overrode instead of living only in this session. Nothing downstream re-checks
testpoint-vs-spec (sim conformance judges TB-vs-testpoint), so an accepted coverage gap is a
terminal accept.
- **request changes**: revise incrementally, re-run the script gate, re-dispatch the reviewer,
re-present.
- **reject**: finalize with `--status fail` and no `--fail-reason`, which records
`fail_reason="user rejected plan"`.
### Finalize
Every run ends here, including one you could not carry to the human gate:
```bash
python3 <skill>/scripts/simplan/__main__.py finalize \
--workdir {workdir} --spec <design> \
[--status fail] [--fail-reason "<one-line reason>"] [--fix-owner <rule>] \
[--revision '<one-line revision narrative>']
```
You supply only the human-gate outcome, plus `--revision` on a scoped revision. On a failure name
the owner: `--fix-owner specification` when the spec does not say enough for any plan to cover the
gap, `--fix-owner simulation-plan` when the gap is yours but you have exhausted what you can do
from here, and omit the flag when you cannot tell.
finalize re-runs `check-scaffold` in-process on the pass path — it was clean at the script gate, so
a failure now means an artifact was edited afterwards, which is BLOCKED rather than a routable fail.
Exit 0 = `result.json` written, status pass or fail. A non-zero exit is a program exception:
BLOCKED, reason on stderr, never a `status=fail`.
## Return Contract
Control returns to the caller, which decides what runs next from `result.json`.
Your sole completion signal is `{workdir}/result.json` present with `status=pass`.