build(apm): consume holocron plugins through apm instead of plugin install

Why:
The repo published apm packages but consumed them the old way — `claude plugin install
<name>@holocron`, six plugins enabled per project. Dogfooding stopped one layer short of the
install tooling kyberforge itself ships.

Implementation notes:
- Root apm.yml declares the six packages as dependencies.apm git+path objects against the
  holocron remote. Object form over `<name>@holocron` aliases on purpose: an alias first needs
  `apm marketplace add`, which writes to ~/.apm/marketplaces.json — user scope, absent on a fresh
  clone. Unpinned against the default branch, matching the autoUpdate the native install had.
- apm.lock.yaml is committed; .claude/skills/, .claude/agents/ and apm_modules/ are gitignored
  regenerable install output. Committing the deployed skills would add a third mirror of content
  ADR-0017 already governs two copies of.
- .mcp.json is generated by apm from plugins/bin/.mcp.json, so the obsidian MCP server survives
  the switch.
- .claude/settings.json is reduced to {"hooks": {}}. apm replays the install into a scratch tree
  and diffs, so any repo-owned key there is permanent drift that fails apm-audit-ci. Nothing was
  lost: enabledPlugins was empty after the uninstall and the only hooks entry was PreToolUse: [].
- tests/run-bats.sh and tests/run-tests.sh exclude apm_modules/. It holds a full copy of every
  plugin, and a copied .bats file resolves its helpers against the dependency root rather than
  this repo — 334 tests, 167 failures before the exclusion.

Impact:
Skills are now unnamespaced — `git-commits`, not `git:git-commits` — because apm deploys plain
project skills with no plugin to prefix. AGENTS.md, CONTEXT.md and docs/spec/architecture.md are
updated accordingly. Root apm.yml now declares dependencies, which arms apm-audit-ci's
lockfile-exists check for the root manifest. External consumers are unaffected: the marketplace
manifests are untouched and `apm pack --check-clean` stays clean. Project scope only.

ADR: 0018

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
This commit is contained in:
2026-08-14 17:15:17 +00:00
parent c3ec5f2d3d
commit 2e8732a8e5
11 changed files with 2907 additions and 26 deletions

View File

@@ -21,6 +21,13 @@ fi
# keeps the loop in this shell so the appends survive. `sort` is still fed
# newline-delimited output, exactly as before. Same convention as
# tests/run-tests.sh.
#
# apm_modules/ is excluded because `apm install` materializes a full copy of
# every dependency there — including this repo's own plugins, which it consumes
# from the holocron remote. Those copies carry their own .bats files whose
# relative paths (`$BATS_TEST_DIRNAME/../../../../../../`) resolve to the
# dependency's root, not this repo's, so they fail on a missing bats-support
# helper. They are the same tests already discovered under plugins/.
TEST_FILES=()
while IFS= read -r f; do
TEST_FILES+=("$f")
@@ -29,6 +36,7 @@ done < <(
-not -path "*/tests/bats/*" \
-not -path "*/test_helper/*" \
-not -path "*/.claude/worktrees/*" \
-not -path "*/apm_modules/*" \
| sort
)
@@ -67,7 +75,7 @@ if [[ -n "$GIT_TOPLEVEL" && "$GIT_TOPLEVEL" == "$REPO_ROOT" ]]; then
[[ -n "$f" ]] && EXPECTED_FILES+=("$REPO_ROOT/$f")
done < <(
git -C "$REPO_ROOT" ls-files -- '*.bats' \
| grep -Ev '(^|/)tests/bats/|(^|/)test_helper/|(^|/)\.claude/worktrees/' \
| grep -Ev '(^|/)tests/bats/|(^|/)test_helper/|(^|/)\.claude/worktrees/|(^|/)apm_modules/' \
| sort || true
)
else

View File

@@ -119,6 +119,10 @@ run_bats
# /bin/bash 3.2, which has no `mapfile`. Process substitution (not a pipe)
# keeps the loop in this shell so the appends survive. `sort` is still fed
# newline-delimited output, exactly as before.
#
# apm_modules/ is excluded for the same reason tests/run-bats.sh excludes it:
# `apm install` materializes dependency copies of this repo's own plugins there,
# and re-running a dependency's tests re-runs what plugins/ already covers.
SCRIPTS=()
while IFS= read -r script; do
SCRIPTS+=("$script")
@@ -126,6 +130,7 @@ done < <(
find "$SEARCH_ROOT" -name "test-*.sh" \
-not -path "*/.git/*" \
-not -path "*/.claude/worktrees/*" \
-not -path "*/apm_modules/*" \
| sort
)