From 0af73498caeda8de8614aed736b1140b593c08e8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 14:29:57 +0000 Subject: [PATCH 1/2] fix(docs): write down the test ADR 0018 was applied under, and close five wrong keeps The sweep in c66f26ce closed 16 cross-plugin path citations under ADR 0018 and kept 23 on a dividing test it stated only in its pull request. The test is not in the ADR. That omission is not academic: the same commit ruled a reach into a skill's private scripts/lib/ out of bounds in detector-findings 2.8.1 and left the byte-identical shape standing in shell-test-helpers, three files apart, on the same day. Five keeps re-derived and corrected, each verified against the tree first: - shell-test-helpers/README.md dropped the link to repo-hygiene:clean's scripts/lib/test-helpers.sh and names the copy instead, on 2.8.1's own remedy. The scripts/ entry-surface pointers later in that file resolve and stay. - plugin-data-report-keying/README.md drops three location pins (:97, :53-62, "steps 6 and 7"). All three resolved when checked, so none was broken yet; the pin is the part that rots, and detector-findings 2.7.1 already dropped a :414 pin as a class after it drifted. Each citation quotes its content inline, so the pin carried nothing. Patch bump to 1.0.1 per that contract's own rule. - docs/native-surfaces/records.json's note names /claude-ops:audit-native-overlap instead of addressing its private reference/canonical-pairs.json. The store is hand-editable by its own note, so the generation argument the sweep kept it on does not apply. overlap.py generate re-run: the rendered view is unchanged, as the note is store-only. ADR 0018 gains an amendment with three clauses it never carried: 1. The evidence/address test, stated. FIX a citation that is the doc's address for an obligation, since the binding fails exactly where it binds. KEEP one that is evidence about this tree at a moment in time. The ADR path-cites plugin skill privates five times on its own first page, so either it violates itself while stating the rule or clause 2 was always scoped to function rather than form. Its own five are recorded as evidence. 2. scripts/lib/ and any private subdirectory sit outside the entry-surface carve-out. Already ruled in detector-findings 2.8.1; a ruling that lives only in one convention's changelog is invisible to the next doc that needs it. 3. Plugin-level non-skill trees are outside the privacy question and inside clause 3. The population is recorded as measured, not swept. The sweep spec gains an open-remainder section: twelve citations c66f26ce created and never rostered (each fix wrote a changelog entry quoting the path it removed), proven disjoint from the three already counted by differencing the tree at the fix commit's parent, so the population is 16 fixed and 35 kept rather than 16 and 23. Also three trees excluded by fiat with no carve-out authorizing it, and the dometrain security review whose re-trigger fired at 0.1.0 and has gone unrun to 0.2.7. One roster correction: the loop-lane citation was reported at :14 and is at :69 today, release 9.1.0 having landed above it. Re-derive by text, not line. Refs #3468, refs #3460. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo --- ...capsulation-boundary-for-skill-citation.md | 94 +++++++++++++++++++ .../plugin-data-report-keying/CHANGELOG.md | 21 +++++ .../plugin-data-report-keying/README.md | 13 +-- docs/conventions/shell-test-helpers/README.md | 3 +- docs/native-surfaces/records.json | 2 +- docs/specs/extract-ssot-sweep-2026-08-28.md | 83 ++++++++++++++++ 6 files changed, 208 insertions(+), 8 deletions(-) diff --git a/docs/adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md b/docs/adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md index a90947fce7..de9db25644 100644 --- a/docs/adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md +++ b/docs/adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md @@ -226,3 +226,97 @@ in full. adds a `.claude/skills/` tree, since both premises of the distribution-unit argument would fail. It does not re-open on a request to relax cross-plugin citation: that is the case the contract is about and the evidence here does not touch it. + +## Amendment (2026-08-28) — the test clause 2 was applied under, and two surfaces it never named + +A remediation sweep applied clause 2 across `docs/**`, fixed 16 citations, kept 23, and stated its +dividing test only in the pull request that carried it. **The test is nowhere in this record.** It +has already cost something measurable: one commit ruled a reach into a skill's private `scripts/lib/` +out of bounds in one convention doc and left the byte-identical shape standing in another, three +files apart, on the same day. An unwritten test cannot be applied consistently, and was not. Three +things are recorded here so the next pass reads the rule instead of re-deriving it. + +### The dividing test: fix an address, keep evidence + +Clause 2 cannot mean every occurrence of a cross-plugin path, because **this document breaks that +reading on its own first page.** It path-cites another plugin's skill privates five times, three of +them the same `context/` file, which is private under any reading of the contract: +`plugins/docs-hygiene/skills/audit-encapsulation/context/public-surface-contract.md` at lines 19, 44 +and 108, that skill's `scripts/detect.sh` at 209, and +`plugins/source-control/skills/worktree/SKILL.md` at 222. Either this record violates itself in the +act of stating the rule, or clause 2 has always been scoped to what a citation *does* rather than to +the characters it contains. Only the second reading is coherent, and it is the reading every applied +fix in fact used. + +**FIX a citation that is the doc's address for an obligation.** The reader is being sent there to +get the rule the sentence declares itself bound by, and the path is missing for exactly the audience +the sentence binds: a contract fetched over `raw.githubusercontent.com` by an installed plugin, a +lane running with one plugin installed and not the other. The binding fails precisely where it +binds. Name the public `/plugin:skill` invocation instead, keeping any section name so a reader +inside this checkout still lands in the right place. + +**KEEP a citation that is evidence about this tree at a moment in time.** Three shapes qualify: + +- a **dated record** whose claim is what a named file contained on that date, a changelog entry + quoting the citation it just removed included; +- a **worked example whose content is quoted inline**, so the reader gets the point without + following the path; +- a **statement whose subject is the path**, where naming the invocation instead would delete the + assertion rather than relocate it. + +Evidence does not fail when the cited file moves; it becomes a record of something that was true, +which is what a dated record is for. **This document's own five citations are the second and third +kinds, and they stay.** So does the ADR-bodies refusal that the sweep record already carries on this +ground. + +The test governs function, not form, so it takes nothing away from clause 3. A kept citation still +has to resolve from the base its own form implies, and a kept citation that has rotted is a clause 3 +defect whatever it is evidence of. A pin *into* a kept citation, a `:97` or a `steps 6 and 7`, is the +part with no evidentiary value once the content is quoted inline, and it is the part that rots first: +drop it and keep the path. + +### A skill's `scripts/` is its entry surface; `scripts/lib/` is not + +The contract's carve-out lets harness surfaces, CI workflows, git hooks and automation registries +path-cite a skill's `scripts/` **entry scripts**. It reaches the scripts a caller is meant to invoke, +and stops there. **A private subdirectory beneath `scripts/`, `scripts/lib/` above all, is outside +the carve-out, and is private on the contract's own definition of a skill's non-public files.** It +holds implementation a +caller never names, so citing it buys a reader nothing an invocation would not, and costs the same +rename exposure the contract exists to prevent. + +This was already ruled, in +[`detector-findings` 2.8.1](../conventions/detector-findings/CHANGELOG.md), against a row citing +`plugins/docs-hygiene/skills/audit-noise/scripts/lib/noise-shapes.sh`, and its remedy is the one to +copy: name the thing in the terms the sentence already uses ("its shape library", "the scanner", +"`/repo-hygiene:clean`'s bundled test-helper copy") so the assertion is unchanged and survives the +next rename. It is recorded here because a ruling that lives only in one convention's changelog is +invisible to the next doc that needs it: the commit that wrote 2.8.1 left the byte-identical shape +standing in `docs/conventions/shell-test-helpers/README.md`, and a separate pass had to close it. + +### Plugin-level non-skill trees are outside this ADR's privacy question, and inside clause 3 + +`plugins/

/reference/`, `context/`, `hooks/`, `scripts/` and `agents/` sit **outside every skill +directory**. The public-surface contract defines privacy relative to a skill directory, so it does +not reach them, and neither does clause 2's encapsulation half. The repo has ruled on exactly one +such site, and only in a changelog: +[`detector-findings` 2.8.1](../conventions/detector-findings/CHANGELOG.md) kept +`plugins/review/reference/topic-docs.md` because "that file sits outside every skill directory" and +because the section around it argues explicitly for naming that file by repo path. + +**That ruling generalizes on privacy and does not generalize into permission.** Nothing in these +trees is private under the contract, so no citation of them is a clause 2 violation. Clause 3 still +binds every one of them, and so does the fetched-contract problem that motivated clause 2: a +marketplace-checkout-relative `plugins/

/reference/…` is not on disk for a reader who installed the +plugin, so a citation of that shape that functions as an **address** is still to be fixed under the +test above, on its own merits rather than as an encapsulation defect. The 2.8.1 keep survives that +test on its second leg, not its first. + +**The population is measured, and it is not swept here.** A verifier counted 51 distinct `docs/**` +citations into those trees. Re-derived independently for this amendment with a different expression: +59 occurrences at 48 distinct `path:line` sites in 16 files outside `docs/specs/`, `docs/topics/` and +`docs/adr/`, and 148 occurrences at 132 sites once those three are included. The counts disagree +because the shape has no single search expression, which is the point: **a later pass must re-derive +against the live tree and must not cite any of these numbers as a total.** They are recorded as a +measured order of magnitude, so that pass knows it is buying dozens of judgments rather than a +handful. diff --git a/docs/conventions/plugin-data-report-keying/CHANGELOG.md b/docs/conventions/plugin-data-report-keying/CHANGELOG.md index f5d667b03a..30846d43d3 100644 --- a/docs/conventions/plugin-data-report-keying/CHANGELOG.md +++ b/docs/conventions/plugin-data-report-keying/CHANGELOG.md @@ -5,6 +5,27 @@ is versioned by the `Version:` stamp in `README.md` (SemVer). A rule whose `[SPE tightens is a major bump; a new rule or a new named example is a minor bump; wording and adoption-table updates are a patch. +## 1.0.1 — 2026-08-28 + +Patch under this contract's own rule — wording only. No `[SPEC]` obligation tightens, no rule is +added, and no worked example is added or removed. + +- **Rule 1c's two worked examples and rule 2's reference implementation stop pinning a location + inside the cited file.** They read `plugins/bugs/skills/write/SKILL.md:97`, + `plugins/claude-config/skills/unhobble/SKILL.md:53-62`, and "steps 6 and 7" of + `plugins/machine-health/skills/audit/SKILL.md`. All three resolved when re-derived, so none was + broken yet; the pin is the part that rots, and this contract has already lost one to rot — + [`detector-findings` 2.7.1](../detector-findings/CHANGELOG.md) dropped a `:414` pin as a class + after finding it had drifted onto a comment five lines past the check it named. Each of these + three citations already quotes the content it is pointing at, so the pin was carrying nothing the + sentence did not. The paths and the plugin name stay, the quotes stay, and the machine-health + citation now names what its procedure does — renders the report, then updates state — instead of + two step numbers that renumber on the next inserted step. +- **The citations themselves stay, and [ADR 0018](../../adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md)'s + amendment of the same date says why.** Each is evidence about this checkout — a worked example + whose content is quoted inline — rather than the address a reader must visit to get a rule. The + amendment states that test, which until now was applied without being written down. + ## 1.0.0 — 2026-08-12 Initial published contract. Written because the hazard was already understood inside the fleet and diff --git a/docs/conventions/plugin-data-report-keying/README.md b/docs/conventions/plugin-data-report-keying/README.md index b7b85b16ea..ad3d2d7014 100644 --- a/docs/conventions/plugin-data-report-keying/README.md +++ b/docs/conventions/plugin-data-report-keying/README.md @@ -1,7 +1,7 @@ # Plugin-data report keying, retention, and overwrite -Version: 1.0.0 -Last updated: 2026-08-12 +Version: 1.0.1 +Last updated: 2026-08-28 A marketplace-wide contract for **how a plugin names what it writes under `${CLAUDE_PLUGIN_DATA}`** — the key, the retention shape, and whether a write may overwrite. It does not govern *what* may live @@ -95,7 +95,7 @@ skill's namespace during review. ### 1c — the "looks scoped but isn't" case, named so it is not repeated -`plugins/bugs/skills/write/SKILL.md:97` keys on the **kebab-cased basename of the project +`plugins/bugs/skills/write/SKILL.md` keys on the **kebab-cased basename of the project root**: > `${CLAUDE_PLUGIN_DATA}/bug-reports//` … The plugin data directory is per-plugin, not @@ -105,7 +105,7 @@ root**: The line states the hazard correctly and then picks a colliding key: two same-named checkouts — a fork, a same-named worktree, `~/work/api` and `~/oss/api` — share one slug directory, and the duplicate scan cross-matches between them. It escapes *overwrite* only because its filenames are -timestamped. `plugins/claude-config/skills/unhobble/SKILL.md:53-62` names the same insufficiency in +timestamped. `plugins/claude-config/skills/unhobble/SKILL.md` names the same insufficiency in prose: "`${CLAUDE_PLUGIN_DATA}` is machine-global, so two checkouts sharing a basename…". **This is recorded here as the worked example, not filed as a `bugs` defect.** A basename is @@ -120,8 +120,9 @@ writer; it obliges the next one not to repeat it. | Read back and served to the operator | `/` **and** the read must derive the same key | Serving the newest ≠ serving this project's | | A trend or a history | one file per run **plus** an appended line | A same-day rerun must not erase the earlier point | -The reference implementation of the third row is `plugins/machine-health/skills/audit/SKILL.md`, steps -6 and 7: `/reports/health-.md` — "one file per run, so a same-day rerun +The reference implementation of the third row is `plugins/machine-health/skills/audit/SKILL.md`, where +its procedure renders the report and then updates state: +`/reports/health-.md` — "one file per run, so a same-day rerun does not overwrite the earlier report" — plus `/state/latest.json` and one appended line in `/state/history.jsonl`, "the trend source of truth". Note it is *not* an adopter of rule 1: its roots are passed in explicitly by the caller rather than keyed, for a reason that file states — a diff --git a/docs/conventions/shell-test-helpers/README.md b/docs/conventions/shell-test-helpers/README.md index fd8163a601..1e47ae1483 100644 --- a/docs/conventions/shell-test-helpers/README.md +++ b/docs/conventions/shell-test-helpers/README.md @@ -24,7 +24,8 @@ below are not that: they are already three genuinely different shapes, not one l [`claude-ops/hooks/claude-ops-test-helpers.sh`](../../../plugins/claude-ops/hooks/claude-ops-test-helpers.sh). - **Skill-script shape** (`pass`/`fail`, `FAILED`/`CASE_NUM` counters, file-existence assertions): [`source-control/scripts/test-helpers.sh`](../../../plugins/source-control/scripts/test-helpers.sh), - [`repo-hygiene/skills/clean/scripts/lib/test-helpers.sh`](../../../plugins/repo-hygiene/skills/clean/scripts/lib/test-helpers.sh). + and `/repo-hygiene:clean`'s bundled test-helper copy — named rather than linked because it sits + under that skill's private `scripts/lib/`, not on the entry surface the pointers below reach. - **Vendored-seam shape** (same `pass`/`fail` primitives, but owned by the seam itself so it stays correct wherever the seam is resolved from — bundled or consumer-vendored — independent of this repo's tooling): [`work-items/tools/work-item-tracker/tests/lib.sh`](../../../plugins/work-items/tools/work-item-tracker/tests/lib.sh). diff --git a/docs/native-surfaces/records.json b/docs/native-surfaces/records.json index f327c397ed..e77166f636 100644 --- a/docs/native-surfaces/records.json +++ b/docs/native-surfaces/records.json @@ -1,6 +1,6 @@ { "schema": 1, - "note": "SSOT for native-surface overlap verdicts. Hand-editable and human-gated: every verdict here was written by a person, never by a run. docs/NATIVE-SURFACES.md is generated from this file and is never the source. Seeded candidates live in plugins/claude-ops/skills/audit-native-overlap/reference/canonical-pairs.json; a candidate becomes a row only when someone rules on it. Phrasing rules for anything baked out of these rows: docs/conventions/native-references/README.md.", + "note": "SSOT for native-surface overlap verdicts. Hand-editable and human-gated: every verdict here was written by a person, never by a run. docs/NATIVE-SURFACES.md is generated from this file and is never the source. Seeded candidates come from the canonical-pair seed bundled with /claude-ops:audit-native-overlap; a candidate becomes a row only when someone rules on it. Phrasing rules for anything baked out of these rows: docs/conventions/native-references/README.md.", "rows": [ { "native": { "name": "code-review", "class": "bundled-skill", "markers": [] }, diff --git a/docs/specs/extract-ssot-sweep-2026-08-28.md b/docs/specs/extract-ssot-sweep-2026-08-28.md index 2a7b9133de..a910ebeea8 100644 --- a/docs/specs/extract-ssot-sweep-2026-08-28.md +++ b/docs/specs/extract-ssot-sweep-2026-08-28.md @@ -32,6 +32,7 @@ having moved to `:16` when #3380 inserted a `## Contents` index above it. - [Verified, not applied](#verified-not-applied) - [Contradictions found](#contradictions-found) - [New refusals, recorded so nobody re-opens them](#new-refusals-recorded-so-nobody-re-opens-them) +- [Open remainder after the encapsulation close](#open-remainder-after-the-encapsulation-close) - [Recall limits this run declares](#recall-limits-this-run-declares) ## What this run did differently @@ -328,6 +329,88 @@ Beyond the predecessor's 13. Each was resolved against the real files and refuse - Plus roughly 120 further per-cluster refusals across the eight triage batches, each recorded with its form and ground in the pull request that carries this file. +## Open remainder after the encapsulation close + +Recorded, not fixed. A later pass adjudicates these; this section exists so it does not have to +re-find them. Stamped 2026-08-28 and subject to the decay rule at the top of this file. + +### Twelve citations the encapsulation close created and never rostered + +The pass that closed the encapsulation floor wrote a convention CHANGELOG entry for each fix, and +every entry quotes the path it removed. Those quotes are themselves `docs/**` citations of the exact +shape the pass was sweeping, created by the sweep, counted by nobody. **Verified against +`origin/main`, twelve of them:** + +| File | Lines | +|---|---| +| `docs/conventions/config-cascade/CHANGELOG.md` | 12, 13, 14 | +| `docs/conventions/detector-findings/CHANGELOG.md` | 44, 57 | +| `docs/conventions/loop-lane/CHANGELOG.md` | 69 | +| `docs/conventions/native-references/CHANGELOG.md` | 16, 17, 18 | +| `docs/conventions/permission-rule-hygiene/CHANGELOG.md` | 14 | +| `docs/conventions/topic-docs/CHANGELOG.md` | 11, 12 | + +One correction to the roster this was handed as: the `loop-lane` row was reported at `:14`, which is +where it sat when the pass wrote it. Release `9.1.0` landed above it, and the citation is at `:69` +today, inside the `9.0.2` entry. That is the decay rule firing on a record less than a day old, and +it is why a later pass must **re-derive by the text and never by these numbers.** + +**The twelve are disjoint from the three this file already counts, confirmed by differencing the +tree at the fix commit's parent.** Before that commit, exactly three lines in +`docs/conventions/*/CHANGELOG.md` cited a plugin skill by path: two in `detector-findings` and one in +`loop-lane`. Those are the "three dated changelog entries that quote a citation as it stood" in the +kept set. Every one of the twelve above was written by the fix commit itself, so **the arithmetic is +16 fixed and 35 kept, not 16 and 23**, and the changelog-evidence class is fifteen rather than three. + +**All twelve are keep-correct** under the test now written into +[ADR 0018](../adr/0018-treat-the-plugin-as-the-encapsulation-boundary-for-skill-citation.md)'s +amendment: each is a dated changelog entry whose whole claim is what a named file contained on that +date. Nothing here needs an edit. What is missing is the judgment, and a pass that re-derives this +shape finds twelve rows nobody ruled on and has to adjudicate them from scratch to learn they were +fine. The general lesson is worth more than the twelve rows: **a sweep that documents each fix by +quoting the citation it removed manufactures new instances of the shape it is sweeping,** so its own +output has to be swept before the count is closed. + +**The pass writing this section did the same thing, deliberately, three more times.** +`docs/conventions/plugin-data-report-keying/CHANGELOG.md` 1.0.1 quotes the three pins it dropped, so +the class stands at eighteen. They are keep-correct on the same ground as the twelve, and they are +declared here rather than left for the next re-derivation to find. That is the only discipline +available: the alternative is a changelog entry that does not say what it changed. + +### Three trees the sweep excluded by fiat + +The sweep's encapsulation floor scoped itself to `docs/**` and then dropped five subtrees. Two of the +exclusions are defensible and stated as such: `docs/SKILL-CHEAT-SHEET.md` (162 citations, generated +and CI drift-checked, so an edit is reverted by its generator) and `docs/upstream/` (vendored, not +this repo's prose to style). **The other three rest on nothing.** `docs/specs/` (roughly 265 sites), +`docs/topics/` (roughly 43) and `docs/adr/` (25) were excluded on the assertion that "the dated +records under `docs/specs/`, `docs/adr/` and `docs/topics/` are out of scope by the same test", and +no carve-out in ADR 0018 authorizes any of it. + +The assertion is *probably* right and is *not* established. Under the amendment's test most of that +population is evidence: dated records, and ADR bodies that quote what they cite. But "most of a +265-site population is evidence" is a hypothesis about 265 sites, argued from the tree a citation +lives in rather than from what the citation does, which is the form-over-function reasoning the +amendment rejects. A tree-shaped exclusion is a carve-out; ADR 0018 grants none. Either the pass that +resumes this samples the three trees and records the ruling, or the ADR gains the carve-out +explicitly. It should not stay a habit. + +Counts re-derived for this record and rounded deliberately: the exact figure moves with the search +expression, and per this file's own recall-limits discipline none of these numbers is a total. + +### A stale record found in passing, not an ADR matter + +`docs/MIGRATION-PLAYBOOK.md:943` carries the `dometrain` security-review record: "Reviewed at +`0.1.0`; a version bump adding a new trust surface re-triggers this review." **The plugin's manifest +reads `0.2.7` today** (`plugins/dometrain/.claude-plugin/plugin.json`, confirmed against +`origin/main`). The trigger has had every opportunity to fire across those bumps and the review was +never re-run, so the record asserts a currency it does not have. + +This is not a citation defect and does not belong to the encapsulation lane. It is logged here +because the encapsulation pass kept that record's five file-and-frontmatter citations *on the +strength of* its re-trigger clause, which means the clause was read and its own condition was not +checked. Whoever re-runs the review owns the record; the citations are fine either way. + ## Recall limits this run declares A finding count read as a defect count is worse than no count. An adversarial verifier audited the From cc328c52d8dbfda1a07892c026157befdc9a20b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 14:33:39 +0000 Subject: [PATCH 2/2] docs: correct two claims a fifth review round found, both stale statuses Round five of an adversarial claims audit. Both findings are the same defect family as rounds three and four: a status trusted instead of re-derived, in a document whose own decay rule forbids exactly that. "34 to 32" was wrong, and wrong in an instructive way. The claim appeared in the sweep record and in native-references' changelog: that closing V-review-13 and V-review-14 took the predecessor's L4 roster from 34 open rows to 32. Re-deriving all 34 against the roster's own text test -- is the cited private surface still named at the citing path? -- finds 22 of the other 32 already closed before this change set began. Twelve of them were closed by #3380, the same commit that last wrote the roster, which left its own line "All 34 path:line citations were re-verified on 2026-08-26 and every one resolves" standing over twelve citations it had just deleted. This change set read that status as fact and republished it, in a file that says at line 18 "the check is the text, never the status and never the line number". What actually remains of the 34 is Group 2's eight intra-plugin path-form defects, untouched here. The spec also still shipped the probe shape #3469 removed, presented as verified. It showed probe >/dev/null 2>&1 && probe | filter | head -N || echo TOKEN and stated it was verified by execution in three states: token outside a repository, nothing when the filter matches nothing, and the cap holding above it. Under set -o pipefail two of those three states print the token instead, which is why docs-hygiene 0.21.24 and code-tidying 0.14.13 replaced the form with the pipeline in a brace group closed by `:`. The plugin changelogs recorded that correctly; the spec is the resumable record a later pass reads, and it carried the superseded shape with no pipefail qualifier at all -- the word appears nowhere in docs/. Both the shape and the verification claim are corrected, and the qualifier is now stated as the lesson: a verification that fixes the shell's options and does not say so proves less than it appears to. The commit message of c66f26ce carries the same "34 to 32" error and cannot be edited; this entry is the correction of record. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UmrZGdp1dgbZuPCy7tcRJo --- .../native-references/CHANGELOG.md | 5 ++- docs/specs/extract-ssot-sweep-2026-08-28.md | 31 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/docs/conventions/native-references/CHANGELOG.md b/docs/conventions/native-references/CHANGELOG.md index 25774cac4d..f9f278f355 100644 --- a/docs/conventions/native-references/CHANGELOG.md +++ b/docs/conventions/native-references/CHANGELOG.md @@ -27,6 +27,9 @@ internals become public invocations. - **Two of the three were standing findings.** They are `V-review-13` and `V-review-14` in [`docs-hygiene-sweep-unapplied-remediations.md`](../../specs/docs-hygiene-sweep-unapplied-remediations.md)'s L4 group of 34, recorded open on 2026-08-26 and unapplied since. That roster is a point-in-time - record and is not edited here, per its own decay rule; it is down to 32. Found by the whole-repo + record and is not edited here, per its own decay rule, and re-deriving it against its own text + test shows most of it is already closed: these two were the last open rows of its 24-row Group 1, + twelve of the rest having been closed by #3380 itself, and its eight Group 2 rows remain. Found by + the whole-repo extract-ssot sweep's encapsulation floor, which re-derived the shape rather than trusting the roster and reached the third site the roster did not carry. diff --git a/docs/specs/extract-ssot-sweep-2026-08-28.md b/docs/specs/extract-ssot-sweep-2026-08-28.md index a910ebeea8..3625838ce9 100644 --- a/docs/specs/extract-ssot-sweep-2026-08-28.md +++ b/docs/specs/extract-ssot-sweep-2026-08-28.md @@ -172,11 +172,16 @@ filtered probes print nothing. block to carry no `$` expansion other than a bare `$HOME`: an expansion leaves the composed block unverifiable to the worktree-isolation guard, and the skill then fails to load from an isolated agent (`session-flow` 0.17.16). `audit-derivability` demonstrates the binding, not a form to copy. -All five sites took an equivalent that adds no `$` at all and heads the same `&&` list with a -status-only run of the site's own probe: +All five sites first took an equivalent that adds no `$` at all and heads the same `&&` list with a +status-only run of the site's own probe. **That form was itself wrong, and is no longer what +ships.** It is correct only without `set -o pipefail`; under pipefail the `&&` list inherits the +pipeline's status, which both `grep` matching nothing and `git` taking SIGPIPE at the cap make +non-zero, firing the failure token on a healthy probe. `docs-hygiene` 0.21.24 and `code-tidying` +0.14.13 replaced it with the pipeline inside a brace group closed by `:`, a command that cannot +fail, which is correct under both settings: ```text -git status --porcelain >/dev/null 2>&1 && git status --porcelain 2>/dev/null | … | head -N || echo "(git status unavailable)" +git status --porcelain >/dev/null 2>&1 && { git status --porcelain 2>/dev/null | … | head -N; :; } || echo "(git status unavailable)" ``` The probe runs twice, which is the price of not capturing it. No line holds a `$` expansion after @@ -184,11 +189,14 @@ the change: the `$` characters that remain are end-of-line anchors in single-quo and field references in single-quoted `awk` programs, all pre-existing and none of them expanded by the shell. Each site kept its own filter, its own cap and its own label noun; each label gained `empty = none`; the failure token is the `(git status unavailable)` string the normalized status -probes already use. Verified by execution in three states per site: outside a repository each prints -the token, inside a repository whose dirty files do not match the filter each prints nothing, and -above the cap the cap holds with no spurious fallback. Landed in `docs-hygiene` 0.21.23 and -`code-tidying` 0.14.12, which also carry the two `detect.test.sh` extractors that read these lines -out of `SKILL.md` and were anchored on the old labels. +probes already use. Verified by execution in three states per site, **without pipefail**: outside a +repository each prints the token, inside a repository whose dirty files do not match the filter each +prints nothing, and above the cap the cap holds with no spurious fallback. That qualifier is the +whole lesson: the same three states under pipefail print the token in two of them, which is what +0.21.24 and 0.14.13 had to correct. A verification that fixes the shell's options and does not say +so proves less than it appears to. Landed in `docs-hygiene` 0.21.23 and `code-tidying` 0.14.12, +which also carry the two `detect.test.sh` extractors that read these lines out of `SKILL.md` and +were anchored on the old labels. One filtered probe with the unreachable shape remains open, and is out of this section's scope because its probe is not `git status`: @@ -474,8 +482,11 @@ figures to differ. this very change set added, written in the bare plugin-relative form ADR 0018 names as its real defect class), two conformance rows in `docs/conventions/config-cascade/README.md`, one in `docs/conventions/pre-pr-ordering/README.md`, three in - `docs/conventions/native-references/README.md` (two of them `V-review-13` and `V-review-14` off - the predecessor's 34-item L4 roster, which is therefore down to 32), and one adopter-row detail + `docs/conventions/native-references/README.md` (two of them `V-review-13` and `V-review-14`, the + last two rows of the predecessor's 34-item L4 roster still open by that roster's own text test: + re-derivation found 22 of its other 32 rows already closed, twelve of them by #3380 itself, so + what remains of the 34 is its eight Group 2 intra-plugin path-form defects, untouched here), and + one adopter-row detail in `docs/conventions/detector-findings/README.md`. **What remains is a judgment set, not a backlog**: 23 further in-shape citations were kept with reasons, because each is evidence about this checkout rather than an address for an obligation. They are the three worked examples in