Produce a clean-room implementation spec for a single peripheral (Ethernet MAC, UART, GPIO, SD/MMC, USB, display/mailbox, I2C/SPI, …) so an engineer can write a from-scratch driver in a differently-licensed OS. Use when asked to spec a driver or research a hardware block before coding it. Orchestrates os-investigator and the board-expert skill, and enforces the transfer protocol, mandatory leak scanning, and the provenance ledger; consumer-side enforcement lives in cleanroom-implementer.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add curtisgalloway/public-skills --skill cleanroom-spec --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Cleanroom Spec?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/curtisgalloway-cleanroom-spec)More formats (shields.io, HTML) on the badges page.
---
name: cleanroom-spec
description: >-
Produce a clean-room implementation spec for a single peripheral (Ethernet MAC, UART, GPIO,
SD/MMC, USB, display/mailbox, I2C/SPI, …) so an engineer can write a from-scratch driver in a
differently-licensed OS. Use when asked to spec a driver or research a hardware block before
coding it. Orchestrates os-investigator and the board-expert skill, and enforces the transfer
protocol, mandatory leak scanning, and the provenance ledger; consumer-side enforcement lives in
cleanroom-implementer.
---
<!--
SPDX-FileCopyrightText: 2026 Curtis Galloway
SPDX-License-Identifier: Apache-2.0
-->
# Peripheral driver spec (clean-room)
You produce one **implementation spec per peripheral** — the document an engineer reads to write a
driver from scratch. It has two halves: **HALF 1 hardware** (clean-room facts, no source) and
**HALF 2 target-OS integration** (read the OS tree, cite file:line, reuse-vs-write). Each spec is
self-contained, saved into the project's `docs/` only after passing independent verification,
indexed in `AGENTS.md`, and recorded in the provenance ledger.
The clean room's value is **evidentiary**: a process you can't show is a process you don't have.
This skill therefore produces not just the spec but the record that it was produced cleanly — the
pinned provenance, the verifier's verdict, the scan reports, and the ledger.
**Not the skill for source you own.** If the driver source is yours, your organization's, or
compatibly licensed, the wall below is not just unnecessary but counterproductive — use
`anchored-peripheral-spec`, which produces the same spec shape with every fact anchored to the
file:line it came from so a reviewer can verify it against the code.
## Compose, don't duplicate
- **`os-investigator`** owns the *method* and the **clean-room rule**: return hardware FACTS and
MECHANISM in your own words — register offsets, bit fields, IRQ numbers, init ordering, descriptor
layouts — every fact tagged `[databook]`/`[standard]`/`[DT]`/`[source-observed]`, and **NEVER
reproduce driver/firmware source code**, even when asked. Load and follow it for HALF 1. It also
ships the mechanical scanner (`scripts/leak_scan.py`).
- **The board-expert skill** (e.g. `rpi-expert`) owns the *map*: the SoC/board addresses, IP identity,
quirks, cached references. Route "what address/IRQ/clock/compatible" questions through it.
(Anything cached into a board-expert skill must itself be datasheet-cited or verifier-PASSed —
a cache is a wall-crossing that replays into every context that loads the skill.)
- **`cleanroom-implementer`** owns the *consumer side*: the standing rules and enforcement for
agents writing code from landed specs — hook-based blocking of encumbered-source access, the
spec-gap escalation path, the restricted implementer agent, and the session transcript audit.
- **The verifier** loads `os-investigator` too — it is the canonical statement of allowed/forbidden
and the home of the scanner.
- **This skill** owns the *spec shape*, the **transfer protocol**, the verification gate, the
ledger, and the delegation pattern.
The HALF 2 (OS integration) reading of the *target* tree (Fuchsia, etc.) is your own codebase — read
it directly and cite file:line. The clean-room rule applies only to the *source* OS/firmware you're
porting away from.
## The move that makes a spec good: identify the IP first
Before anything else, **name the silicon IP block + vendor**, then find the **authoritative datasheet
for that IP** — which is often a *sibling/proxy part* that is publicly documented even when the exact
part is NDA:
- Cadence GEM/MACB → the Xilinx Zynq-7000 (UG585) / UltraScale+ (UG1085) GEM chapters document the
identical IP publicly.
- ARM PrimeCell (PL011 UART, PL022 SPI, PL061 GPIO, PL080 DMA) → the ARM TRM for that PrimeCell.
- Synopsys DesignWare (DWC3/DWC2 USB, DW-APB-SSI SPI, DesignWare I2C/I2S, AXI-DMA) → Synopsys
databooks / the Zynq chapters.
- A Broadcom/vendor PHY → the closest register-compatible family-member datasheet + IEEE 802.3
clauses for the standard MII/autoneg registers.
A spec built on "the IP is X; here is X's datasheet" is citable and correct; a spec built on reading
the Linux driver is neither. Use the kernel/devicetree only as a **map** to learn *which* registers
the hardware uses, then cite the datasheet. Every fact retagged from `[source-observed]` to
`[databook]` is a fact that never needed the clean room.
## Required structure of every spec
1. **Clean-room usage notice (top of the document)** — a short notice telling every consumer of
the spec, human or agent: this spec was produced from encumbered source by a designated
clean-room reader and independently verified; do **NOT** read the original source-OS/firmware
code yourself; this spec plus its cited public references are the only implementation inputs.
Further clauses: **on any gap, file a spec-gap** (append the question to
`docs/spec-gaps/<device>.md`, mark the code site `TODO(spec-gap)`, continue with other work —
never open the source; the urge to "check the driver" *is* a spec-gap);
**`[source-observed]` facts are never verified against the source** — verify on hardware or
file a spec-gap; **do not open `docs/provenance/`** — it exists for verifiers and counsel, not
implementers; **code written from this spec must pass the pre-merge gate** (output scan + clean
session audits — see the ledger) before it lands; and **if this spec's content hash no longer
matches its latest ledger PASS line, treat it as unverified** — it has been edited since
verification.
2. **IP identity & provenance** — vendor + IP family + specific instance/revision; what a vendor
wrapper adds over the stock IP; the public-citable lineage.
3. **Canonical references (a headline deliverable)** — a TABLE: each authoritative datasheet /
programr's guide / standard, *what it authoritatively covers*, and *how to find it* (doc number,
URL, chapter/section). Prefer hardware specs + public proxies + relevant IEEE/standards over the
source-OS driver.
4. **Register map** — grouped by the **databook's** functional organization (never driver-touch
order); offsets + the bit fields that matter, each value carrying its provenance tag. Flag where
exact offsets are revision-dependent and must be confirmed.
5. **Ordered init sequence** — including prerequisites (clocks, resets, parent buses, address
windows), step by step, **each step tagged**; `[source-observed]` orderings marked "order not
known to be required"; `[source-observed]` constants marked "re-derive on hardware".
6. **Data / descriptor formats** — DMA ring/descriptor word layouts, ownership/wrap/status bits,
alignment, 32- vs 64-bit addressing.
7. **Interrupts** — the full routing chain (device → aggregator → MSI → top-level controller), the
status bits that matter, and ack/clear semantics (incl. any level-vs-edge / IACK quirks).
8. **DMA / addressing** — bus↔CPU address translation (cite the ranges/dma-ranges), bus-master
windows, cache/coherency rules.
9. **Sub-protocols** — e.g. MDIO/PHY management, PHY register access, tuning sequences.
10. **Target-OS mapping** — read the OS tree: which existing driver to MODEL on (file:line), the
exact driver-facing protocol(s) to implement, **reuse-vs-write** call, the bind rule + DT node
shape, packaging into the board/product. Apply the project's driver-language policy (e.g. new from
scratch → Rust+DFv2; modifying existing → keep its language) and flag reuse candidates (standard
IP that already has an OS driver).
11. **Milestones** — the minimal first observable result, then the full integration; note any
prerequisite drivers (PCIe RC, bus/clock/IRQ glue) and any earlier stepping-stone. **Full
integration includes the pre-merge output scan** (below).
12. **Gotchas (consolidated)** + **per-area confidence ratings** (be honest where bit positions are
NDA/family-inferred → mark "verify on hardware") + the **clean-room attestation** for HALF 1:
the pinned provenance (`<repo>@<commit>` — **no file paths in the spec body**; the full file
map lives in the sidecar `docs/provenance/<device>-map.txt`, which is the verifier's
comparison list and the target list for the pre-merge output scan, and which implementers
never open) and the **verification record** (date, verdict, scan-report path, spec sha256 at
PASS — filled by the verifier/orchestrator, not the spec author).
## How to run it (delegate; don't inline)
**Delegation here is a clean-room requirement, not just a context-saving nicety.** `os-investigator`
and the board-expert skills are *subagent roles*: their bodies fetch and read GPL/encumbered source.
The main/orchestrating agent — the one that will write the differently-licensed target-OS code — must
**never run those skills inline or read the source-OS tree / board cache itself.**
- **Per peripheral, prefer one subagent per spec** so several specs progress in parallel and the main
context stays lean. In Antigravity, define the investigator as a subagent
(`.agents/agents/spec-investigator.md`, `subagent: true`) and invoke it deliberately rather than
hoping the primary agent delegates, or launch it as its own task in the Agent Manager so the work
runs in a separate context and its artifacts stay separate too. Either way, instruct it to load
`os-investigator` + the board-expert skill and give it the spec subagent template filled in.
- **A subagent's context is separate; its *credentials and environment* are not.** Delegation buys
you a clean orchestrator context, not enforcement — that's Tier 1 and 2 in
`cleanroom-implementer`. If the dirty side must be *authorized* to read source (role scoping), it
needs its own process, not just its own context.
- Do this even when the peripheral is for a *later* phase — captured specs are the implementation
source of truth when that phase starts.
### The transfer protocol (unverified spec text never enters the orchestrator)
The wall only holds if the orchestrator never holds unverified spec text — a leak the verifier would
have caught must not first transit the very context that writes the target-OS code. So:
1. The orchestrator picks a scratch path and spawns the spec subagent
(`templates/spec-subagent-prompt.md`). The subagent
**writes the spec to that path itself**, writes the file map to the provenance sidecar
(`docs/provenance/<device>-map.txt`), and returns **only** `{path, one-paragraph summary,
<repo>@<commit>}` — no spec text and no file paths in its reply. The spec body itself is
attractant-free: facts stated as hardware facts, no source-tree paths, no "Linux does X in
file Y" narration.
2. The orchestrator spawns a **fresh verifier subagent** on the path
(`templates/verifier-prompt.md`) — never the
subagent that wrote the spec, never the main agent. The verifier is itself a designated
clean-room reader.
3. **PASS** → the orchestrator moves the file to `docs/<device>-spec.md`, computes `sha256sum`,
appends a ledger line, and adds the one-line `AGENTS.md` index entry from the returned summary.
The orchestrator may now read the spec freely — it is clean by verification, not by promise.
4. **FAIL** → hand the verdict (section + line refs only) to a **fresh** spec subagent to rewrite the
flagged sections in place at the scratch path, then re-verify. **After two FAILs on the same
section, stop and escalate to the human with the verdict only** — repeated failure usually means
the only authority for that mechanism *is* the source, and whether/how to express it is a
judgment call for a person.
5. **Editing a landed spec re-enters this loop** (edit at a scratch copy, re-verify, re-land, new
ledger line). The ledger's content hash is how drift is detected: a spec whose hash no longer
matches its latest PASS line is unverified.
### The evidentiary ledger
`docs/provenance-ledger.md` — one line per spec revision and one per output scan:
```
2026-08-07 | dwc3 | docs/dwc3-spec.md | sha256:3f9c2a1b04de | linux@<commit> | PASS | docs/provenance/dwc3-scan-2026-08-07.txt
2026-09-02 | dwc3 | output-scan: src/devices/usb/dwc3/ | linux@<commit> | clean | docs/provenance/dwc3-output-scan-2026-09-02.txt
```
Retain everything: the ledger, the scan reports under `docs/provenance/`, and the
investigator/verifier session transcripts. Together they are the evidence that the clean room
existed and was enforced — the thing you can hand to counsel.
### The spec-gap protocol (the sanctioned path when a spec is insufficient)
Implementer contamination is usually gap-driven, not defiance-driven: the spec is missing
something, and reading the source is one tool call away. The fix is a sanctioned path that is
*cheaper* than the forbidden one:
1. The implementer appends one line to `docs/spec-gaps/<device>.md` —
`- [open] <date> <spec section> <question>` — marks the code site `TODO(spec-gap)`, and
**continues with other work**. Filing a gap is never a failure; reading the source costs the
session's entire diff.
2. The orchestrator sweeps open gaps into fresh `os-investigator` runs (the dirty side answers),
amends the spec at a scratch copy, re-verifies, re-lands, adds a ledger line, and marks the gap
`[resolved <date>]`.
The protocol appears in three places on purpose — the usage notice, the implementer's standing
rules (`cleanroom-implementer`), and the project context file (`AGENTS.md` at the workspace root, or
a workspace rule under `.agent/rules/`) — because a notice read 40k tokens ago does not survive
context pressure; the standing block does.
### The output-side scan (pre-merge gate)
The spec's provenance map doubles as the diff-target list for the strongest available check on the
*implementation*. Verified non-access is not attainable for a model-written implementation (the
model's training data included the source); **verified dissimilarity of the output is**, and this is
where it happens: before a driver written from this spec merges, run the scanner over the new driver
sources against the sidecar map's files at the pinned commit, save the report to
`docs/provenance/`, and add a ledger line. The pre-merge gate is the output scan **plus a clean
session transcript audit** for every implementation session that touched the driver (see
`cleanroom-implementer`). A finding in either blocks the merge until resolved and rescanned — and
a contaminated session's diff is discarded wholesale and regenerated, never salvaged.
```
python3 <os-investigator>/scripts/leak_scan.py <new driver sources...> \
--against <provenance-map files at repo@commit> --whitelist <nomenclature file>
```
### Consumer-side enforcement (see `cleanroom-implementer`)
Instructions are the weakest layer; the companion skill ships the enforcement: an Antigravity
`PreToolUse` hook that blocks and logs encumbered-source access (checkout paths, kernel-mirror URLs,
shell fetches, MCP tools), permission deny rules that take the web tools and encumbered paths away
outright, a restricted `driver-implementer` subagent (no web, no MCP, no `invoke_subagent`,
sandboxed shell), the `AGENTS.md` standing block, and `session_audit.py` for the pre-merge transcript
and artifact audit. Layering, strongest first: environment (no encumbered checkout mounted, egress
off or allowlisted, datasheets pre-fetched into `docs/references/`), harness (hook + permissions +
restricted subagent), instructions (notice + standing rules).
**Role scoping:** the hook applies to every session in the project — including the dirty side,
which *must* read source. Investigator and verifier processes therefore run with
`CLEANROOM_ROLE=investigator` (or `verifier`) in their environment: the hook then allows the
access but still logs it, so the log doubles as a complete, attributed record of every
encumbered-source access. Run dirty-side work as a **separate `agy` process** with the variable
exported — not an `invoke_subagent` call from an implementation session, which inherits that
session's environment — so the role never leaks into a context that writes code. Permissions have no
environment escape at all, so scope them by launch instead: the dirty side runs from settings
without the implementer's deny rules.
### Spec subagent prompt template
The fill-in prompt ships next to this skill at `templates/spec-subagent-prompt.md`. Read it when
spawning, substitute every `<angle-bracket>` placeholder (peripheral identity, scratch path, target
tree, board-expert skill name), and pass the result as the subagent's prompt. It encodes the
transfer protocol, the os-investigator constraints, the self-scan, the attractant rules, the
usage-notice requirement, and the required coverage for both halves.
### Verify before saving (mandatory, every spec)
A returned spec is not done until an **independent verification subagent** has passed it. Fire off a
*fresh* subagent — never the one that wrote the spec, never the main agent — with the verifier template.
It checks exactly five things: mechanical scan, leak judgment, hardware-derived structure,
attractants, and the usage notice. It does **not** check technical accuracy — accuracy belongs to a separate pass or to
hardware, and folding it in would dilute the one job that must be done adversarially. Its verdict
must never quote the source *or* the offending spec passages (quoting would re-leak them into the
main context). Section + line-range references only.
The verdict is **PASS + scan-report path**, or **FAIL + scan-report path** with a list of
`{section, line range, one-line reason}` entries. Only a PASS lands in `docs/`.
### Verifier prompt template
The fill-in prompt ships at `templates/verifier-prompt.md`. Substitute the placeholders (spec path,
pinned `<repo>@<commit>`, sidecar map path) and pass it verbatim to the fresh verifier subagent. It
walks the five checks above and pins the required verdict format.
## Quality bar
- Every register/sequence is a datasheet/standard fact re-expressed in your own words — **no source
code**, ever — and every constant and step carries its provenance tag; `[source-observed]` items
say so and say what to do about it.
- The reference table names *obtainable* documents (public proxies when the exact part is NDA).
- Reuse is identified honestly: "standard IP X → OS already has driver Y" is gold; flag the caveats
(devicetree bind support, exact register-compat, prerequisite glue).
- Confidence is per-area and honest; anything inferred from a sibling part says so and says "verify
on hardware."
- The orchestrator never held spec text that hadn't PASSed.
- The spec body names no source-tree file paths; the file map lives only in the provenance
sidecar, and implementers never open `docs/provenance/`.
- Every saved spec opens with the clean-room usage notice, has PASSed independent verification, and
has a ledger line whose hash matches the file; every merged driver has an output-scan line and
clean session-audit lines. An unverified spec never lands in `docs/`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!