docs: source ADR-0029 claims and sync ADRs and hook docs with behaviour
- cite the VS Code prompt-file deprecation and the verbatim apm quote - add ADR-0029 boundary-clause enforcement and Consequences - mark superseded ADR-0019 passages; record neutral lock advice, source fork and reloadSkills, amend for the hook hardening - move the ADR-0025 amendment out of the Decision list - amend ADR-0022 for create keeping 0.1.0 - fix hooks.md merge and event claims, README guard caveat, gates.md Vale globs, and pin the research registry URL Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkT7RSDwDbmrM9T34b6sTi
This commit is contained in:
@@ -9,9 +9,10 @@ Author hooks in `plugins/kyberforge/.apm/hooks/*.json`. `.apm/` is the only cont
|
||||
`apm install` is the only supported install path (ADR-0024) — there is no generated mirror at the
|
||||
plugin root and no per-plugin `plugin.json`, so `.apm/hooks/` is both where you edit and what ships.
|
||||
|
||||
apm merges every `*.json` in that directory into a single hook definition and writes the event
|
||||
bindings into the consuming project's `.claude/settings.json`; see "Deployed shape" below. Scripts a
|
||||
hook invokes live in the same directory, alongside the JSON that references them.
|
||||
For Claude, apm merges the event bindings from every `*.json` in that directory into the consuming
|
||||
project's `.claude/settings.json`; see "Deployed shape" below. That merge is Claude's rendering, not
|
||||
a general rule: Copilot gets one file per source file (see "GitHub Copilot CLI and Codex"). Scripts
|
||||
a hook invokes live in the same directory, alongside the JSON that references them.
|
||||
|
||||
## Hook file structure
|
||||
|
||||
@@ -32,14 +33,12 @@ The shape Claude Code reads, and therefore the shape to author under `.apm/hooks
|
||||
}
|
||||
```
|
||||
|
||||
Events (**partial list**): `PreToolUse`, `PostToolUse`, `Notification`, `Stop`, and `SessionStart`
|
||||
(verified end-to-end by the hook below). Claude Code's plugin hook set is larger — `SessionEnd`,
|
||||
`UserPromptSubmit`, `PreCompact` and `SubagentStop` also exist — and this repo's vendored corpus does
|
||||
not enumerate it anywhere: `docs/research/docs/claude-code-plugins/configuration.md:100` describes
|
||||
the file as "Event handlers (PreToolUse, PostToolUse, etc.)", and `agent-definition.md:53` covers
|
||||
only the per-agent `hooks` field, not the plugin-level set. Treat the five names above as the ones
|
||||
this repo has verified, not as the schema. Check Claude Code's own hooks documentation before wiring
|
||||
an event not listed here.
|
||||
Events: see `plugins/kyberforge/docs/research/docs/microsoft-apm/hooks-primitive-schema.md`,
|
||||
section "Events: `_HOOK_EVENT_MAP`", for which names apm renames per target and which it passes
|
||||
through unchanged. For Claude, author every event in PascalCase (`SessionStart`, `UserPromptSubmit`,
|
||||
`PreCompact`, and so on). A camelCase name apm does not map, such as `userPromptSubmit`, draws only
|
||||
a non-fatal warning and never fires; an all-lowercase one draws no warning at all and never fires
|
||||
either.
|
||||
|
||||
## Referencing a script — use the `.apm/` path
|
||||
|
||||
@@ -85,7 +84,9 @@ mechanic. See ADR-0019, correction 2026-09-19, and the comment above `executable
|
||||
|
||||
`check-apm-current.sh` keeps an apm-consumed install level with its remote: it runs `apm outdated`,
|
||||
and if anything is behind, runs `apm update --yes` and returns `reloadSkills: true` so the running
|
||||
session picks up the redeployed content. Rationale, measurements, and the failure modes are in
|
||||
session picks up the redeployed content. `reloadSkills` is a documented `SessionStart`
|
||||
`hookSpecificOutput` field that makes Claude Code re-scan skill directories once the hooks finish
|
||||
(code.claude.com/docs/en/hooks, checked 2026-09-29). Rationale, measurements, and the failure modes are in
|
||||
ADR-0019.
|
||||
|
||||
**Claude Code only, and where it looks for the lockfile.** The hook exits 0 at once, silently and
|
||||
@@ -101,12 +102,26 @@ host that sets no `CLAUDE_PROJECT_DIR` the lockfile guard passes in every apm co
|
||||
fallback ran `apm update --yes` there (ADR-0019, correction 2026-09-28).
|
||||
|
||||
**The `timeout` in `hooks.json` must exceed the script's own budget.** The script spends at most
|
||||
`timeout 60 apm outdated` plus `timeout 300 apm update`; the hook entry declares `timeout: 380`, the
|
||||
sum plus a buffer. Set it lower and a slow remote gets the hook SIGKILLed mid-`apm update`, leaving a
|
||||
`timeout -k 5 60 apm outdated` plus `timeout -k 5 300 apm update`, 370 s counting each 5 s SIGKILL
|
||||
grace; the hook entry declares `timeout: 380`, the sum plus a buffer. Set it lower and a slow remote gets the hook SIGKILLed mid-`apm update`, leaving a
|
||||
partially redeployed `.claude/skills/` and emitting no notice — precisely the silent failure the hook
|
||||
exists to prevent. `tests/test-apm-current-hook.sh` pins the relationship (host timeout > sum of the
|
||||
script's internal timeouts) rather than the literal, so raising either side alone fails the suite.
|
||||
|
||||
**The time limits are portable and hard to escape (ADR-0019, amendment 2026-09-29).** The script
|
||||
uses `timeout`, or `gtimeout` where only Homebrew coreutils provides it (stock macOS). With neither,
|
||||
it emits a notice and exits without running `apm`, instead of dying silently on exit 127. It exports
|
||||
`GIT_TERMINAL_PROMPT=0`, so a remote that wants credentials fails at once instead of waiting out the
|
||||
timeout on a prompt nobody can see. Where `flock` exists and `apm_modules/` does too, `apm update`
|
||||
runs under a non-blocking lock on `apm_modules/.kyberforge-apm-update.lock`. A second session that
|
||||
starts during a refresh skips its own and says so. Without `flock` the refresh runs unserialised.
|
||||
|
||||
**The lock advice is neutral when the default branch is unknown.** The notice tells you to discard
|
||||
the rewritten `apm.lock.yaml` on a feature branch and to decide deliberately on the default branch.
|
||||
It learns the default from `refs/remotes/origin/HEAD`, which `git remote add` never writes. When
|
||||
that ref is unset, on a detached HEAD, or outside a git checkout, it says only "commit it or
|
||||
discard it deliberately" rather than guessing `main` (ADR-0019, amendment 2026-09-19).
|
||||
|
||||
**Staleness is detected by matching apm's summary line, and both spellings count.** `apm outdated`
|
||||
has no `--json` or otherwise machine-readable output (verified against apm 0.28.0), so the hook
|
||||
greps its text. apm prints `1 outdated dependency found` in the singular when exactly one package is
|
||||
|
||||
@@ -4,7 +4,7 @@ source_keys:
|
||||
- apm-cli-installed-source
|
||||
- apm-docs-llms-full
|
||||
- apm-github-repo
|
||||
- context7-microsoft-apm
|
||||
- context7-microsoft-apm # partial: earlier pass only; the 2026-09-28 re-verification could not reach Context7 (see sources.md)
|
||||
---
|
||||
|
||||
Ground truth for this file is the installed apm-cli **0.28.0** source (`apm_cli/integration/hook_integrator.py`, `hook_native_formats.py`, `hook_ir.py`, `hook_file_routing.py`, `_hook_dropped_targets.py`, `targets.py`, `security/executables.py`) plus a live `apm install` of a scratch package targeting `claude` and `copilot` (2026-09-28). Where the published docs (`llms-full.txt`) disagree with 0.28.0, the disagreement is called out; the published docs track upstream `main` and may describe a newer release.
|
||||
|
||||
@@ -4,7 +4,7 @@ source_keys:
|
||||
- apm-cli-installed-source
|
||||
- apm-docs-llms-full
|
||||
- apm-github-repo
|
||||
- context7-microsoft-apm
|
||||
- context7-microsoft-apm # partial: earlier pass only; the 2026-09-28 re-verification could not reach Context7 (see sources.md)
|
||||
---
|
||||
|
||||
This file is checked against the installed apm-cli **0.28.0** source (`primitives/models.py`, `primitives/parser.py`, `primitives/discovery.py`, `utils/patterns.py`, `integration/instruction_integrator.py`, `integration/base_integrator.py`, `integration/targets.py`, `compilation/agents_compiler.py`, `commands/compile/cli.py`) and against a live `apm install` / `apm compile` of a scratch package (2026-09-28).
|
||||
|
||||
@@ -4,7 +4,7 @@ source_keys:
|
||||
- apm-cli-installed-source
|
||||
- apm-docs-llms-full
|
||||
- apm-github-repo
|
||||
- context7-microsoft-apm
|
||||
- context7-microsoft-apm # partial: earlier pass only; the 2026-09-28 re-verification could not reach Context7 (see sources.md)
|
||||
---
|
||||
|
||||
Checked against the installed apm-cli **0.28.0** source (`integration/prompt_integrator.py`, `integration/command_integrator.py`, `integration/base_integrator.py`, `integration/targets.py`, `security/gate.py`) and a live `apm install` of a scratch package targeting `claude` and `copilot` (2026-09-28).
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
## apm-cli-installed-source
|
||||
|
||||
- **URL:** file:///root/.local/pipx/venvs/apm-cli/lib/python3.11/site-packages/apm_cli/
|
||||
- **URL:** https://github.com/microsoft/apm/tree/v0.28.0/src/apm_cli/
|
||||
- **Local copy read:** the installed package at `~/.local/pipx/venvs/apm-cli/lib/python3.11/site-packages/apm_cli/` (pipx, apm-cli 0.28.0), which is what was actually read; the URL above is the matching upstream tag, for readers without that install.
|
||||
- **Description:** Installed apm-cli 0.28.0 package source, which is the version this repo runs. Read as ground truth for the hooks, instructions and prompts docs, including `integration/hook_integrator.py`, `hook_native_formats.py`, `hook_ir.py`, `hook_file_routing.py`, `instruction_integrator.py`, `command_integrator.py`, `prompt_integrator.py`, `targets.py`, `primitives/`, `utils/patterns.py`, `compilation/agents_compiler.py`, `commands/compile/cli.py` and `security/executables.py`. Cross-checked by live `apm install` / `apm compile` runs of a throwaway package (claude and copilot targets) in a scratch dir on 2026-09-28.
|
||||
- **Contributing files:** hooks-primitive-schema.md, instructions-primitive-schema.md, prompt-primitive-schema.md
|
||||
- **Status:** `extracted`
|
||||
@@ -28,6 +29,6 @@
|
||||
- **Contributing files:** hooks-primitive-schema.md, instructions-primitive-schema.md, prompt-primitive-schema.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
Note (2026-09-28): during the verification pass for the hooks, instructions and prompts docs, the Context7 `/microsoft/apm` endpoint returned an invalid-API-key error. Those three files were re-verified against `apm-cli-installed-source` and `apm-docs-llms-full` only. Their `context7-microsoft-apm` key reflects the earlier pass.
|
||||
Note (2026-09-28): during the verification pass for the hooks, instructions and prompts docs, the Context7 `/microsoft/apm` endpoint returned an invalid-API-key error. Those three files were re-verified against `apm-cli-installed-source` and `apm-docs-llms-full` only. Their `context7-microsoft-apm` key reflects the earlier pass, and each of the three carries a note saying so next to the key.
|
||||
|
||||
Note: `releasing.md`'s `--check-clean`/`--check-versions` scope, `apm pack` exit-code semantics, and the `.apm/`-vs-root-flat-dir mutual exclusivity referenced there were additionally cross-checked directly against `apm_cli/bundle/plugin_exporter.py`, `apm_cli/commands/pack.py`, and `apm_cli/marketplace/drift_check.py` in the installed `apm-cli` 0.28.0 package (`/root/.local/pipx/venvs/apm-cli/`), not just Context7 doc snippets — confirmed by a live `apm pack --format plugin` run inside `plugins/bin` that reproduced the documented `[!] Skipping root-level skills/ because .apm/ is present` warning.
|
||||
|
||||
Reference in New Issue
Block a user