diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 2ad0473..837e8f3 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -21,7 +21,7 @@ { "name": "audit", "source": "./audit", - "version": "0.1.4", + "version": "0.2.0", "description": "Bulk, read-only audits of a QuantEcon repository — issue triage, PR review, technical debt, translation parity — each producing an evidence-cited report bundle" } ] diff --git a/audit/.claude-plugin/plugin.json b/audit/.claude-plugin/plugin.json index 2743184..8d0b6d4 100644 --- a/audit/.claude-plugin/plugin.json +++ b/audit/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "audit", "description": "Bulk, read-only audits of a QuantEcon repository — issue triage, PR review, technical debt, translation parity — each producing an evidence-cited report bundle", - "version": "0.1.4", + "version": "0.2.0", "author": { "name": "QuantEcon" } } diff --git a/audit/CHANGELOG.md b/audit/CHANGELOG.md index 67ef60e..d94342b 100644 --- a/audit/CHANGELOG.md +++ b/audit/CHANGELOG.md @@ -6,6 +6,15 @@ Versions are [semver](https://semver.org) as a user of this plugin experiences i Repository: [QuantEcon/skills](https://github.com/QuantEcon/skills) ([every commit that touched this plugin](https://github.com/QuantEcon/skills/commits/main/audit)). How a release is made: [developing-skills § Versioning and releases](https://github.com/QuantEcon/skills/blob/main/docs/developing-skills.md#versioning-and-releases). +## 0.2.0 — 2026-08-07 + +The two severity-1 defects from the first measured run, which are the same defect at different altitudes: an audit's own record claiming more than it can support. + +**Changed** + +- **`[verified]` now requires evidence reachable from the ref the audit named.** Doctrine §2 is the single statement of it — §1 rule 1 no longer carries its own copy of the accepted-forms list, which is how the two drifted apart in the first place — and it covers every citation form — a commit must be an ancestor of the baseline ref, a `file:line` must be that line *on the ref* rather than in the working tree, a PR must be merged into it — and `/audit:issues` runs `git merge-base --is-ancestor ` before tagging a commit citation. Run 1's headline finding cited a commit that is real, does touch the file, and exists only on an unmerged branch, while the report's header said it had verified against `main`. A citation that resolves for its author and not for its reader is worse than an untagged claim, because the tag is what invited the trust. Evidence that genuinely lives off-ref stays citable — as the open PR it is, tagged `[stated]` or `[inferred]`. +- **Phase 2 checkpoints both of its passes.** `findings.md` now carries `## Open` and `## Closed` sections, and the resume rule partitions `issues.json` by state and resumes each side independently at the lowest number with no entry. Run 1 wrote only the 56 open issues to the checkpoint and sent the 62 closed ones straight to the catalog, so a run interrupted during the closed pass would have re-verified all 62 from scratch while reporting itself complete — the single-block resume rule shipped in 0.1.2 could not see the difference. + ## 0.1.4 — 2026-08-03 Doctrine §4's rule survived the first measured run; its justification did not. The section is re-derived from what that run actually produced, and the cost figures the skill quotes are replaced with measured ones. diff --git a/audit/references/doctrine.md b/audit/references/doctrine.md index 67b673c..df3a099 100644 --- a/audit/references/doctrine.md +++ b/audit/references/doctrine.md @@ -4,7 +4,7 @@ The method every skill in this plugin follows. Skills own their subject matter ## 1. What makes a bulk audit trustworthy -1. **Never trust an artifact's claim about its own state.** Every "fixed in X", "scheduled into Y", "superseded by Z" is a hypothesis until checked against the current default branch, `CHANGELOG.md`, and merged PRs. Cite `file:line`, a PR, or a tag for each verified claim. +1. **Never trust an artifact's claim about its own state.** Every "fixed in X", "scheduled into Y", "superseded by Z" is a hypothesis until checked against the current default branch, `CHANGELOG.md`, and merged PRs. Every verified claim carries a citation that resolves on the ref the audit named — the accepted forms and the reachability rule are [§2](#2-evidence-classes). 2. **Check both directions.** Open items that are actually resolved, *and* closed items whose fix never really landed. An audit that only prunes is half an audit. 3. **Read every thread, closed ones included.** Closed threads carry decisions, deferred remainders, and agreed-but-never-filed work. Skipping the closed side was the one gap in this runbook's first execution, and it is the default failure mode — the snapshot captures closed threads precisely so there is no cost excuse. 4. **Distinguish guarded from fixed.** "Now fails loudly" is not "root cause resolved". Record which kind of remainder each item carries. @@ -19,12 +19,24 @@ Findings are tagged so a reader can tell what was actually inspected. This is th | Tag | Means | Minimum citation | |---|---|---| -| `[verified]` | Checked against code, a diff, or a build artifact | `file:line`, a merged PR, or a tag | +| `[verified]` | Checked against code, a diff, or a build artifact | `file:line`, a merged PR, a tag, or a commit — each reachable from the baseline ref (below) | | `[stated]` | Asserted by a human in a thread or a notes file | Comment URL or `file:line` | | `[inferred]` | The audit's own reasoning across sources | The sources it reasons from | An unqualified claim is a defect. `[inferred]` is legitimate and often the most valuable class — but it must never be dressed as `[verified]`, and a status change recommended on `[inferred]` alone should say so in the recommendation itself. +**Every `[verified]` citation is relative to one named ref, and must be reachable from it.** An audit states the ref it verified against — the default branch at the snapshot commit — and that ref is what makes its citations checkable. A citation the reader cannot resolve there is not a weaker citation; it is a false one, and worse than an untagged claim, because the tag is what invited the trust. + +The failure is easy to miss because it resolves for whoever wrote it. Run 1's headline finding cited a commit that is real, does touch the file, and exists only on an unmerged branch — while the report's own header said "verified against `main` @ `2c3d624`" ([defect 1](https://github.com/QuantEcon/skills/issues/21)). A working tree carrying extra branches, or a `gh` call that searches the whole repository rather than one ref, will both produce citations that a reader checking out the stated ref finds nothing at. + +So, before tagging anything `[verified]`: + +```bash +git merge-base --is-ancestor # exit 0 = citable; non-zero = not on that ref +``` + +The same question applies to the other citation forms, mechanically or by eye: a `file:line` must be that file and that line **on the baseline ref**, not in the working tree; a PR must be *merged into* it; a tag must be an ancestor of it. Where a claim genuinely rests on off-ref work — an open PR's branch, a fork — that is legitimate evidence, but it is cited as the open PR it is and tagged `[stated]` or `[inferred]`, never `[verified]`. + ## 3. Read-only boundary **These skills observe and report. They never mutate what they audit** — no closing, labelling, milestoning, commenting or editing on the tracker, and in the audited repo no commits, no pushes, no branches, and no changes to tracked files. diff --git a/audit/skills/issues/SKILL.md b/audit/skills/issues/SKILL.md index 9b2067e..55db1ea 100644 --- a/audit/skills/issues/SKILL.md +++ b/audit/skills/issues/SKILL.md @@ -47,11 +47,13 @@ State which was used in the report's method section. Everything the run produces | Path | Written by | Holds | |---|---|---| | `snapshot/` | phase 1 | `meta.json`, `issues.json`, `prs.json`, `coverage.json` | -| `findings.md` | phase 2 | one entry per item, appended as each is verified | +| `findings.md` | phase 2 | one entry per item — **both** the open and the closed pass, under `## Open` and `## Closed`, appended as each is verified | | `links.md` | phase 3 | the cross-link graph | | `01-…` `02-…` `03-…` `README.md` | phase 4 | the delivered bundle | -**Append to the checkpoint as you go, not when the phase ends** ([doctrine §4](../../references/doctrine.md#4-checkpointing)). Phase 2 carries nearly all the judgement, so it is both the phase an interruption lands inside rather than between, and the phase whose log the catalog is later assembled from. On restart, read `findings.md` and resume at the lowest number in `issues.json` that has no entry there; re-verify the last entry rather than trusting a possibly truncated write. +**Append to the checkpoint as you go, not when the phase ends** ([doctrine §4](../../references/doctrine.md#4-checkpointing)). Phase 2 carries nearly all the judgement, so it is both the phase an interruption lands inside rather than between, and the phase whose log the catalog is later assembled from. + +**Both passes are checkpointed, and the resume rule reads `issues.json`.** Phase 2 walks two sets — the open issues and the closed ones — and each has its own section in `findings.md`. On restart, partition `issues.json` by state and, for each partition independently, resume at the lowest number with no entry under the matching heading; re-verify the last entry in each rather than trusting a possibly truncated write. Do not infer progress from the file's length or from a single block: run 1 wrote only the open set to `findings.md` and sent the 62 closed issues straight to the catalog, so a resume would have re-verified all 62 from scratch while reporting itself complete ([defect 2](https://github.com/QuantEcon/skills/issues/21)). ## Phase 1 — snapshot @@ -77,7 +79,15 @@ Per [doctrine §1](../../references/doctrine.md#1-what-makes-a-bulk-audit-trustw Sibling-repo checks belong here too: for QuantEcon, "resolved in a sibling" and "one step of a rollout" are the two most common wrong conclusions a single-repo audit reaches. -Write each item's finding to `findings.md` as it is verified, in the catalog entry format from [deliverables.md](../../references/deliverables.md#the-auditissues-bundle) — so phase 4 assembles the catalog rather than re-deriving it, and an interrupted run loses one item rather than the phase. +**Check every citation against the baseline ref before tagging it `[verified]`** ([doctrine §2](../../references/doctrine.md#2-evidence-classes)): + +- **a commit** — run `git merge-base --is-ancestor `; exit 0 means citable +- **a `file:line`** — confirm that line says what the finding claims *on the ref*, not in the working tree +- **a pull request** — confirm it is merged into that ref + +Evidence that lives only on an unmerged branch is still worth citing — as the open PR it is, tagged `[stated]` or `[inferred]`. + +Write each item's finding to `findings.md` as it is verified — the closed pass too, under its own heading, not straight into the catalog — in the catalog entry format from [deliverables.md](../../references/deliverables.md#the-auditissues-bundle), so phase 4 assembles the catalog rather than re-deriving it and an interrupted run loses one item rather than the phase. ## Phase 3 — relate