From 4d336bbf357975af877372a83bf91f495b8b9acb Mon Sep 17 00:00:00 2001 From: Defame1297 Date: Mon, 17 Aug 2026 12:28:30 +0000 Subject: [PATCH] docs: stop the preloaded instruction set asserting machine state Why: four defects in the files every session pays for, all introduced or left behind by the trim. AGENTS.md told agents the `:` form still resolves "because user-scope native installs were left enabled on purpose", and that a working namespaced call "is not something to fix". That premise is false on this machine: installed_plugins.json is empty, no enabledPlugins key exists in ~/.claude.json, and ~/.apm/marketplaces.json is empty. ADR-0018 already reversed itself once on this exact claim (Correction 2026-08-14) using that same enablement as its evidence, so flipping the assertion again would be the third revision in three. Both files now assert nothing about install state at all, which removes the flip-flop surface instead of re-aiming it. The other three are guard-rails whose instruction survived the trim while the caveat that made it safe did not: - The run-tests.sh line omitted --strict, so it named the one invocation that reports SKIPPED rather than failed when a dependency is missing. gates.md records this gate going green having verified 15 of 17 suites on a vale-less PATH. .pre-commit-config.yaml:70 already uses --strict for that reason. - The .claude/settings.json prohibition lost its ADR-0019 exception, so an agent applying it literally would strip apm's own merged SessionStart entry and create the drift the rule exists to prevent. - LESSONS.md still routed graduated rules to CONTEXT.md's Principles section, which this branch deleted. Implementation notes: the six terms the trim dropped while AGENTS.md still claimed CONTEXT.md glosses everything -- authoring root, content mirror, apm package, output profile, near-miss, vacuous green -- are restored as one-line entries per CONTEXT-FORMAT.md, sourced from architecture.md, gates.md and skill-audit's description-quality.md rather than reworded. ADR-0018 gets a third dated note recording the observation and the fact that the state has now been described two ways, and its stale user-scope inventory is replaced by a pointer to it; the decision it records is untouched. LESSONS.md:3 carried the identical stale claim as :5 and is fixed with it. Impact: preloaded context is now free of assertions about machine state. Refs: #105 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TmFqzpuExLJv3m114XVE9w --- AGENTS.md | 6 +-- CONTEXT.md | 40 +++++++++++++++++-- LESSONS.md | 4 +- ...po-consumes-its-own-plugins-through-apm.md | 23 +++++++++-- 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b7d9e16..6c30aed 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,17 +27,17 @@ This repo dogfoods its own plugins. Before shelling out, check whether a skill a - Vale prose linting → `vale-config` / `vale-run` - This repo's own AGENTS.md → `agentsmd-author` / `agentsmd-audit` -Use the bare, **unnamespaced** names. The `:` form (`gitea:gitea-prs`) also still resolves, because user-scope native installs were left enabled on purpose (ADR-0018) — a working namespaced call is not evidence that anything is broken and is not something to "fix". Prefer the bare name anyway: it is what `apm install` deploys, and what survives those user-scope installs eventually being converted. +Use the bare, **unnamespaced** names. That is what `apm install` deploys and the only form this repo's own install produces — a project skill has no plugin to prefix (ADR-0018). Whether the `:` form (`gitea:gitea-prs`) also resolves depends on native plugin installs at user scope, outside this repo; write the bare name either way. Fall back to raw shell only when no skill covers it. ## Session rules -- **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins//.apm/hooks/` and deployed by apm, never hand-written into that file. Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`. +- **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins//.apm/hooks/` and deployed by apm, never hand-written into that file. The `SessionStart` entry already in it is exactly that: kyberforge authors it in `plugins/kyberforge/.apm/hooks/hooks.json` and apm merges it in, so it is apm's own output, it is what the replay expects, and it belongs in the commit — do not strip it (ADR-0019). Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`. - **`apm.lock.yaml` turning up modified is expected, not a bug.** kyberforge's `SessionStart` hook runs `apm outdated` at startup and `apm update --yes` when something is behind, which rewrites the lock. Commit or discard it deliberately. - **A `.apm/` edit is not live in this session until it is pushed.** The six dependencies resolve from the holocron remote, unpinned against the default branch. `apm install` deploys from the lock; `apm update` is what re-resolves refs. - **The ADR-0020 skill gates ship hot, with no baseline.** 26 of 39 descriptions and 9 of 39 bodies exceed their FAIL tier, and the `Kyberforge.CompositionNote` Vale rule fires 10 errors across `gitea-issues`, `gitea-labels-milestones`, `gitea-prs` and `gitea-workflow`. Editing any of those skills *for any reason* means retrofitting it to the contract first — a one-line fix cannot be committed until the skill complies. Deliberate; tracked as Gitea issue #99. `skill-size-check` will not warn you about the Vale half, so check both: `pre-commit run --all-files`. -- **Run `bash tests/run-tests.sh` before considering any change done.** +- **Run `bash tests/run-tests.sh --strict` before considering any change done.** Keep the flag: without it a suite whose dependency is missing exits 77 and is counted SKIPPED rather than failed, so the run goes green having verified less than it claims. - **Before pushing, run the whole gate locally:** `pre-commit run --hook-stage pre-push --all-files`. Pushing runs 14 repo-defined hooks, not just the test suite. - **Pushing without a network** needs `SKIP=apm-marketplace-check,apm-pack-check-clean git push` — those two resolve a remote marketplace entry via `git ls-remote`. Skip only those two; the rest are real local checks, and adding one to `SKIP` disarms it silently. - **Author commits with `git-commits`** — it validates Conventional Commits, which `commit-msg` enforces. diff --git a/CONTEXT.md b/CONTEXT.md index 272dc6c..8e1fef7 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -57,6 +57,24 @@ The deployable unit — one or more skills, agents, hooks, commands, and MCP ser single installable directory under `plugins//`, compiled from that plugin's `.apm/` source. _Avoid_: package, bundle, module +**apm package**: +The unit apm builds and installs — `plugins//apm.yml` plus the hand-authored +`plugins//.apm/` tree it compiles from (ADR-0015). +_Avoid_: plugin directory, source tree + +**Content mirror**: +The generated flat `skills/`, `agents/`, `commands/`, `instructions/`, `extensions/` directories and +merged `hooks/hooks.json` at a plugin root — also called the flat mirror — compiled from that +plugin's `.apm/` tree so hosts that convention-scan those paths discover the content (ADR-0017). +_Avoid_: generated copy, duplicate tree + +**Output profile**: +An `apm pack` target format for a generated manifest; apm has `claude` +(`.claude-plugin/marketplace.json`) and `codex` (the differently-shaped +`.agents/plugins/marketplace.json`) and none for Copilot CLI's legacy path, which a sync script +mirrors instead. Mechanics: `docs/spec/architecture.md`. +_Avoid_: build target, export format + **Plugin marketplace**: A Git repository carrying a `marketplace.json` manifest that lists installable plugins. There is no backend, registry, or SaaS — the Git repo is the marketplace. @@ -135,6 +153,22 @@ The deterministic Vale pass that runs ahead of `skill-audit`/`agent-audit`'s Des so LLM judgment is spent only on what a pattern cannot catch. Mechanics: `docs/spec/gates.md`. _Avoid_: linting, style check +**Authoring root**: +The directory a gate resolves against — the nearest ancestor of the file being checked holding +`plugins/*/.apm/skills` or `plugins/*/.apm/agents`, falling back to the nearest ancestor holding +`.git`. The walk: `docs/spec/gates.md`. +_Avoid_: repo root, project root + +**Near-miss**: +A sibling skill or agent whose plausible queries share keywords with this one but need something +different — the only thing a boundary clause should exclude. +_Avoid_: overlap, similar skill + +**Vacuous green**: +A check that reports success because it measured nothing — zero files scanned, an unparsed value read +as empty, a conditional branch that never armed. +_Avoid_: false pass, clean run + **Issue**: The cross-provider term for a tracked unit of work. Gitea is this repo's canonical tracker (ADR-0007), but skills say "linked issue" generically rather than naming a provider. @@ -181,9 +215,9 @@ _Avoid_: ticket, card, task - "skill" was used for both the authored `SKILL.md` under `plugins//.apm/skills/` and the deployed copy under `.claude/skills/` — resolved: the authoring source is the **Skill**; the deployed copy is gitignored `apm install` output and is never edited. -- Skills answer to two names, bare (`gitea-prs`) and namespaced (`gitea:gitea-prs`), because - user-scope native installs were left enabled deliberately (ADR-0018) — resolved: write the bare - name; a working namespaced call is not evidence of a defect. +- Skills can answer to two names, bare (`gitea-prs`) and namespaced (`gitea:gitea-prs`), depending on + whether a native install exists at user scope alongside the apm one (ADR-0018) — resolved: write + the bare name, which is the only form `apm install` produces. - "context" means both the model's live token window (the **Preload tax** sense) and the bounded domain this file describes — resolved: unqualified "context" in this repo means the token window. - "audit" was used for both an author skill's inline closeout and `forge`'s independent diff --git a/LESSONS.md b/LESSONS.md index 3c8f0b4..7a01d6c 100644 --- a/LESSONS.md +++ b/LESSONS.md @@ -1,8 +1,8 @@ # Lessons -Patterns observed during development of this repo. Three or more entries on the same pattern → promote to CONTEXT.md (or the relevant instruction file) as a standing rule. +Patterns observed during development of this repo. Three or more entries on the same pattern → promote to `docs/spec/architecture.md` (or the relevant instruction file) as a standing rule. -**Graduation rule:** When three or more entries cover the same pattern, the human reviews and promotes it to the appropriate standing location: `CONTEXT.md` for domain-level principles, `core/instructions/coding.md` for coding conventions, `core/instructions/testing.md` for testing conventions, or `core/instructions/subagent-orchestration.md` for delegation conventions. Those four are the whole set — `core/instructions/` holds `coding.md`, `governance.md`, `subagent-orchestration.md` and `testing.md`, and nothing else. Git conventions have no standing file of their own: promote them to `core/instructions/coding.md`, or create a new instruction file deliberately rather than assuming one exists. The graduated entries are marked `[graduated → target file]` rather than deleted (audit trail). +**Graduation rule:** When three or more entries cover the same pattern, the human reviews and promotes it to the appropriate standing location: `docs/spec/architecture.md` for structural and domain-level principles — `CONTEXT.md` is not a destination, its `## Principles` section was deleted and what was there now sits under that file's "AGENTS.md pattern" and "Reference conventions" headings — `core/instructions/coding.md` for coding conventions, `core/instructions/testing.md` for testing conventions, or `core/instructions/subagent-orchestration.md` for delegation conventions. Those four are the whole set — `core/instructions/` holds `coding.md`, `governance.md`, `subagent-orchestration.md` and `testing.md`, and nothing else. Git conventions have no standing file of their own: promote them to `core/instructions/coding.md`, or create a new instruction file deliberately rather than assuming one exists. The graduated entries are marked `[graduated → target file]` rather than deleted (audit trail). **Who writes here:** The session-handoff skill (Chunk 3) prompts LESSONS.md extraction before closing a session. The human may also write directly. diff --git a/docs/adr/0018-repo-consumes-its-own-plugins-through-apm.md b/docs/adr/0018-repo-consumes-its-own-plugins-through-apm.md index 8b2eb95..4f5551a 100644 --- a/docs/adr/0018-repo-consumes-its-own-plugins-through-apm.md +++ b/docs/adr/0018-repo-consumes-its-own-plugins-through-apm.md @@ -72,6 +72,20 @@ survives those user-scope installs eventually being converted, and the namespace resolves for anyone installing holocron natively, so skill bodies written for both audiences should name the bare skill. +**Correction (2026-08-17): the evidence under the correction above is gone, and the claim goes with +it — not to its opposite.** Observed on this machine: `~/.claude/plugins/installed_plugins.json` is +`{"version": 2, "plugins": {}}`; there is no `enabledPlugins` key anywhere in `~/.claude.json` +(`grep -c enabledPlugins` returns 0); `~/.apm/marketplaces.json` is `{"marketplaces": []}`. The +`holocron` entry in `~/.claude/plugins/known_marketplaces.json` survives, but a registered +marketplace is not an installed plugin. So the user-scope installs the 2026-08-14 correction cited +are not there, and neither is the state the *original* consequence described before it. The claim +about the namespaced form has now been written twice off two different observations of the same +machine, and this ADR has already reversed itself once on it. That is the finding: the fact is +machine state, not a property of this decision, and it changes without any commit. No instruction +file — `AGENTS.md`, `CONTEXT.md`, or a skill body — should assert either way whether +`:` resolves. The rule that survives every observation is the one that was always the +actionable half: write the bare name, because it is the only form `apm install` produces. + **apm owns `.claude/settings.json`.** (ADR-0019 supersedes the "exactly `{"hooks": {}}`" claim below — once a package ships a hook, apm merges it into that file and the merged entry is apm's own output. The rule that nothing repo-authored goes in the file is unchanged.) `apm audit --ci` replays the install into a scratch tree and @@ -117,10 +131,11 @@ pinned `resolved_commit` in `apm.lock.yaml` and does not re-resolve refs (`apm i documents this explicitly — "does NOT refresh refs; use 'apm update' for that"). Running it after a merge redeploys the same content and reports success. -**User scope is untouched, deliberately.** `bin@holocron`, `gitea@holocron`, and a stale -`hello-world@holocron` remain natively installed at user scope, and every project other than this -one still resolves its skills that way. Converting them is a separate decision with a blast radius -beyond this repo. +**User scope is untouched, deliberately.** This decision changed project scope only; whatever is +natively installed at user scope was left alone, and converting it is a separate decision with a +blast radius beyond this repo. The specific inventory this paragraph used to name +(`bin@holocron`, `gitea@holocron`, a stale `hello-world@holocron`) is machine state and is stale — +see the 2026-08-17 correction above. The decision recorded here is unaffected by what that state is. ## Alternatives considered