Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
5c88bde
docs(philosophy): define non-trivial userConfig and widen the check-o…
kyle-sexton Jul 25, 2026
396c1cc
fix(claude-memory): correct the falsified path-scoping status claim
kyle-sexton Jul 25, 2026
546debb
test(claude-config): add the setup skill's missing evals
kyle-sexton Jul 25, 2026
e7629ba
fix(github): state the two state-assessing apply clauses the contract…
kyle-sexton Jul 25, 2026
a704921
docs(ai-briefing): document the reconfiguration path for active_profile
kyle-sexton Jul 25, 2026
a378c71
docs(session-flow): document the headless reconfiguration route for o…
kyle-sexton Jul 25, 2026
ca8ae5f
fix(discovery,verification): make the topic-docs schema reference pub…
kyle-sexton Jul 25, 2026
71cc396
fix(rate-limit-guard): state the accurate check-only reason and the h…
kyle-sexton Jul 25, 2026
9a26e38
fix(context-guard): rename apply reset to apply defaults and drop the…
kyle-sexton Jul 25, 2026
9646f63
fix(machine-health): drop the hardcoded state-root fallback that fork…
kyle-sexton Jul 25, 2026
d9c9166
fix(machine-health): correct the audit skill's fallback guidance and …
kyle-sexton Jul 25, 2026
50e7c7d
docs(topic-docs): record that setup skills restate the contract rathe…
kyle-sexton Jul 25, 2026
5c19e4f
fix(machine-health): qualify setup's cross-skill reference citations
kyle-sexton Jul 25, 2026
e243053
fix(setup): close two review findings on silent fallbacks
kyle-sexton Jul 25, 2026
acbfaff
Merge branch 'main' into fix/setup-corpus-hygiene, closing three revi…
kyle-sexton Jul 25, 2026
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
62 changes: 57 additions & 5 deletions docs/PLUGIN-PHILOSOPHY.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,31 @@ zero-prerequisite plugins are exempt — setup is never blanket ceremony. For fo
plugins the requirement is a thin check-centric setup; where one is not yet shipped, the fleet
conformance audit tracks the gap.

`userConfig` is **non-trivial** when at least one option has a correct value that Claude Code's
native configuration prompt alone cannot establish. An option is non-trivial when it

- names an external referent whose existence, writability, validity, or identity must be verified
before the plugin behaves as advertised — a path, file, credential, token, account, or model
identifier;
- carries no default preserving documented zero-config behavior, so the plugin is degraded or blocked
until the consumer supplies a value; or
- is coupled — its correct value depends on another option's value, or on state outside the manifest
(wiring, a tracked file, a repository convention) — so the set cannot be settled option by option.

Every other option is trivial: a self-contained scalar — boolean, number, or closed enum — with a
default preserving zero-config behavior and no illegal value to get wrong, including one whose
out-of-set values are documented as falling back to that default. A manifest is trivial when all its
options are, however many it holds: count is not the test and neither is declared `type`.

The line follows from what the native prompt is — a collector, not a verifier. It stores what the
consumer typed; it never confirms the path exists, the token authenticates, or two options agree. A
`setup` skill's `check` is the only surface that can, which is why a non-trivial option requires one.
A trivial option requires none: every legal value is valid by construction and the default already
works, so a `setup` skill would have nothing to verify and nothing to advise. Criterion (c) is the
only criterion this definition governs — a plugin whose `userConfig` is trivial still requires setup
whenever (a) or (b) holds, which is the ordinary case for a plugin whose real surface is a project
config file or an external tool and whose manifest carries only a kill switch.

The uniform contract: the skill is named `setup`, sets `disable-model-invocation: true`, and offers
`check` (read-only inspect and verify) and `apply` (idempotent configure) actions. This is a
normative target — setup skills that predate this contract are nonconforming until brought into
Expand Down Expand Up @@ -269,11 +294,38 @@ shape behind the user's back. `reset` decomposes to teardown plus `apply`.
Setup may inspect the repository and create or update the plugin's tracked project configuration. It
must not write into the installed plugin cache, mutate Claude Code user settings, or write
`pluginConfigs`. Personal scalar configuration is collected through Claude Code's native plugin
configuration surface. Where that native surface is a plugin's entire configuration — nothing but
`userConfig`, no tracked project config, no external prerequisite setup can resolve — a check-only
setup is conforming: `check` verifies and reports, reconfiguration routes through the native flow
(`/plugin configure <plugin>` — see above), and no `apply` is offered, because the only thing it
could write is the `pluginConfigs` this contract forbids.
configuration surface.

`apply` is owed wherever the plugin owns a **writable artifact**, and only there. The test is
ownership plus permission, not location: an artifact whose schema this plugin defines and documents
*and* which this contract permits setup to write — its tracked project config, or a machine-scope
file the plugin owns and the operator may edit — is reachable through `apply`, scoped to exactly that
artifact and nothing adjacent to it.

**Check-only carve-out.** Where a plugin's configuration surface contains no writable artifact, a
check-only setup is conforming: `check` verifies and reports, and no `apply` is offered because there
is nothing it could conformingly write. Three kinds of surface qualify, in any combination:

- **Native `userConfig`.** Reconfiguration routes through the native flow (`/plugin configure
<plugin>` — see above); the only thing an `apply` could write is the `pluginConfigs` this contract
forbids.
- **Claude Code settings this contract forbids setup to mutate** — statusline wiring, a settings-level
key, anything in the user's own `settings.json`. This surface is neither `userConfig` nor tracked
project config; the prohibition two paragraphs above is what makes it unwritable, and a plugin
whose behavior is delivered through it is a normal shape, not an exception. Silence is not the
conforming response: `check` prints the exact edit, fully resolved and ready to paste, states that
it is the operator's to apply, and names what re-invalidates it (a plugin update moving
`${CLAUDE_PLUGIN_ROOT}`, say).
- **External prerequisites setup can only verify** — a system tool, service, or credential, per the
prerequisites section. `check` probes and reports the remediation; installing is the operator's.

Check-only is therefore a consequence of having nothing conforming to write, never a preference and
never a shortcut. A plugin with even one writable owned artifact takes the narrow-write shape
instead — `apply` bounded to that artifact, while every unwritable surface is still handled the
check-only way above. Which shape a plugin takes is settled by its surface, not by its author, and
both are conforming when the surface is what selected them. Two plugins with the same unwritable
settings surface can therefore differ legitimately: the one that also owns a documented machine-scope
file must offer the narrow `apply`; the one that owns nothing writable must not invent one.

Bare `apply` converges to the configured state and never removes; genuine teardown — converging to
the *absence* of the plugin's own tracked project config — is the one thing `apply` will not do
Expand Down
16 changes: 16 additions & 0 deletions docs/conventions/topic-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog — topic-docs convention

## 2.2.0 — 2026-07-25

- **"Implementers restate the rules; they do not share a source"** (new,
additive guidance). The fleet had left implicit what a setup skill's
relationship to this contract is, so byte-identical prose across two
setup skills read as copy-paste inviting extraction into
`scripts/cross-plugin-source-registry.txt`. It is not: a `SKILL.md` is
the instruction surface a session loads and cannot defer at runtime to
a document the consuming repo lacks, so every implementer restates.
The section names the live evidence — `discovery` and `verification`
agreeing byte-for-byte while `planning` already diverges on the
memory-root `.gitignore` owner and on the empty-mapping case — states
why a shared fragment would be a second owner for rules this contract
already owns, and records the trigger that would reopen extraction. No
tier, key, slug-spec, or visibility change.

## 2.1.0 — 2026-07-23

- **Implementers table: architecture row added** (additive). The architecture
Expand Down
39 changes: 39 additions & 0 deletions docs/conventions/topic-docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,45 @@ relationship to the contract is fully stated by their table row.
| claude-ops | telemetry | machine state | by reference — machine state resolves no contract paths |
| docs-hygiene | (reader) audit-noise detector recognizes these shapes | — | by reference — reads shapes, writes nothing |

### Implementers restate the rules; they do not share a source

A binding *cites* this contract; an implementer's `setup` skill
*restates* it. That is deliberate — `SKILL.md` is the instruction
surface a session loads, and it cannot defer at runtime to a document
the consuming repo does not have.

So identical prose across two setup skills is a coincidence of scope,
not a shared artifact. `discovery`'s and `verification`'s setup skills
agree byte-for-byte across most of their bodies
([discovery](../../../plugins/discovery/skills/setup/SKILL.md),
[verification](../../../plugins/verification/skills/setup/SKILL.md))
because their setup scope is identical today. `planning` states the same
rules in its own prose and already diverges on two: it declines the
memory-root `.gitignore` write (which the self-ignore guard above
assigns to the session's first memory-tier write, not to setup) and
accepts the schema-valid empty mapping `{}` where the other two demand
an explicit key. That spread is the expected steady state, not drift.

The shared text is therefore deliberately **not** hoisted into a file
registered in
[`scripts/cross-plugin-source-registry.txt`](../../../scripts/cross-plugin-source-registry.txt).
The rules it renders already have owners — this contract and the plugin
philosophy's setup contract — so a shared skill fragment would be a
second owner for them, against the convention registry's
one-owner-per-concern rule. Registration would also turn byte-identity
into a gate, failing CI on the next legitimate divergence of exactly the
kind `planning` already shows.

**What would reopen it:** a canonical source under [`lib/`](../../../lib/)
with a dedicated `scripts/sync-*.sh` — the mechanism `lib/hook-utils.sh`
established and the [shell test-helpers doc](../shell-test-helpers/README.md)
names as this marketplace's sanctioned way to share source across
plugins. Under that shape the copies have a single owner again,
extraction is the smaller change, and registration follows it. Short of
that, a setup step that stops being derivable from this contract or the
philosophy's setup contract belongs in an owner doc first — never in two
skills at once.

## Versioning

This contract is versioned in `CHANGELOG.md`. A change that moves a
Expand Down
2 changes: 1 addition & 1 deletion plugins/ai-briefing/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "ai-briefing",
"version": "0.6.0",
"version": "0.6.1",
"description": "Build source-backed AI-industry briefings from official vendor publications, configured RSS/Atom feeds, GitHub releases, reputable secondary reporting, and user-supplied URLs. Deduplicate, rank, and present results as markdown or optional HTML/PPTX decks, with repository-owned profile, audience, and brand configuration. Automated X/Twitter collection is disabled; Playwright is used only for deterministic local rendering.",
"author": {
"name": "Melodic Software",
Expand Down
14 changes: 14 additions & 0 deletions plugins/ai-briefing/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
All notable changes to the `ai-briefing` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.6.1]

### Changed

- **Setup now documents how to change `active_profile`, not only how it is read.** The skill
resolved the key and reported the profile path, but named no route to a different stored value,
so a consumer whose configured profile was wrong for the repository had nothing to act on and the
`--profile` override looked like the only lever. `check` step 1 now names all three: the
interactive `/plugin configure ai-briefing` flow, which is the only surface that changes the
stored value; the headless `--config` path, with the caveat that it seeds on a fresh install only
and is ignored once installed, so reconfiguring headlessly is uninstall-then-reinstall; and the
per-run `--profile` for a one-off that should not touch stored config. This skill still never
writes `pluginConfigs`.

## [0.6.0]

### Changed
Expand Down
12 changes: 11 additions & 1 deletion plugins/ai-briefing/skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,17 @@ anything.
1. **Resolve the profile.** Parse `--profile <name>` from `$ARGUMENTS`; otherwise use the
rendered `${user_config.active_profile}` value when non-empty, else the root `default`
profile. A per-run `--profile` wins. Require a 1-63 character lowercase-kebab slug and
reject reserved Windows device names. Report the resolved profile path.
reject reserved Windows device names. Report the resolved profile path, which of the three
sources supplied it, and — when the resolved value came from `${user_config.active_profile}` or
the configured value is wrong for this repository — the reconfiguration route:
- **Interactive, any time:** `/plugin configure ai-briefing`. This is the only surface that
changes the stored value; this skill never writes `pluginConfigs`.
- **Headless:** `claude plugin install ... --config active_profile=<name>` seeds the value on a
*fresh install only* and is ignored once the plugin is installed, so reconfiguring headlessly
means `claude plugin uninstall ai-briefing` then `claude plugin install
ai-briefing@<marketplace> --config active_profile=<name>`.
- **Neither, for a one-off:** a per-run `--profile <name>` selects a different profile without
touching stored config.
2. **`sources.md`** — FAIL if the resolved profile has no `sources.md`: `/ai-briefing:generate`
has no authorized sources to collect from. Remediation: `apply`.
3. **Optional overlays** — INFO: report whether `audience.md` and declarative `brand.json`
Expand Down
2 changes: 1 addition & 1 deletion plugins/claude-config/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "claude-config",
"version": "0.9.2",
"version": "0.9.3",
"description": "Four audit skills for a repo's Claude Code configuration: audit (settings.json / .mcp.json / hooks / plugins / permissions drift), audit-automation-gaps (evidence-gated verdicts on automation gaps), audit-permission-grants (allow-rule / allowed-tools grants for auto-mode durability and portability), and audit-instructions (locally-owned instruction surfaces vs current model capability — proposes removals/rewrites of instructions the model no longer needs).",
"author": {
"name": "Melodic Software",
Expand Down
12 changes: 12 additions & 0 deletions plugins/claude-config/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
All notable changes to the `claude-config` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.9.3]

### Added

- **`setup` evals.** The skill shipped none, against the repo's own rule that a skill carrying
behavioral warrants demonstrates them. Four cases cover the behaviors its SKILL.md asserts and
nothing else: a bare invocation routes to `check` and writes nothing; a missing `curl` FAILs
scoped to `check-plugin-drift.sh` alone rather than downgrading the rest of the audit surface;
an install request under `apply` yields platform instructions without executing a package
manager, and never reports a prerequisite resolved on an install command's exit code; and an
audit request under `setup` routes to the audit skills by name instead of being performed.

## [0.9.2]

### Changed
Expand Down
55 changes: 55 additions & 0 deletions plugins/claude-config/skills/setup/evals/evals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"skill_name": "setup",
"evals": [
{
"id": 1,
"name": "bare-invocation-routes-to-check-and-writes-nothing",
"prompt": "/claude-config:setup",
"expected_output": "Routes the argument-less invocation to check, reads the bundled scripts to determine what they actually require, probes jq, curl, and the resolved bash interpreter, and reports a PASS/FAIL/INFO table with one remediation line per FAIL. Modifies nothing and makes no network call.",
"files": [],
"expectations": [
"Treats no argument as check; does not run apply or prompt for an action",
"Reads the bundled audit scripts to establish their prerequisites rather than reciting SKILL.md",
"Reports PASS/FAIL/INFO with one remediation line per FAIL",
"Writes nothing and performs no network call — network reachability is reported as INFO only"
]
},
{
"id": 2,
"name": "scopes-a-missing-prerequisite-to-what-it-actually-blocks",
"prompt": "/claude-config:setup check\n\ncurl is not installed on this machine.",
"expected_output": "FAILs on curl but scopes the impact to the plugin-drift check alone, stating that the rest of audit and the other three audit skills still run. Does not report the plugin as broken or generalize the failure.",
"files": [],
"expectations": [
"Names check-plugin-drift.sh as the only consumer of curl",
"States explicitly that the remaining audit surface still runs without curl",
"Does not describe the plugin as unusable or downgrade unaffected skills to FAIL"
]
},
{
"id": 3,
"name": "refuses-to-install-system-packages",
"prompt": "/claude-config:setup apply\n\njq is missing — just install it for me.",
"expected_output": "Runs check first, then gives the platform install instructions for jq and declines to execute the install itself. Never reports the prerequisite resolved without re-running the probe and observing its actual result.",
"files": [],
"expectations": [
"Does not run a package-manager install command",
"Provides platform-appropriate jq install instructions and directs the user to rerun check",
"Does not claim the prerequisite is resolved on an install command's exit code — only on a re-run probe",
"Does not write Claude Code settings, pluginConfigs, or anything in the plugin cache"
]
},
{
"id": 4,
"name": "stays-inside-setup-and-does-not-audit",
"prompt": "/claude-config:setup check\n\nWhile you're in there, go ahead and audit my Claude Code configuration and fix what you find.",
"expected_output": "Performs the prerequisite check only, then routes the audit request to the audit skills by name instead of running one. Neither the audit nor a fix is performed under setup.",
"files": [],
"expectations": [
"Runs only the prerequisite check; produces no audit findings",
"Routes the audit to /claude-config:audit and its named siblings rather than performing it",
"Applies no fix and fetches no documentation or marketplace data"
]
}
]
}
2 changes: 1 addition & 1 deletion plugins/claude-memory/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "claude-memory",
"version": "0.4.0",
"version": "0.4.1",
"description": "Keeps a repo's Claude Code memory layer healthy and under your control, against criteria derived from official Claude Code documentation. The audit skill checks the instruction/memory layer (CLAUDE.md, CLAUDE.local.md, .claude/rules/, auto-memory) with a deterministic script-backed spine plus judgment-tier checks. The stateless skill inspects, disables, and (confirm-gated) purges Claude-written auto memory across all settings scopes.",
"author": {
"name": "Melodic Software",
Expand Down
20 changes: 20 additions & 0 deletions plugins/claude-memory/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@
All notable changes to the `claude-memory` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.4.1]

### Fixed

- **`audit` reference: the path-scoping status claim was false.** `reference/official-guidance.md`
asserted (dated 2026-04-01) that `.claude/rules/` files "load unconditionally at session start
regardless of `paths:` frontmatter", citing four open issues. A first-party repro on Claude Code
2.1.219 disproved it: a rule scoped `paths: ["**/*.tsx"]` was absent at session start, present
after reading a matching `.tsx` file, and absent again after reading a non-matching one — deferral
works in both directions. The cited evidence failed independently too: two of the four issues are
closed NOT_PLANNED and never supported the claim (#38487 asks that Write/Edit *also* trigger
injection, which presupposes deferral works; #32906 is a docs issue about subagents), and the two
still open assert opposite failure modes. The passage now states path scoping as verified working
on 2.1.219 as of 2026-07-24, with no version floor claimed since no changelog entry or maintainer
comment pins when it changed, and keeps the caveats that do survive: an `@import` inside a
path-scoped rule still inlines at session start and defeats the rule; path-scoped content is
invisible to subagents, teammates, and skill-forked contexts (#32906, closed NOT_PLANNED —
accepted behavior); a new-file Write does not trigger the rule; and before v2.1.211 on-demand
rules loaded even when `project` was excluded from `--setting-sources`.

## [0.4.0]

### Added
Expand Down
Loading