plumb-line-audit · diff
git:20260811.4a0d014 to git:20260811.0e73b40
7 added, 2 removed. Audit A to A.
---
name: plumb-line-audit
description: Use when auditing a diff or repository against the plumb-line principles — finds laundered uncertainty, boundary leaks, hardcoded priors, overstated maturity, outputs lacking recorded lineage, and baseline drift with no explanation. Read-only: it reports, never auto-fixes.
---
# Audit against the plumb-line principles
REQUIRED READING FIRST: `reference/portable-principles.md` (plugin root).
If this file cannot be read, stop immediately and report: "Cannot audit: `reference/portable-principles.md` is missing or unreadable. Do not proceed from memory — the principles file is the source of truth for this audit."
Scope the audit to the diff if one is given, else the whole repo. For broad
sweeps, dispatch read-only subagents and keep only their findings.
**Declared architecture first (stop before auditing blind).** Several checks
(P1 source-truth, P2 layering, the omission pass's adoption calibration) are
only as good as the audit's knowledge of what the project *declares*. Resolve
it, in order: (1) supplied in the invocation/prompt by the builder; (2) the
project's ruleset file (`AGENTS.md`, `CLAUDE.md`, or equivalent) and boundary
config. If neither yields a source-truth layer and layer direction:
- **Builder present** — stop before the traversal plan and say what is missing.
Offer to invoke `plumb-line-bootstrap` in its **declaration-only entry** (a
two-minute detour: it asks only the three declaration questions, writes the
ruleset so the next audit never hits this stop, installs nothing, and returns
the baton); on yes, invoke it naming that entry, then resume the audit using
the freshly declared architecture. On no, proceed as below.
- **Builder absent, or declined** — proceed, calibrated to adopted principles
only (see "Calibrate to adopted principles"), and record the gap in the
report: an undeclared architecture is itself a P6-adjacent advisory (the
project's rules exist only as vibes), and the audit's P1/P2 coverage is
correspondingly `partial`, which the coverage map must say.
Never infer a source-truth layer the project did not declare — an invented
layer would make P1/P2 findings artifacts of the audit's own assumptions.
**Coverage honesty (emit a traversal plan first).** Before reading, list the
in-scope files — the diff's touched files, or the repo's source tree — and state
which you will read, sample, or skip. That list is the audit's **denominator**.
Track `read` / `partial` / `not-read` as you go and report it in the coverage map
(Report §4). On anything larger than a small diff you will NOT read every file;
say so up front rather than at the end. Never state or imply full coverage unless
every in-scope file is marked `read` — an unread file with no finding is *not* a
clean file, and claiming otherwise is the laundered-uncertainty / overstated-
maturity failure this audit exists to catch (P8 — State-first lineage, and the
honest-denominator discipline), turned on the audit itself.
## Check catalogue (each finding cites the principle it violates)
1. Laundered uncertainty (P3) — a value that lost its confidence/provenance as it flowed downstream; a mock/approximate value treated as clean truth.
- If the project uses the plumb-line provenance primitive: flag code that hand-builds provenance metadata bypassing combineProvenance/derive, and any value given a clean source while derived from a tainted input. Runtime complement: auditMeta().
2. Boundary leak (P2) — an import or call crossing layers against the declared direction; symbolic/derived/mock logic inside the source-truth layer (P1).
3. Hardcoded prior (P5) — a magic number encoding a judgment call, not injected/versioned config.
4. Overstated maturity (P6) — code or docs claiming current/done for something partial/mock/planned.
5. Missing lineage (P8) — an output stored without the inputs needed to reproduce it.
- If the project uses the provenance primitive: flag derived values that are never marked or carry no lineage; recommend asserting auditMeta(metaOf(value)) === [] in tests.
6. Unexplained drift (P9) — a changed golden-baseline value with no recorded reason.
7. Suppressed null result (spine) — a code path that cannot express "no structure/no effect/inconclusive". Confirmed only where rejection is a declared or practiced concern; where rejection is adopted nowhere, an always-accept/always-success stub is an advisory adoption gap, not a violation (see "Calibrate to adopted principles" below).
8. Escaped fakery (P4) — mock/approximate/fallback/cached data that left its container: not labelled (e.g. missing a derivedFromMock-style marker), or flowing into an export/output path that should exclude it unless explicitly opted in.
9. Uncontracted output (P7) — a public output shape with no versioned, validated contract: missing a validator, a version constant, or a canonical key list.
## Method
Run two passes — they catch different failure modes, and some checks need both.
**Presence pass — things wrongly present.** A bad thing IS in the code: an upward
import (P2), a magic number (P5), an escaped/unlabelled mock (P4), a mock value
treated as clean truth (P3), overstated maturity (P6), a changed baseline value
(P9 drift). Grep/read for the smell, then confirm by reading context — never
report on a keyword match alone.
**Omission pass — things wrongly absent.** A good thing is MISSING, and an
absence has no smell to grep. Enumerate instead — and put the enumeration in your
report as a table, one row per output-producing function / public output shape.
Give each question below its OWN column — do not collapse them. This table is a
REQUIRED artifact: the dropped field is invisible unless you walk every output,
and skipping the table is how the omission gets missed. For each output ask —
- does it carry **provenance** (where the value came from) where it influences a decision? (P3)
- does it carry **confidence** where it influences a decision? (P3)
- does it record **lineage** — the inputs needed to *reproduce* it (source, record/row count, field names, config/version used)? (P8)
- does it have a versioned, validated contract? (P7)
- can it express a null / "no effect" / rejection outcome? (spine)
- is there a golden baseline pinning it? (P9)
Provenance and lineage are SEPARATE columns and a present provenance string does
NOT satisfy lineage: a free-text `provenance` ("stub source: …") or a
`weights_version` answers *where from / which config*, but lineage answers *can I
regenerate this exact output* — which usually needs the record count, the field
names, and the source identity that a provenance string omits. If the
lineage-bearing layer's output has provenance but no field recording those
reproduction inputs, that is a missing-lineage (P8) violation, not a pass.
The failure this pass exists to prevent is a `lineage`, `confidence`, or
`provenance` field silently dropped from ONE output while a declared rule (or a
sibling output) says it should be there — invisible to grep, caught only by the
table. The most common miss is treating provenance-present as lineage-present;
the dedicated lineage column is what forces the check.
**Calibrate to adopted principles (governs the omission pass).** A principle is
_adopted_ when EITHER the project's declared ruleset/architecture requires it
(the primary signal — e.g. "service outputs are lineage-bearing", a declared
contract convention) OR sibling code already practices it (the fallback signal —
another output records lineage, other shapes ship a contract). An omission of an
adopted principle is a **violation** — and the declaration alone is enough: a
service output with no lineage violates a declared "services are lineage-bearing"
rule even if NO other output happens to carry lineage (the field may have been
removed from the only place it lived — its absence cannot also be its alibi).
Only where a principle is neither declared nor practiced anywhere — adopted
nowhere — do you refrain from flagging each output: report it ONCE as an advisory
adoption gap (a P6 maturity note), not as N violations. This keeps the audit
honest on small or early repos while still catching the dropped-field regression.
**The spine obeys this calibration too.** An always-`true` / always-success stub
(e.g. a `submitPayment` that can only return `accepted: true`) suppresses the
null/rejection outcome — but whether that is a **violation** or merely an
**adoption gap** turns on the same test. If rejection is declared in the
architecture or practiced by sibling code, the missing reject path is a confirmed
spine violation. If rejection is adopted nowhere — neither declared nor practiced
anywhere — a deliberate stub on a layer that never claims to reject is the
under-claim case: report it once as a `needs-review` advisory adoption gap, never
as a confirmed violation.
- Default to under-claiming: if unsure a finding is real, mark it "needs review",
not "violation".
## Report (audit format) — report-format v3
Every report has four parts in this order: **header**, **glossary**, **findings
table**, **coverage map**. The shape is fixed — same input, same shape, every run
(the audit owes its own output the reproducibility it demands of the code it
reviews).
**1. Header block** — verbatim keys, so a stored report is reproducible:
```
report-format: v3
scope: <path, diff range, or "repository">
principles-revision: <the "Principles revision" from reference/portable-principles.md>
date: <YYYY-MM-DD>
commit: <git SHA of the audited tree, or "working tree (uncommitted)">
```
**2. Principle glossary** — one line per principle *referenced anywhere in this
report*, emitted before the findings, so a reader never meets a bare code
(explain before use). Names come from `reference/portable-principles.md` — do not
paraphrase. Omit principles this report does not cite; on a clean run the glossary
may be empty. Example (include only the rows you cite):
```
P1 — Source-truth layer P4 — Quarantined fakery P7 — Contracted outputs
P2 — One-way layering P5 — Injectable priors P8 — State-first lineage
P3 — Confidence + provenance P6 — Maturity vocabulary P9 — Golden baseline + explain-the-drift
spine — null-result expressibility
```
Every `P#` reference elsewhere in the report renders with its name inline
(`P3 — Confidence + provenance`), never a bare `P3`.
**3. Findings table** — ALWAYS this table, never freeform prose. One row per
finding, columns in this exact order:
| Path | Line | Function | Issue | Suggested Fix | Principle |
| ---- | ---- | -------- | ----- | ------------- | --------- |
| `src/foo.py` | 42 | `load_scores` | mock value given a `real` source | tag via `derive`, keep `derivedFromMock` | P3 — Confidence + provenance |
- **Path** — repo-relative (never a bare basename; large repos have same-named files).
- **Line** — line or range; `—` if not line-anchored.
- **Function** — enclosing function/symbol, or `—`.
- **Issue** — one-line description.
- **Suggested Fix** — a direction, not a patch.
- **Principle** — the inline-named principle (`P# — <name>`).
**4. Coverage map** — the audit's honest denominator, so a reader can tell what
was actually examined. List every in-scope file (or directory, for a large tree)
marked `read` / `partial` / `not-read`, then state the count and an explicit
no-completeness caveat:
```
coverage: 12/47 files read, 3 partial, 32 not-read (32%)
scope note: findings are drawn from the read set only; a not-read file with no
finding is not a clean file. This audit does not claim completeness.
```
For a diff-scoped run the denominator is the diff's touched files and coverage is
normally 100% — say so explicitly rather than omitting the map. The map is
REQUIRED on every run: it is the artifact that stops the audit from implying it
found everything when it only sampled.
End with a one-line summary count (e.g. `4 findings: 2 violations, 2 needs-review`).
A clean repo still emits the header, an empty/omitted glossary, an explicit
`No findings.` line in place of table rows, and the coverage map — a clean result
is a valid result, but only over the files the coverage map lists as `read`.
The omission-pass enumeration table (defined in the Method section) is a separate
report artifact with its own columns; when the auditor emits it, its principle
references are inline-named too, exactly like the findings table above.
**Validate the report against its own contract before emitting it.** P7 applies
to this skill's own output: a contract with a version and a key list but no
check is a claim, not a contract. When `scripts/check_report_format.py` is
available (it ships in the plumb-line repo), run it on the report — writing the
report to a temp file if it has not been saved — and fix any violation before
printing. If the script is not reachable (the common case in a consumer repo,
which has the skills but not this repo's `scripts/`), say so in one line rather
than implying the report was mechanically checked:
```
- report-format: validated by scripts/check_report_format.py
- report-format: not mechanically validated (checker unavailable)
+ format-validation: scripts/check_report_format.py — clean
+ format-validation: not run (checker unavailable in this repo)
```
+
+ Put this line **below the coverage map**, and note the key is
+ `format-validation:`, *not* `report-format:` — a second `report-format:` line
+ would be read as a duplicate header key, and re-running the checker on the saved
+ report would then fail it on its own validation marker.
Never suppress a finding to make the report validate — the contract describes the
report's *shape*, and a shape violation is fixed by fixing the shape.
**Report file — always offer, never auto-write.** Print the full report
(header + glossary + table + summary) to the conversation every run. Then, and
only then, ask whether to save it to `plumb-line-audit.md`; write the file solely
on an explicit yes. Never write it without asking — repeated runs on the same
input MUST produce the same file-write behavior.
## After the report — hand the baton (read-only; invoke on yes, never apply)
The audit reports; it does not fix. After the report is printed and the file
question is settled, present the next steps as ONE short offer — and when the
builder accepts one, **invoke that skill directly** (via the host's skill
mechanism) rather than telling them to go run it; a handoff that ends in "you
could run X" drops the baton. The auditor itself still applies nothing — the
invoked skill owns its own actions and its own consent gates.
- **Apply the findings** (the default next step when there are findings) —
invoke `plumb-line-remediate`, pointing it at this report (the saved
`plumb-line-audit.md` if the builder said yes to the file, else the printed
findings table). Remediate classifies, shows a diff per finding, and keeps
its own honesty guardrail — the read-only boundary moves with the baton.
- **Plan the fixes first** — hand the findings to a planning skill (superpowers
`writing-plans`, or plan mode) for a fix-plan the builder works through. This
produces a plan, not edits.
- **Close a provenance/enforcement gap** — when findings include missing
provenance or lineage, laundered taint, or a layer boundary nothing enforces
(P3 — Confidence + provenance, P4 — Quarantined fakery, P8 — State-first
lineage, P2 — One-way layering), offer `plumb-line-bootstrap` to wire the
discipline in (host rule file + git hooks). Offer this only when such a gap
actually appears, not on every run.
Declined, or no builder present to answer: stop after the report — never
auto-invoke anything that edits files. On a clean run (no findings), skip the
remediate offer entirely; offer bootstrap only under the gap condition above.