plumb-line-remediate · git:20260810.78d799a · 2026-08-10 · sha256 5b017fe2b6e1bda3

plumb-line-remediate git:20260810.78d799aA

Immutable. This exact content is served forever at /api/v1/blob/5b017fe2b6e1bda3.

---
name: plumb-line-remediate
description: Use when applying findings from a plumb-line audit report — the builder has a report (or pasted findings) and wants the fixes made. Opt-in and separate from the audit, which is read-only and never fixes.
---

# Remediate plumb-line audit findings

REQUIRED READING FIRST: `reference/portable-principles.md` (plugin root).
If this file cannot be read, stop immediately and report: "Cannot remediate:
`reference/portable-principles.md` is missing or unreadable. Do not proceed from
memory — the principles file is the source of truth for what a fix must honor."

The audit finds; this skill fixes. The two never blur: remediation runs only on
an explicit invitation, consumes a report the audit produced (any
`report-format: v1`+; v3 is current), and applies nothing the builder has not
seen. If there is no report, offer to run `plumb-line-audit` first, or accept
findings pasted in the findings-table shape (Path / Line / Function / Issue /
Suggested Fix / Principle).

## The contract (what a remediation run IS)

A remediation run produces, in order:

1. a **fix plan** — every finding classified before any edit,
2. **per-finding diffs** — each shown before it is considered done,
3. a **remediation record** — the finding→change table that gives the
   remediation itself the lineage it demands of the code (P8),
4. a **verification step** — the project's own enforcement, run, with output.

Editing a file the builder never sees a diff for, or finishing without the
record, is a failed run — regardless of whether the fixes were correct.

## Step 1 — Classify every finding before touching anything

Read the whole findings table first and classify each row. Print the plan as a
table (Finding / Path / Class / Intended action) before the first edit.

**Mechanical** — the principle determines the fix; no epistemic value must be
invented. Typical: remove an upward import (P2 — One-way layering); replace a
hardcoded constant with the already-existing injected config (P5 — Injectable
priors); add a version constant + validator to an uncontracted output (P7 —
Contracted outputs); record lineage fields that are computable from inputs in
scope (P8 — State-first lineage); relabel maturity to what the code already is
(P6 — Maturity vocabulary); wrap an untagged return in `mark`/`derive` where the
source is unambiguous (P3 — Confidence + provenance).

**Judgment** — the fix requires a claim only the builder can stand behind: which
confidence a value deserves, whether a stub should stay or become a real
integration, which layer a relocated piece of code belongs to, what a changed
baseline's explanation is (P9 — Golden baseline + explain-the-drift). For each
judgment finding, propose a concrete default and ask; apply only on a yes. When
the builder is not present to answer, apply the **conservative default** (below),
mark it `applied-conservative` in the record, and say it needs their review.

**The conservative default: claim nothing the code cannot support.** Where an
epistemic value must be supplied and nobody has answered, take the weakest
honest claim — a fabricated or stubbed value gets the floor (`confidence: 0` /
lowest rung, `source: mock`, `derivedFromMock: true`); an unverifiable origin is
labelled as what it is, not what it is hoped to be. Never pick a "reasonable
middle" (0.5-ish) for a value that is actually fake: an invented moderate
confidence is an invented fact. Two runs on the same input must produce the same
claim — the floor is deterministic; optimism is not.

## Step 2 — Apply one finding at a time, diff shown per finding

Work finding by finding, smallest first if order is free. For each: make the
edit, then show the diff (before/after or unified) labelled with the finding it
resolves, in the conversation, as you go — not a bulk dump at the end. The
builder must be able to stop a bad direction after finding 1, not discover it
after finding N.

**Scope discipline.** Touch only what the finding requires. Adjacent
improvements you notice — a stale comment, a string that could be richer, a
refactor begging to happen — go in the record's **Proposed (not applied)**
section, never into the working tree. One exception, because honesty beats
minimality: if your edit makes an existing statement in the same file false
(a comment or docstring naming a constant you just deleted, a doc line
describing behavior you just changed), correct that statement as part of the
finding's diff — a remediation must not manufacture a lie by omission. Note the
correction in the record.

If a fix for one finding would conflict with the fix for another, stop and
surface the conflict rather than picking silently.

## Step 3 — The honesty guardrail (what a fix may never do)

A remediation may never resolve a finding — or satisfy a gate, a test, a
deadline, or a re-audit — by making the code *less* honest. Concretely, never:

- clear or hand-set a taint flag (`derivedFromMock` and kin) on data that is
  still mock-derived;
- raise a confidence, or swap a source label for a cleaner one, so a check
  passes;
- delete or bypass a null-result / rejection branch so an output always
  succeeds;
- drop a provenance, confidence, or lineage field because its honest value
  is embarrassing;
- update a golden baseline without a recorded explanation of which input moved
  (P9 — the explanation IS the fix; a silent update is the violation).

When the only change that would satisfy a finding, gate, or instruction is one
of these, the finding is **blocked**, not fixable: record it as `blocked` with
the honest paths out (implement the real thing, or obtain an explicit written
waiver that scopes the check), and leave the code truthful. A gate failing on
honest metadata is the gate working; the remediation's job was done when the
metadata became honest. This mirrors bootstrap's rule: if you cannot name a
source-truth layer, that absence is the finding — here, if you cannot fix it
honestly, that impossibility is the finding.

## Step 4 — Verify with the project's own enforcement

After the last fix, run what the project already trusts, and show the output:

- plumb-line adapters if installed (boundary check, `no-provenance-bypass`
  lint, `require-provenance-output` if the project declared a surface,
  pre-commit gate);
- otherwise the project's own linters/tests that cover the touched files;
- at minimum, an import/load smoke check of every file changed.

Then offer — never auto-run — a re-audit scoped to the touched files, so
find-and-fix stay separate acts with separate records. On a yes, **invoke
`plumb-line-audit` directly** (via the host's skill mechanism) rather than
telling the builder to run it — the baton passes by invocation, not
instruction.

## Step 5 — The remediation record

End every run with the record, then offer — never auto-write — to save it as
`plumb-line-remediation.md` (same always-offer contract as the audit's report
file). The record has a header and a table:

```
remediation-format: v1
source-report:       <path or "pasted findings">
source-report-format: <its report-format version>
principles-revision: <from reference/portable-principles.md>
date:                <YYYY-MM-DD>
commit:              <git SHA before remediation, or "working tree (uncommitted)">
```

| Finding | Path | Class | Action | Change summary |
| ------- | ---- | ----- | ------ | -------------- |

- **Action** is one of: `applied-mechanical`, `applied-judgment` (builder said
  yes), `applied-conservative` (default taken, needs review), `proposed`
  (suggested, not applied), `blocked` (honest fix impossible — reason given),
  `skipped` (builder said no).
- Below the table: the **Proposed (not applied)** list, and one line per
  `blocked`/`applied-conservative` row saying what the builder must decide.
- Principle references render inline-named (`P3 — Confidence + provenance`),
  never bare codes, exactly as in the audit report.

A run where every finding lands as `applied-mechanical` still emits the full
record — the record is the lineage of the remediation, not a summary of its
difficulties.

## Quick reference

| Situation | Action |
| --------- | ------ |
| No audit report exists | Offer `plumb-line-audit` first, or accept pasted findings |
| Fix needs an epistemic value nobody supplied | Conservative floor, `applied-conservative`, flag for review |
| Only a dishonest edit would satisfy the finding/gate | `blocked` + honest paths out; leave code truthful |
| Noticed an improvement no finding asked for | Record under Proposed (not applied) |
| Your edit made a nearby comment/doc false | Correct it within the finding's diff; note in record |
| Two findings' fixes conflict | Stop; surface the conflict |
| All fixes applied | Run project enforcement, show output, offer re-audit |