lint-cdc · diff
git:20260910.1ea7ef3 to git:20260919.cdf15ac
31 added, 127 removed. Audit A to A.
---
name: lint-cdc
- description: Use when running SpyGlass lint or CDC checks, analyzing violations, adding waivers, supplementing SGDC annotations, or re-running after RTL changes; not for synthesis or simulation.
+ description: Check RTL lint and clock-domain crossings with SpyGlass, evaluate violations and justified waivers, and close the current assessment.
---
- # Lint / CDC
-
- Your sole responsibility: converge SpyGlass lint and CDC to a clean error and warning count on
- this module's RTL, and close the run through the `lintcdc` CLI.
-
- ## Iron Rule
-
- - Write only under `{workdir}`. Every injected input location is read-only, as is every other
- stage's output.
- - **Scripts are black boxes, never Read their source.** Invoke them per this skill's documented
- command lines (flags via `--help`); on a non-zero exit act on the documented failure protocol
- (stderr, stdout verdict), not the source. Sole exception: debugging a suspected bug in a
- script itself.
-
- ## What you read, and what you edit
-
- `<skill>` is this skill's own base directory, named on the first line of this file.
-
- `{workdir}/dispatch.json` carries the `inputs` table below, so `<key>` denotes a location and
- you read `<key>/<subpath>`. It also carries a `scope` list when the kernel knows which inputs
- changed since your last run: that narrows what you triage, never what the tool analyzes.
-
- | Path | Use |
- |---|---|
- | `<annotations>/constraint-annotations.json` | The `sgdc` block per child: every depth annotation this RTL implies. `bootstrap` renders all four categories into the SGDC itself — you never transcribe them. Nothing upstream matched a name against the netlist, so a name SpyGlass cannot find surfaces here first, as its author's defect. |
- | `<rtl>/rtl-files.json` | Per-child file layout, which `bootstrap` turns into `scripts/filelist.txt`. |
- | `<sgdc_seed>/constraints/<TOP>.sgdc` | Clocks and resets from specification. `bootstrap` reads it every round, so a correction here arrives on its own; it is not yours to restate or override. |
- | `<requirements>/requirements.json` | The engineer's requirements, one row each with the stage that judges it. The rows judged by `lint-cdc` are yours: a bar on the counts, a rule about what may be waived, a clock-domain fact the analysis must confirm. A row that says zero warnings means zero, not zero after waivers. You declare a verdict on each through `finalize`. |
- | `<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 |
-
- Two files under `{workdir}` are yours, and both reach you holding the previous round's work
- rather than a pristine template:
-
- - `scripts/local.sgdc`: your own SGDC — the port/clock associations the seed cannot know.
- - `scripts/waiver.tcl`: the waivers, each carrying its reason.
-
- `scripts/constraints.sgdc` is generated every round from the seed and annotations. SpyGlass
- reads it, then `scripts/local.sgdc`, on every run. Edit the local file; corrections to the seed
- or annotations belong to their authors, and step 4 routes them there.
+ # Lint and CDC
- Everything else under `{workdir}` is produced by the tools you invoke, and `finalize`
- enumerates it into `artifacts[]` for you.
+ Assess the RTL against the task's lint and CDC requirements. Read `dispatch.json`, original intent,
+ requirements, RTL, constraint annotations and specification SGDC. Use carried evidence when its
+ implementation, setup and scope still apply; choose the checks the current task needs.
- ## Workflow
+ Write under `{workdir}`; upstream inputs remain read-only. The framework carries published setup
+ and reports into this directory, without the old verdict. Keep useful analysis in `evidence/`.
- ### 1. Deploy
+ ## Prepare and check
- Run `bootstrap` to lay down the run scaffold:
+ `<skill>` is this skill's directory:
```bash
- python3 <skill>/scripts/lintcdc/__main__.py bootstrap --workdir {workdir} [--top <TOP>]
+ python3 <skill>/scripts/lintcdc/__main__.py bootstrap --workdir {workdir}
```
- It deploys NO-CLOBBER so your two files survive, substitutes the `MY_TOP` placeholder, and
- assembles `scripts/constraints.sgdc` from the specification seed and generated annotations,
- preserving your `scripts/local.sgdc`. It aborts when `{workdir}/Makefile` already exists (the kernel-written
- `dispatch.json` does not count as "deployed"), when the seed does not resolve, and when the
- annotations sidecar is unreadable — the first two would leave SpyGlass analysing a design with no
- clock declared, which it reports as a clean run. It reads the top-module name from
- `manifest.module` when `--top` is omitted. Non-zero exit: stderr names the cause. `make` is the
- interface to everything it deployed.
-
- ### 2. Constrain
-
- The seed and the annotations are already in the assembled SGDC — you write neither. What is left
- is what only this stage can know, into `scripts/local.sgdc`: the `abstract_port` associations
- that bind a reset to the domain it resets and an input to its driving domain, without which CDC
- cannot see the driver side of a crossing.
-
- The seed's clock/reset block is not yours to override, and an annotation you disagree with is not
- yours to correct. Both belong to whoever declared them, and step 4 routes a failure there — a
- correction you make locally would leave that author's file wrong and synthesis reading it.
-
- ### 3. Converge
-
- Run `make all` for a first pass, which shares one `elaborate` across both goals, or `make lint`
- then `make cdc` when you want `set_case_analysis` settled before CDC runs. Each target ends in
- `collect_report.py`, which writes `<kind>-report.txt` for a human and `<kind>-violations.json`
- carrying `counts` plus one row per message.
-
- Triage every `severity=error` and `severity=warning` row in both sidecars. The annotations
- already suppressed the false-positive classes their authors declared, so a row that survived is
- one of three things:
-
- - **Acceptable anyway** → a `waive` in `scripts/waiver.tcl` carrying `-comment "<why>"`, then
- re-run that check to confirm it took. SpyGlass subtracts a waived message before anything
- counts it, so that text is the only surviving record of what you let through, and `finalize`
- BLOCKS on an entry without one.
- - **A real defect, or an annotation its author never declared** → leave it. This run fails and
- step 4 attributes it.
- - **An annotation that names something SpyGlass cannot find** (`checkSGDC_existence`, an
- `SGDC_*` Fatal) → also leave it. The name came verbatim from the sidecar, so the sidecar is
- wrong and rtl-design owns it. Do not repair it in `local.sgdc`: that hides the defect and
- leaves synthesis reading the same bad file.
-
- Re-review inherited waivers on any run whose RTL changed: an entry written against an old
- finding silently swallows a new same-rule violation. Scope each as narrowly as the rule allows,
- anchoring to the design unit or instance rather than a bare rule id, and delete entries whose
- original finding is gone.
-
- A non-zero `make` ends the run with nothing to triage, so go straight to step 4 carrying the
- cause from `collect_report.py`'s stderr. Never read success from a `*-violations.json` being
- present: when SpyGlass itself fatals, `make` stops before the parser runs, so the sidecar from
- an earlier iteration is still sitting there with its old counts.
+ Bootstrap installs missing setup without overwriting authored files and refreshes the file list
+ and generated SGDC. `constraint-annotations.json`
+ supplies synchronizer, reset, case-analysis and quasi-static declarations. Check inherited
+ `scripts/local.sgdc` and `scripts/waiver.tcl` against the current design. Correct invalid input
+ declarations at their source.
- ### 4. Close
+ Run `make lint`, `make cdc`, or `make all` from the prepared workdir; `all` shares elaboration.
+ The entrypoint withdraws the selected check's old summaries before execution. Wait for completion,
+ read the reports and inspect violations. A waiver needs a specific `-comment` explaining why it is
+ valid under the requirements; determine whether a warning limit applies before or after waivers.
+ Repair local setup errors and rerun the affected checks.
- Run `finalize` to write the envelope. Every run ends here, a failed `make` included, and you
- never hand-assemble it:
+ ## Judge and close
```bash
- python3 <skill>/scripts/lintcdc/__main__.py finalize \
- --workdir {workdir} [--fix-owner <rule>] [--fail-reason "<cause>"] \
- [--requirements '[{"id": "R-103", "met": true, "actual": "0 errors, 0 warnings, 0 waivers", "measured": "reports/moresimple.rpt, whole run"}]']
+ python3 <skill>/scripts/lintcdc/__main__.py finalize --workdir {workdir} \
+ [--requirements '[{"id":"R-1","met":true,"actual":"observed result","measured":"report and scope"}]'] \
+ [--fail-reason "unresolved cause"] [--fix-owner <rule>]
```
- It ANDs the two sidecars for the gate (`status=pass` iff both exist and `counts.error` and
- `counts.warning` are both 0 in both), reads the SpyGlass version off the report, reshapes those
- rows into `violations[]`, folds your `--requirements` verdicts in as `requirements[]` — refusing
- an envelope that leaves any `lint-cdc` row unjudged, and failing the run on any `met: false` —
- and enumerates `artifacts[]`. Every verdict carries `measured` — which report, which row, which scope you read it from — because the row names a dimension and `measured` is what that name indexed. The flags carry what the report cannot:
-
- - **`--fail-reason`**, which fills `stage_specific.fail_reason`, when a `make` died before the
- parser wrote its sidecar, so the cause exists only on the stderr you read. Supplying it is
- itself the declaration of failure. A `make` that failed carrying no `FAIL=` token at all never
- reached the report step, which is an environment failure rather than a report defect.
- - **`--fix-owner`** on every failure, tool and license failures included, since it is what fills
- `stage_specific.fix_owner`. You are the only party that read the report, and the line a
- violation is reported at is not always the line that must change:
- [`references/attribution-rules.md`](references/attribution-rules.md) records the measured
- families where it points elsewhere. Name the stage whose artifact is wrong even for a rule
- nobody has catalogued; omit the flag only when you cannot name one at all.
-
- Exit 0 means written, pass or fail. Exit 2 is BLOCKED and never a `status=fail`: an unreasoned
- waiver, an empty `--fail-reason`, or a program exception.
-
- ## Return Contract
+ Declare a judgment for every lint-cdc requirement using the reports and the actual waiver policy.
+ `finalize` reads `lint-violations.json` and `cdc-violations.json` and checks waiver reasoning.
+ Use `--fail-reason` for invalid or incomplete checks even if old reports contain numbers. Name
+ the repair owner from the defect, including this stage for its own setup; do not infer ownership
+ from the path of an error. Acceptance changes follow the user's actual authorization.
- Emit `STATUS: DONE` as your last line once `result.json` exists, or
- `STATUS: BLOCKED <one-line reason>` when nothing could be written. What runs next is the
- caller's decision, taken from `result.json`.
+ Finalize withdraws the previous result before judging. Exit 0 means a pass/fail result was written;
+ return `STATUS: DONE`. On a nonzero exit, resolve the cause or return `STATUS: BLOCKED <cause>`.