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