refactor!: carry out the simplification audit across gates, tests, plugins and docs #135
@@ -1,95 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "holocron",
|
|
||||||
"interface": {
|
|
||||||
"displayName": "holocron"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"name": "kyberforge",
|
|
||||||
"source": {
|
|
||||||
"source": "local",
|
|
||||||
"path": "./plugins/kyberforge"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Developer Tools"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "bin",
|
|
||||||
"source": {
|
|
||||||
"source": "local",
|
|
||||||
"path": "./plugins/bin"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Utilities"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "git",
|
|
||||||
"source": {
|
|
||||||
"source": "local",
|
|
||||||
"path": "./plugins/git"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Version Control"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gitea",
|
|
||||||
"source": {
|
|
||||||
"source": "local",
|
|
||||||
"path": "./plugins/gitea"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Version Control"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "core",
|
|
||||||
"source": {
|
|
||||||
"source": "local",
|
|
||||||
"path": "./plugins/core"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Productivity"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mattpocock-skills",
|
|
||||||
"source": {
|
|
||||||
"source": "url",
|
|
||||||
"url": "mattpocock/skills",
|
|
||||||
"ref": "v1.2.3",
|
|
||||||
"sha": "835450ef244ab7335f75d95b83e7d979eae22a6d",
|
|
||||||
"tag_pattern": "v{version}"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Productivity"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "lint",
|
|
||||||
"source": {
|
|
||||||
"source": "local",
|
|
||||||
"path": "./plugins/lint"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"installation": "AVAILABLE",
|
|
||||||
"authentication": "ON_INSTALL"
|
|
||||||
},
|
|
||||||
"category": "Developer Tools"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -43,19 +43,6 @@
|
|||||||
"category": "Productivity",
|
"category": "Productivity",
|
||||||
"source": "./plugins/core"
|
"source": "./plugins/core"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "mattpocock-skills",
|
|
||||||
"description": "Skills for Real Engineers — planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.",
|
|
||||||
"version": "1.2.3",
|
|
||||||
"category": "Productivity",
|
|
||||||
"source": {
|
|
||||||
"source": "github",
|
|
||||||
"repo": "mattpocock/skills",
|
|
||||||
"ref": "v1.2.3",
|
|
||||||
"sha": "835450ef244ab7335f75d95b83e7d979eae22a6d",
|
|
||||||
"tag_pattern": "v{version}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "lint",
|
"name": "lint",
|
||||||
"description": "Skills and agents for configuring and running linters.",
|
"description": "Skills and agents for configuring and running linters.",
|
||||||
|
|||||||
13
.github/plugin/marketplace.json
vendored
13
.github/plugin/marketplace.json
vendored
@@ -43,19 +43,6 @@
|
|||||||
"category": "Productivity",
|
"category": "Productivity",
|
||||||
"source": "./plugins/core"
|
"source": "./plugins/core"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "mattpocock-skills",
|
|
||||||
"description": "Skills for Real Engineers — planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.",
|
|
||||||
"version": "1.2.3",
|
|
||||||
"category": "Productivity",
|
|
||||||
"source": {
|
|
||||||
"source": "github",
|
|
||||||
"repo": "mattpocock/skills",
|
|
||||||
"ref": "v1.2.3",
|
|
||||||
"sha": "835450ef244ab7335f75d95b83e7d979eae22a6d",
|
|
||||||
"tag_pattern": "v{version}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "lint",
|
"name": "lint",
|
||||||
"description": "Skills and agents for configuring and running linters.",
|
"description": "Skills and agents for configuring and running linters.",
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Fall back to raw shell only when no skill covers it.
|
|||||||
- **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/<name>/.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`.
|
- **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/<name>/.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.
|
- **`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.
|
- **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.
|
||||||
- **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.
|
- **No pre-push hook needs the network.** Root `apm.yml`'s marketplace has no remote package entries, so every hook resolves locally.
|
||||||
- **This repo and Gitea are the only source of truth.** All project state, decisions, and working conventions live here. Do not use an external memory system for this project — cached state diverges from the repo and you get a split brain. Before answering any design or architecture question, check `docs/adr/` for an existing decision.
|
- **This repo and Gitea are the only source of truth.** All project state, decisions, and working conventions live here. Do not use an external memory system for this project — cached state diverges from the repo and you get a split brain. Before answering any design or architecture question, check `docs/adr/` for an existing decision.
|
||||||
|
|
||||||
## Key documents
|
## Key documents
|
||||||
|
|||||||
@@ -94,13 +94,7 @@ Every other pre-push hook does run.
|
|||||||
|
|
||||||
See [`docs/spec/gates.md`](docs/spec/gates.md) for what each hook enforces and why.
|
See [`docs/spec/gates.md`](docs/spec/gates.md) for what each hook enforces and why.
|
||||||
|
|
||||||
**Offline?** Exactly two pre-push hooks need the network, because root `apm.yml`'s marketplace contains one remote package entry that must be resolved with `git ls-remote`:
|
**Offline?** No pre-push hook needs the network: root `apm.yml`'s marketplace has no remote package entries (the last one, `mattpocock-skills`, was removed), so `apm-marketplace-check` and `apm-pack-check-clean` resolve everything from local sources. All pre-push hooks pass offline.
|
||||||
|
|
||||||
```bash
|
|
||||||
SKIP=apm-marketplace-check,apm-pack-check-clean git push
|
|
||||||
```
|
|
||||||
|
|
||||||
Skip **only** those two. The remaining pre-push hooks are real local checks and pass offline; adding one of them to `SKIP` disarms it silently.
|
|
||||||
|
|
||||||
## Editing plugin content
|
## Editing plugin content
|
||||||
|
|
||||||
|
|||||||
12
apm.yml
12
apm.yml
@@ -64,13 +64,11 @@ marketplace:
|
|||||||
|
|
||||||
# Output targets (map form). Each output writes to its profile default
|
# Output targets (map form). Each output writes to its profile default
|
||||||
# path; add 'path:' under a key to override.
|
# path; add 'path:' under a key to override.
|
||||||
# 'codex' requires every package below to declare 'category:' (satisfied).
|
|
||||||
outputs:
|
outputs:
|
||||||
claude: {}
|
claude: {}
|
||||||
codex: {}
|
|
||||||
|
|
||||||
# CI tip: build one or all formats with a machine-readable manifest:
|
# CI tip: build a machine-readable manifest:
|
||||||
# apm pack --marketplace=claude,codex --json | jq -r '.marketplace.outputs[].path'
|
# apm pack --marketplace=claude --json | jq -r '.marketplace.outputs[].path'
|
||||||
|
|
||||||
versioning:
|
versioning:
|
||||||
strategy: per_package
|
strategy: per_package
|
||||||
@@ -106,12 +104,6 @@ marketplace:
|
|||||||
version: 1.1.2
|
version: 1.1.2
|
||||||
category: Productivity
|
category: Productivity
|
||||||
|
|
||||||
- name: mattpocock-skills
|
|
||||||
description: Skills for Real Engineers — planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.
|
|
||||||
source: mattpocock/skills
|
|
||||||
version: "1.2.3"
|
|
||||||
category: Productivity
|
|
||||||
|
|
||||||
- name: lint
|
- name: lint
|
||||||
description: Skills and agents for configuring and running linters.
|
description: Skills and agents for configuring and running linters.
|
||||||
source: ./plugins/lint
|
source: ./plugins/lint
|
||||||
|
|||||||
@@ -169,7 +169,10 @@ correction) sorted what they document into three buckets:
|
|||||||
apm has no version-bump automation (established under "Versioning" in issue #90's plan), so an
|
apm has no version-bump automation (established under "Versioning" in issue #90's plan), so an
|
||||||
ageing pin is the accepted cost of a push gate that only fires on this repo's own changes.
|
ageing pin is the accepted cost of a push gate that only fires on this repo's own changes.
|
||||||
Note the pin does not make the entry offline-resolvable: an exact version still requires a
|
Note the pin does not make the entry offline-resolvable: an exact version still requires a
|
||||||
`git ls-remote`, which is why two pre-push hooks need the network (see `AGENTS.md`).
|
`git ls-remote`, which is why two pre-push hooks needed the network (see `AGENTS.md`).
|
||||||
|
**Superseded 2026-09-13:** the `mattpocock-skills` entry has been removed from root `apm.yml`
|
||||||
|
entirely, along with the `codex` marketplace output profile. No pre-push hook needs the network
|
||||||
|
any longer.
|
||||||
- **Caveat on "Status: executed" above:** issue #90's own execution comment flagged, before merge,
|
- **Caveat on "Status: executed" above:** issue #90's own execution comment flagged, before merge,
|
||||||
that Claude Code's ability to actually load content out of `.apm/` was unverified — that caveat
|
that Claude Code's ability to actually load content out of `.apm/` was unverified — that caveat
|
||||||
turned out to be a real defect, not a formality: the native installer has zero awareness of
|
turned out to be a real defect, not a formality: the native installer has zero awareness of
|
||||||
|
|||||||
@@ -95,6 +95,10 @@ This decision covers the six plugins this repo authors. The root marketplace als
|
|||||||
`mattpocock-skills`, a third-party package whose description is not this repo's to write; its entry
|
`mattpocock-skills`, a third-party package whose description is not this repo's to write; its entry
|
||||||
is out of scope and is left as published upstream.
|
is out of scope and is left as published upstream.
|
||||||
|
|
||||||
|
*(Note, 2026-09-13: `mattpocock-skills` has since been removed from the root marketplace. This
|
||||||
|
section's scope statement is retained as the reasoning behind the boundary; the entry it describes
|
||||||
|
no longer exists.)*
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
**A plugin's published `description` states the plugin's domain boundary. It does not enumerate the
|
**A plugin's published `description` states the plugin's domain boundary. It does not enumerate the
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ These are routing boundaries, not inventories — they answer "where does a new
|
|||||||
|
|
||||||
Two compilers produce the plugin roots you see in the tree:
|
Two compilers produce the plugin roots you see in the tree:
|
||||||
|
|
||||||
- **`apm pack` compiles the manifests** (ADR-0015). Per plugin: `.claude-plugin/plugin.json` and `.github/plugin/plugin.json`, both generated from `plugins/<name>/apm.yml`. Repo-wide, from the root `apm.yml`'s `marketplace:` block: `.claude-plugin/marketplace.json` (apm's `claude` output profile) and `.agents/plugins/marketplace.json` (its `codex` profile, a differently-shaped file). Those two are the only marketplace outputs apm has profiles for — the third root manifest, `.github/plugin/marketplace.json` (Copilot CLI's legacy path), is a byte-identical mirror of the Claude one maintained by `scripts/sync-marketplace-mirror.sh` and gated by the `check-marketplace-mirror-sync` pre-push hook.
|
- **`apm pack` compiles the manifests** (ADR-0015). Per plugin: `.claude-plugin/plugin.json` and `.github/plugin/plugin.json`, both generated from `plugins/<name>/apm.yml`. Repo-wide, from the root `apm.yml`'s `marketplace:` block: `.claude-plugin/marketplace.json` (apm's `claude` output profile) — the only marketplace output this repo declares. A second root manifest, `.github/plugin/marketplace.json` (Copilot CLI's legacy path), is a byte-identical mirror of the Claude one maintained by `scripts/sync-marketplace-mirror.sh` and gated by the `check-marketplace-mirror-sync` pre-push hook.
|
||||||
- **`scripts/sync-plugin-content.sh` compiles the content mirror** (ADR-0017). It wraps `apm pack --format plugin` and copies the resulting bundle's flat `agents/`, `skills/`, `commands/`, `instructions/`, `extensions/`, and merged `hooks/hooks.json` back to the plugin root. Claude Code's installer convention-scans those flat paths and has no `.apm/` awareness whatsoever, so the mirror exists solely to satisfy the host's discovery contract.
|
- **`scripts/sync-plugin-content.sh` compiles the content mirror** (ADR-0017). It wraps `apm pack --format plugin` and copies the resulting bundle's flat `agents/`, `skills/`, `commands/`, `instructions/`, `extensions/`, and merged `hooks/hooks.json` back to the plugin root. Claude Code's installer convention-scans those flat paths and has no `.apm/` awareness whatsoever, so the mirror exists solely to satisfy the host's discovery contract.
|
||||||
|
|
||||||
`.apm/` is the sole hand-edited authoring source for plugin content. An edit made in the flat mirror is discarded by the next sync and is reported as drift by the `check-plugin-content-sync` pre-push hook. Hand-authored material that is not an `.apm/` primitive — `README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`, and per-plugin extras such as `plugins/gitea/references/` and `plugins/bin/evals/` — lives at the plugin **root** and is untouched by either compiler.
|
`.apm/` is the sole hand-edited authoring source for plugin content. An edit made in the flat mirror is discarded by the next sync and is reported as drift by the `check-plugin-content-sync` pre-push hook. Hand-authored material that is not an `.apm/` primitive — `README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`, and per-plugin extras such as `plugins/gitea/references/` and `plugins/bin/evals/` — lives at the plugin **root** and is untouched by either compiler.
|
||||||
|
|||||||
@@ -888,30 +888,12 @@ fix.
|
|||||||
|
|
||||||
## Pushing without a network
|
## Pushing without a network
|
||||||
|
|
||||||
Exactly **two** pre-push hooks need the network, for one shared reason: root `apm.yml`'s
|
No pre-push hook needs the network. Every entry in root `apm.yml`'s `marketplace.packages[]`
|
||||||
`marketplace.packages[]` contains exactly one remote entry — `mattpocock-skills`,
|
resolves from a local `./plugins/<name>` path, so `apm-marketplace-check` and `apm-pack-check-clean`
|
||||||
`source: mattpocock/skills` — and resolving it needs a `git ls-remote`.
|
never call `git ls-remote`.
|
||||||
|
|
||||||
| Hook | Offline failure |
|
`apm-audit-ci` calls `apm` too but was always local: its org-policy discovery resolves nothing on
|
||||||
|---|---|
|
this remote before any network call.
|
||||||
| `apm-marketplace-check` (`always_run`, resolves every entry) | `No cached refs (offline)` |
|
|
||||||
| `apm-pack-check-clean` (re-resolves the same entry) | `Error: Git network timeout during ls-remote` |
|
|
||||||
|
|
||||||
Pinning the entry to an exact version does **not** remove the call — an exact pin still ls-remotes.
|
|
||||||
`--offline` rescues neither.
|
|
||||||
|
|
||||||
To push without a network, skip both using pre-commit's own mechanism:
|
|
||||||
|
|
||||||
```
|
|
||||||
SKIP=apm-marketplace-check,apm-pack-check-clean git push
|
|
||||||
```
|
|
||||||
|
|
||||||
**Skip those two alone.** Verified under `unshare -rn`: the other twelve pre-push hooks pass offline
|
|
||||||
because they are real local checks. (`check-executables-allow-sync` landed after that run, but reads
|
|
||||||
two local manifests and makes no network call.) Adding any other hook to `SKIP` disarms it silently.
|
|
||||||
|
|
||||||
`apm-audit-ci` calls `apm` too but stays local: its org-policy discovery resolves nothing on this
|
|
||||||
remote *before* any network call, so it does not join the pair above.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ set -euo pipefail
|
|||||||
# that same file at .claude-plugin/marketplace.json directly, but also has a legacy
|
# that same file at .claude-plugin/marketplace.json directly, but also has a legacy
|
||||||
# convention path at .github/plugin/marketplace.json (see
|
# convention path at .github/plugin/marketplace.json (see
|
||||||
# plugins/kyberforge/docs/research/docs/github-copilot-plugins/marketplace.md) -- and
|
# plugins/kyberforge/docs/research/docs/github-copilot-plugins/marketplace.md) -- and
|
||||||
# that path is a mirror of the Claude output, not a separate apm
|
# that path is a mirror of the Claude output, not a separate apm output profile (this
|
||||||
# output profile (apm only ships "claude" and "codex" mappers; codex writes a
|
# repo declares only the "claude" output). This script keeps that legacy mirror
|
||||||
# differently-shaped file to .agents/plugins/marketplace.json, not this path). This
|
# byte-identical to .claude-plugin/marketplace.json instead of letting it silently
|
||||||
# script keeps that legacy mirror byte-identical to .claude-plugin/marketplace.json
|
# drift (see issue #90 comment thread).
|
||||||
# instead of letting it silently drift (see issue #90 comment thread).
|
|
||||||
|
|
||||||
# Hard error, not a `|| pwd` fallback. Every path this script touches hangs off
|
# Hard error, not a `|| pwd` fallback. Every path this script touches hangs off
|
||||||
# REPO_ROOT, and both of its exits-0 paths are "the files agree" or "neither file
|
# REPO_ROOT, and both of its exits-0 paths are "the files agree" or "neither file
|
||||||
|
|||||||
Reference in New Issue
Block a user