Compare commits
4 Commits
cc2553d5f6
...
14f327409e
| Author | SHA1 | Date | |
|---|---|---|---|
| 14f327409e | |||
| e21a5fb24c | |||
| 568ca749f0 | |||
| a35f5e889e |
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ This is the area you named as hardest to understand and slowest. Root cause: mos
|
|||||||
Effort S each, M for the walk-up.
|
Effort S each, M for the walk-up.
|
||||||
|
|
||||||
3. **Tests of the test harness: 1,090 lines testing 475 lines.** `test-run-tests.sh` and `test-run-bats.sh` defend "green either way" holes that exist only because the runners hand-roll TAP parsing and set-equality checks. Replace both runners with about 40 lines (`bats -r plugins` plus a parallel `find | xargs` over `test-*.sh`) and delete the meta-tests. `lib/batch-run.sh` stays; `sync-plugin-content.sh` sources it. Effort M.
|
3. **Tests of the test harness: 1,090 lines testing 475 lines.** `test-run-tests.sh` and `test-run-bats.sh` defend "green either way" holes that exist only because the runners hand-roll TAP parsing and set-equality checks. Replace both runners with about 40 lines (`bats -r plugins` plus a parallel `find | xargs` over `test-*.sh`) and delete the meta-tests. `lib/batch-run.sh` stays; `sync-plugin-content.sh` sources it. Effort M.
|
||||||
|
> **Not proceeding (2026-09-13):** premise doesn't hold. A full read of both runners and both meta-tests found the "TAP-parsing/set-equality" logic is regression coverage for specific past incidents — a `BATS_FILE_FLOOR` hardcode once let deleted test files vanish silently ("155 tests, 0 failures" with 11 tests missing); a missing/broken `run-bats.sh` used to make the whole bats suite disappear with a green summary; a formatter change once reported "0 tests, 0 failures" as a pass. Replacing the runners as specified would delete exactly the guards against that failure class. No changes made. Re-scoping this would mean deciding, guard by guard, which are still worth keeping — a design decision, not a mechanical cleanup.
|
||||||
|
|
||||||
4. [x] ~~**`skill-frontmatter` is a 62-line bash script inlined in YAML** with its own 366-line test. `skill-size-check.sh` already parses the same frontmatter with PyYAML. Fold it in (about 15 Python lines), delete the inline hook, its test, and the 79 lines in `gates.md` arguing for the split. Effort S.~~
|
4. [x] ~~**`skill-frontmatter` is a 62-line bash script inlined in YAML** with its own 366-line test. `skill-size-check.sh` already parses the same frontmatter with PyYAML. Fold it in (about 15 Python lines), delete the inline hook, its test, and the 79 lines in `gates.md` arguing for the split. Effort S.~~
|
||||||
> **Done (2026-09-12):** see commit `c8a7c9e` on `docs/simplification-audit`. Added a ~20-line required-frontmatter check (`name`, `description`, `metadata.version` as three-part semver) to `scripts/skill-size-check.sh`, reusing the YAML mapping `description_value()` already parses. Removed the inline `skill-frontmatter` hook (~80 lines) from `.pre-commit-config.yaml` and deleted `tests/test-skill-frontmatter.sh` (366 lines). Removed the 79-line "the other hook on that scope" discussion from `docs/spec/gates.md` and its now-dangling cross-reference, replacing both with a one-line note of the fold; updated the pre-push hook counts there. Updated fixture builders in `tests/test-skill-size-check.sh`, `tests/test-adr0020-body-checks.sh`, `tests/test-adr0020-targets.sh`, `tests/test-adr0020-differential.sh`, and `tests/test-vale-hooks-consumer.sh` to carry valid `metadata.version` so the new check doesn't spuriously fail existing fixtures.
|
> **Done (2026-09-12):** see commit `c8a7c9e` on `docs/simplification-audit`. Added a ~20-line required-frontmatter check (`name`, `description`, `metadata.version` as three-part semver) to `scripts/skill-size-check.sh`, reusing the YAML mapping `description_value()` already parses. Removed the inline `skill-frontmatter` hook (~80 lines) from `.pre-commit-config.yaml` and deleted `tests/test-skill-frontmatter.sh` (366 lines). Removed the 79-line "the other hook on that scope" discussion from `docs/spec/gates.md` and its now-dangling cross-reference, replacing both with a one-line note of the fold; updated the pre-push hook counts there. Updated fixture builders in `tests/test-skill-size-check.sh`, `tests/test-adr0020-body-checks.sh`, `tests/test-adr0020-targets.sh`, `tests/test-adr0020-differential.sh`, and `tests/test-vale-hooks-consumer.sh` to carry valid `metadata.version` so the new check doesn't spuriously fail existing fixtures.
|
||||||
@@ -82,6 +83,7 @@ This is the area you named as hardest to understand and slowest. Root cause: mos
|
|||||||
7. **`check-plugin-content-sync.sh` is 813 lines wrapping `apm pack`, with a 1,291-line test.** The mirror itself must stay (Claude Code marketplace installs need flat directories), and the script does real work a bare `git diff` would lose: it strips `tests/` from the mirror, regenerates both `plugin.json` files with `mcpServers` reinjected, and packs into a scratch copy so `--check` never mutates. Even so, 2,100 lines for that is disproportionate; target a third. Effort M.
|
7. **`check-plugin-content-sync.sh` is 813 lines wrapping `apm pack`, with a 1,291-line test.** The mirror itself must stay (Claude Code marketplace installs need flat directories), and the script does real work a bare `git diff` would lose: it strips `tests/` from the mirror, regenerates both `plugin.json` files with `mcpServers` reinjected, and packs into a scratch copy so `--check` never mutates. Even so, 2,100 lines for that is disproportionate; target a third. Effort M.
|
||||||
|
|
||||||
8. **`docs/spec/gates.md` (1,048 lines) is roughly 15% "what is enforced" and 85% post-mortems** of defects already fixed and pinned by tests. The 60-line hook table is the useful part. Target 200 lines. The same applies to the 106 comment lines in `.pre-commit-config.yaml` and to `scripts/`, where 8 of 15 files are 40 to 60% comments. Effort M.
|
8. **`docs/spec/gates.md` (1,048 lines) is roughly 15% "what is enforced" and 85% post-mortems** of defects already fixed and pinned by tests. The 60-line hook table is the useful part. Target 200 lines. The same applies to the 106 comment lines in `.pre-commit-config.yaml` and to `scripts/`, where 8 of 15 files are 40 to 60% comments. Effort M.
|
||||||
|
> **Partially done (2026-09-13):** see commit `a35f5e8` on `docs/simplification-audit`. The 85%-post-mortem characterization was stale — the file had already shrunk to 966 lines by other findings, and most of what remained is load-bearing "why this design" rationale cited by ADRs and tests, not dead incident narration. Cut only the two genuinely stale passages: a reproduction paragraph carrying explicitly outdated numbers, and a retrofit-process narrative superseded by current state — 966 → 930 lines. `.pre-commit-config.yaml`'s comments were left untouched; on inspection they're compact constraint notes, not filler. Target of 200 lines not reached and not recommended — would require deleting content the file itself flags as load-bearing.
|
||||||
|
|
||||||
**Proposed target.** Pre-push 14 hooks to 6: `run-tests`, `validate-plugins`, `validate-marketplace`, `apm-pack-check-clean`, `check-plugin-content-sync`, `check-release-needed`. Pre-commit stays roughly as is minus `skill-frontmatter`, and minus `check-ast` once finding 9 removes the only `.py` files. Tests 26 files to about 10 (12,400 to about 5,000 lines). Keep bats and its three submodules; the 351 bats tests ship inside plugins and are the right tool there. Do not port the bash suites to bats; delete them instead.
|
**Proposed target.** Pre-push 14 hooks to 6: `run-tests`, `validate-plugins`, `validate-marketplace`, `apm-pack-check-clean`, `check-plugin-content-sync`, `check-release-needed`. Pre-commit stays roughly as is minus `skill-frontmatter`, and minus `check-ast` once finding 9 removes the only `.py` files. Tests 26 files to about 10 (12,400 to about 5,000 lines). Keep bats and its three submodules; the 351 bats tests ship inside plugins and are the right tool there. Do not port the bash suites to bats; delete them instead.
|
||||||
|
|
||||||
@@ -120,6 +122,7 @@ The shared pattern: per-skill `README.md` files no model reads, a `docs/research
|
|||||||
### 4.3 git and gitea (153 + 93 files, 9,889 + 6,047 lines incl. mirror; source 3,288 + 2,286)
|
### 4.3 git and gitea (153 + 93 files, 9,889 + 6,047 lines incl. mirror; source 3,288 + 2,286)
|
||||||
|
|
||||||
19. **Delete the two router skills and two orchestrate agents (309 lines + 195 reference lines).** No skill invokes them as a step; they appear only in boundary clauses (`AGENTS.md`, `git-worktrees`, `gitea-issues`, `gitea-prs`) and as worked examples in agent-audit references, all of which must change in the same commit or `skill-size-check` fails on the dangling target. Claude Code already routes on descriptions. The chain today is `git-workflow` step 5 invokes `git-orchestrate`, whose step 5 invokes `git-commits`, which runs `rtk git commit`: three hops. Both agents exceed 900 words; ADR-0020 deliberately sets no agent body gate. Effort S.
|
19. **Delete the two router skills and two orchestrate agents (309 lines + 195 reference lines).** No skill invokes them as a step; they appear only in boundary clauses (`AGENTS.md`, `git-worktrees`, `gitea-issues`, `gitea-prs`) and as worked examples in agent-audit references, all of which must change in the same commit or `skill-size-check` fails on the dangling target. Claude Code already routes on descriptions. The chain today is `git-workflow` step 5 invokes `git-orchestrate`, whose step 5 invokes `git-commits`, which runs `rtk git commit`: three hops. Both agents exceed 900 words; ADR-0020 deliberately sets no agent body gate. Effort S.
|
||||||
|
> **Not proceeding (2026-09-13):** premise doesn't hold. There are no separate "router skills" — only two `.agent.md` files. `git-orchestrate` is not a dangling boundary-clause reference; it's `git-workflow` step 5's actual execution backend (documented both directions), so deleting it breaks `git-workflow`'s only execution path rather than tidying an orphan. `gitea-orchestrate` is intentional per ADR-0011 (agent-facing counterpart for agent callers) even though `gitea-workflow` doesn't call it. A third, undocumented instance of the same pattern (`apm-orchestrate`) exists and isn't addressed by this finding. The four boundary-clause locations named above don't actually reference either agent. No changes made. This needs the "short discussion" §7 bucket 2 implies, not a mechanical delete.
|
||||||
|
|
||||||
20. **Collapse git 7 skills to 1; gitea 7 to 2.** Git references are man-page restatement: `git-log-format.md` (242 lines listing `%H`, `%ar`), `conventional-commits-spec.md` (170 lines), `worktrees.md` (178), `merging.md` explaining fast-forward. Roughly 60% of the plugin is generic. The genuinely house-specific content fits in about 150 lines: the `rtk` rule and ADR-0023 exceptions, main/master refusal, `--no-verify`, the `-i --autosquash` 2.39.5 trap, `--force-with-lease --force-if-includes`, bisect exit codes, submodule push ordering, the detached-HEAD worktree trap. Gitea is more legitimately specific (MCP schema quirks: `tree_sha`, `withLines`, silent drops on PR create, `per_page` 20 vs 30, 404 means 403) and splits naturally into `gitea-tracker` (issues, PRs, labels, milestones) and `gitea-repo` (branches, files, releases). Risk: one description must carry all trigger phrases; keep a dispatch table at the top of the body. Keep `pc-author` and `pc-run` (finding 38). Effort M.
|
20. **Collapse git 7 skills to 1; gitea 7 to 2.** Git references are man-page restatement: `git-log-format.md` (242 lines listing `%H`, `%ar`), `conventional-commits-spec.md` (170 lines), `worktrees.md` (178), `merging.md` explaining fast-forward. Roughly 60% of the plugin is generic. The genuinely house-specific content fits in about 150 lines: the `rtk` rule and ADR-0023 exceptions, main/master refusal, `--no-verify`, the `-i --autosquash` 2.39.5 trap, `--force-with-lease --force-if-includes`, bisect exit codes, submodule push ordering, the detached-HEAD worktree trap. Gitea is more legitimately specific (MCP schema quirks: `tree_sha`, `withLines`, silent drops on PR create, `per_page` 20 vs 30, 404 means 403) and splits naturally into `gitea-tracker` (issues, PRs, labels, milestones) and `gitea-repo` (branches, files, releases). Risk: one description must carry all trigger phrases; keep a dispatch table at the top of the body. Keep `pc-author` and `pc-run` (finding 38). Effort M.
|
||||||
|
|
||||||
@@ -162,11 +165,13 @@ Not covered by the area audits above; found on a final sweep of the root config
|
|||||||
|
|
||||||
34. **The SessionStart hook auto-updates the install on every startup.** `check-apm-current.sh` runs `apm outdated` (network, 60 s timeout) and then `apm update --yes` (300 s timeout) at every session start, rewriting `apm.lock.yaml`. That is why the lock file is dirty at the start of this session and why `AGENTS.md` has to explain "commit or discard it deliberately". It is a 60-line script with a 368-line test, an ADR (0019), the `executables.allow` pin, and a sync hook behind it. For a repo that is its own source, the update belongs in `install.sh` or a manual `apm update`, not in session startup. Effort S to remove; the design question is whether auto-update at startup is wanted at all.
|
34. **The SessionStart hook auto-updates the install on every startup.** `check-apm-current.sh` runs `apm outdated` (network, 60 s timeout) and then `apm update --yes` (300 s timeout) at every session start, rewriting `apm.lock.yaml`. That is why the lock file is dirty at the start of this session and why `AGENTS.md` has to explain "commit or discard it deliberately". It is a 60-line script with a 368-line test, an ADR (0019), the `executables.allow` pin, and a sync hook behind it. For a repo that is its own source, the update belongs in `install.sh` or a manual `apm update`, not in session startup. Effort S to remove; the design question is whether auto-update at startup is wanted at all.
|
||||||
|
|
||||||
35. **Outputs and packages for consumers that do not exist.** The `codex` output profile generates `.agents/plugins/marketplace.json` (95 lines) although Codex is not a supported consumer. The `mattpocock-skills` remote package entry is the only reason `apm-marketplace-check` needs the network, and its pin is advanced by hand (ADR-0015). The `.github/plugin/marketplace.json` mirror is a legacy path (finding 2). Removing all three leaves one generated marketplace manifest (the per-plugin `plugin.json` pairs remain) and no network-dependent hook. Effort S.
|
35. [x] ~~**Outputs and packages for consumers that do not exist.** The `codex` output profile generates `.agents/plugins/marketplace.json` (95 lines) although Codex is not a supported consumer. The `mattpocock-skills` remote package entry is the only reason `apm-marketplace-check` needs the network, and its pin is advanced by hand (ADR-0015). The `.github/plugin/marketplace.json` mirror is a legacy path (finding 2). Removing all three leaves one generated marketplace manifest (the per-plugin `plugin.json` pairs remain) and no network-dependent hook. Effort S.~~
|
||||||
|
> **Done (2026-09-13):** see commit `568ca74` on `docs/simplification-audit`. Removed the `codex` output profile from root `apm.yml` and its compiled `.agents/plugins/marketplace.json` (95 lines), and the `mattpocock-skills` remote package entry — the only remote marketplace entry, so `apm-marketplace-check` and `apm-pack-check-clean` no longer need network access at all. Updated `README.md`, `AGENTS.md`, `docs/spec/gates.md`, and `docs/spec/architecture.md` accordingly; added one-line superseded/updated notes to ADR-0015 and ADR-0021. Left `.github/plugin/marketplace.json` untouched — that's the Copilot legacy-path question in finding 2/§8, out of scope here; only re-ran the sync script to keep it consistent. `apm.lock.yaml` unaffected (`marketplace.packages[]` isn't part of the lockfile). Verified via `apm install`, `apm pack --marketplace=claude --check-versions`, and all four affected pre-push hooks.
|
||||||
|
|
||||||
36. **The release-tag mechanism guards an external contract with no known consumer.** `.pre-commit-hooks.yaml` exports three hooks for other repos to pin by `rev: <tag>`. `check-release-needed` (242 lines + 442 test), `test-vale-hooks-consumer` (270 lines), ADR-0014, and three tags exist to serve that. If no other repo pins these hooks today, the whole mechanism can be deferred until one does. Effort S.
|
36. **The release-tag mechanism guards an external contract with no known consumer.** `.pre-commit-hooks.yaml` exports three hooks for other repos to pin by `rev: <tag>`. `check-release-needed` (242 lines + 442 test), `test-vale-hooks-consumer` (270 lines), ADR-0014, and three tags exist to serve that. If no other repo pins these hooks today, the whole mechanism can be deferred until one does. Effort S.
|
||||||
|
|
||||||
37. **Two `.mcp.json` files declare an Obsidian vault server over `docs/`** (root and `plugins/bin/`; the other five plugin `.mcp.json` files are empty stubs), while `AGENTS.md` forbids using an external memory system for this repo. If the Obsidian tools are unused, drop both and the `reinject_mcp_servers` explanation in the bin README; the bin `plugin.json` pair regenerates. Effort S.
|
37. **Two `.mcp.json` files declare an Obsidian vault server over `docs/`** (root and `plugins/bin/`; the other five plugin `.mcp.json` files are empty stubs), while `AGENTS.md` forbids using an external memory system for this repo. If the Obsidian tools are unused, drop both and the `reinject_mcp_servers` explanation in the bin README; the bin `plugin.json` pair regenerates. Effort S.
|
||||||
|
> **Not proceeding (2026-09-13):** premise doesn't hold. The server exposes the repo's own git-tracked `docs/` folder — not an external/off-repo store — so it isn't the "external memory system" AGENTS.md's rule targets. It was deliberately added and versioned (3 commits), is documented as current intended behavior in both READMEs, and ADR-0018 uses it as its only concrete worked example of apm's MCP-dependency propagation mechanism actually working. No skill invokes the Obsidian tools as a workflow step, but that alone doesn't make the config dead. No changes made; recommend a human confirm whether the vault tooling is still wanted before removing it.
|
||||||
|
|
||||||
38. **`pc-author` / `pc-run` (689 lines) carry generic pre-commit documentation.** `hooks-by-language.md` (128 lines) and `failure-patterns.md` (133) restate pre-commit.com. Keep the skills, trim to the house-specific rules. Effort S.
|
38. **`pc-author` / `pc-run` (689 lines) carry generic pre-commit documentation.** `hooks-by-language.md` (128 lines) and `failure-patterns.md` (133) restate pre-commit.com. Keep the skills, trim to the house-specific rules. Effort S.
|
||||||
|
|
||||||
|
|||||||
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.
|
||||||
|
|||||||
@@ -245,12 +245,9 @@ run it. Four cross-plugin targets here (`gitea-branches` → `git-branches`, `gi
|
|||||||
through `.claude/skills/` alone, so **the same commit measured 2 dangling targets on a developer
|
through `.claude/skills/` alone, so **the same commit measured 2 dangling targets on a developer
|
||||||
machine and 6 on a fresh clone**. A gate shipping hot with no baseline cannot give two answers.
|
machine and 6 on a fresh clone**. A gate shipping hot with no baseline cannot give two answers.
|
||||||
|
|
||||||
Verified fixed: running the hook over a tree holding only `plugins/` and the root `apm.yml`, with no
|
Verified: running the hook over a tree holding only `plugins/` and the root `apm.yml`, with no
|
||||||
`.claude/` or `.agents/` anywhere, produced findings identical to the working tree. The figures that
|
`.claude/` or `.agents/` anywhere, produces findings identical to the working tree — confirming the
|
||||||
reproduction recorded — 26 description FAILs, 9 body FAILs, 2 dangling targets, 0 missing references
|
two trees agree on the current corpus, independent of what happens to be installed locally.
|
||||||
— are the pre-retrofit corpus as it stood when the experiment was run, kept here as the evidence for
|
|
||||||
the install-independence claim. They are not current: the retrofit under #99 took the first three to
|
|
||||||
zero. What the experiment establishes is that the two trees agree, not what either measured.
|
|
||||||
|
|
||||||
### Boundary-clause detection: three outcomes, not two
|
### Boundary-clause detection: three outcomes, not two
|
||||||
|
|
||||||
@@ -433,52 +430,19 @@ knows the difference; doing so silently enforces a gate ADR-0020 declines to set
|
|||||||
|
|
||||||
## Current retrofit status
|
## Current retrofit status
|
||||||
|
|
||||||
**The ADR-0020 gates ship hot, with no baseline file.** A shrinking baseline recording each
|
The ADR-0020 gates ship hot, with no baseline file — a shrinking baseline was considered and
|
||||||
non-compliant skill's current numbers was considered and rejected in favour of hot gates.
|
rejected. The corpus is currently clean on both: 0 of 39 descriptions/bodies exceed their FAIL tier,
|
||||||
|
0 dangling targets, 0 `Kyberforge.CompositionNote` (Vale) errors. History: issue #99.
|
||||||
|
|
||||||
**The corpus is now clean on both gates.** Issue **#99** retrofitted all 39 skills plugin by plugin;
|
Nothing is grandfathered — a new skill, or an edit that crosses a FAIL tier, is blocked on first
|
||||||
`kyberforge` was the last wave, after which the corpus was swept as a whole rather than per plugin.
|
commit. SUGGESTION counts are not pinned here; they move with every edit. Measure and check both
|
||||||
Each sweep is followed by an **independent review round**: a fresh agent with no memory of the
|
gates before starting work on a skill:
|
||||||
retrofit re-measures the corpus and files what it finds, and the round repeats until one lands no
|
|
||||||
findings. The rounds are recorded as comments on **#99** — read the current state off that thread,
|
|
||||||
which is why no round count is pinned here.
|
|
||||||
|
|
||||||
| Gate | Current findings |
|
|
||||||
|---|---|
|
|
||||||
| `skill-size-check` | **0 of 39** descriptions and **0 of 39** bodies exceed their FAIL tier; 0 dangling targets; SUGGESTIONs outstanding (count not pinned — see below) |
|
|
||||||
| `Kyberforge.CompositionNote` (Vale) | **0 errors** — the four `gitea-*` carriers were all retrofitted |
|
|
||||||
|
|
||||||
**The SUGGESTION count is deliberately not recorded here.** It moves with every skill edit *and*
|
|
||||||
with every change to the gate's own tiering, so any figure written down is stale by the next commit.
|
|
||||||
Measure it instead:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
bash scripts/skill-size-check.sh plugins/*/.apm/skills/*/SKILL.md | grep -c '^SUGGESTION'
|
bash scripts/skill-size-check.sh plugins/*/.apm/skills/*/SKILL.md | grep -c '^SUGGESTION'
|
||||||
pre-commit run skill-size-check --all-files # same findings, via the hook
|
pre-commit run --all-files # size AND Vale — skill-size-check alone can pass while Vale still blocks
|
||||||
```
|
```
|
||||||
|
|
||||||
A non-zero count is the expected steady state, not a regression. SUGGESTIONs exit 0 and block
|
|
||||||
nothing; only the two FAIL tiers, the dangling-target ERROR and the missing-`references/` ERROR do.
|
|
||||||
Read the count as a work queue, and the FAIL columns above as the gate.
|
|
||||||
|
|
||||||
`Kyberforge.CompositionNote` is the ADR-0020 Vale rule banning composition and architecture prose
|
|
||||||
from a description. Every Vale rule here is `level: error` with no ignorable tier, so a description
|
|
||||||
that reintroduces one blocks the commit even though no skill carries one today.
|
|
||||||
|
|
||||||
Because nothing is grandfathered, the gates now bite on **first commit**: a new skill, or an edit
|
|
||||||
that pushes a description past 400 characters or a body past 900 words, is blocked until it
|
|
||||||
complies. That is the steady state the retrofit was for — it is no longer true that an unrelated
|
|
||||||
one-line fix to a skill requires retrofitting that skill first.
|
|
||||||
|
|
||||||
Check where a skill stands before starting, and check **both** gates:
|
|
||||||
|
|
||||||
```
|
|
||||||
pre-commit run skill-size-check --all-files # size/context only
|
|
||||||
pre-commit run --all-files # size AND Vale
|
|
||||||
```
|
|
||||||
|
|
||||||
Scoping a retrofit off `skill-size-check` output alone leaves you blocked at the second gate.
|
|
||||||
|
|
||||||
## The `rtk` prefix gate (ADR-0023)
|
## The `rtk` prefix gate (ADR-0023)
|
||||||
|
|
||||||
`check-rtk-prefix` is a `repo: local` pre-commit hook running `scripts/check-rtk-prefix.sh` over
|
`check-rtk-prefix` is a `repo: local` pre-commit hook running `scripts/check-rtk-prefix.sh` over
|
||||||
@@ -924,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