Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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/<p>/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/<p>/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.
5 changes: 4 additions & 1 deletion docs/conventions/native-references/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
21 changes: 21 additions & 0 deletions docs/conventions/plugin-data-report-keying/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 7 additions & 6 deletions docs/conventions/plugin-data-report-keying/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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/<project-slug>/` … The plugin data directory is per-plugin, not
Expand All @@ -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
Expand All @@ -120,8 +120,9 @@ writer; it obliges the next one not to repeat it.
| Read back and served to the operator | `<state-key>/<name>` **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: `<OutputBase>/reports/health-<UTC-timestamp>.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:
`<OutputBase>/reports/health-<UTC-timestamp>.md` — "one file per run, so a same-day rerun
does not overwrite the earlier report" — plus `<StateBase>/state/latest.json` and one appended line in
`<StateBase>/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
Expand Down
3 changes: 2 additions & 1 deletion docs/conventions/shell-test-helpers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
2 changes: 1 addition & 1 deletion docs/native-surfaces/records.json
Original file line number Diff line number Diff line change
@@ -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": [] },
Expand Down
Loading
Loading