From c68e864159939be63943ef876cc61afef712337b Mon Sep 17 00:00:00 2001 From: Defame1297 Date: Mon, 17 Aug 2026 12:29:16 +0000 Subject: [PATCH] docs(spec): correct eight claims the trim carried into the new files Why: the relocation into gates.md and architecture.md moved text faster than it verified it, and a review found eight assertions that do not survive contact with the repo. - gates.md cited docs/research/examples/, which does not exist; the tree is at plugins/kyberforge/docs/research/examples/. The same wrong string survives at ADR-0013:98, where it originated, and is tracked separately. - gates.md's description-budget arithmetic was built on 23,427, a figure ADR-0020 measured at f9b919d and pins there, and compared a name-inclusive total against a description-only ceiling. Dropped rather than re-measured, following the precedent this branch set for CONTEXT.md's token figure: a second live copy is a second thing to go stale. - gates.md listed "every boundary-clause routing target must resolve" as a hard FAIL. skill-size-check.sh emits unresolved targets as SUGGESTION by default, promotes to ERROR only when terminal and either route-notated or corroborated by a resolving target in the same sentence, and reports INFO DID NOT RUN when no universe resolves. Verified with five fixtures; replaced with a three-verdict table. - gates.md and README called `pre-commit run --hook-stage pre-push --all-files` the whole push gate. check-release-needed.sh exits 0 unless PRE_COMMIT_REMOTE_BRANCH is refs/heads/main, which only the real git hook exports -- its own header comment titles this a "Known gap". - "repo-defined" was loose: the two meta hooks are also declared here, and what separates them is repo: meta from repo: local. - architecture.md claimed `when:` appears in two of 39 skill sources. Exactly one SKILL.md carries it; the second hit is a META.md. The likely origin is skill-implementation-workflow.md:233, which claims META.md sits alongside every skill when one exists in the whole tree. - architecture.md said core/AGENTS.md is "deployed beside" CLAUDE.md. deploy-manifest.sh sends them to ~/.claude/ and ~/.agents/ respectively; the relation is import, not adjacency. - gates.md's see-also cited ADRs as bare stems where its neighbours use paths. Implementation notes: architecture.md's closing pointer sent readers to the published description "for what a consumer actually gets", which was true against an enumeration and is not after ADR-0021. It now names the boundary relationship and points at the skills tree and README for an inventory. README's bin bullet gains caveman and zoom-out, since ADR-0021 leaves it the only place an inventory lives. Impact: 16/16 pre-push hooks pass. Deliberately not fixed here and tracked separately: the 12 pre-commit-stage hooks gates.md does not document, its own SentenceOpenerThereIs violation, and README's missing scripts/install.sh step. Refs: #105 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TmFqzpuExLJv3m114XVE9w --- README.md | 9 ++++-- docs/spec/architecture.md | 6 ++-- docs/spec/gates.md | 62 +++++++++++++++++++++++++++++++-------- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 83bd147..1ee498f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The six plugins: - **gitea** — issues, pull requests, labels, milestones, releases, branches, files - **core** — authoring and auditing a repo's `AGENTS.md` and the provider adapter files that defer to it - **lint** — configuring and running linters -- **bin** — cross-cutting workflow skills not yet split into a focused plugin: research, documentation, TDD, prototyping, triage, diagnosis, architecture review, requirement grilling +- **bin** — cross-cutting workflow skills not yet split into a focused plugin: research, documentation, TDD, prototyping, triage, diagnosis, architecture review, requirement grilling, compressed output (`caveman`), and re-orienting mid-task (`zoom-out`) ## Prerequisites @@ -81,12 +81,17 @@ A suite that exits 77 because a dependency is missing is reported as SKIPPED and ## Before pushing -Run the whole pre-push gate locally in one command: +Run the pre-push gate locally in one command: ```bash pre-commit run --hook-stage pre-push --all-files ``` +One caveat: `check-release-needed` is a silent no-op under this invocation. It exits 0 unless +`PRE_COMMIT_REMOTE_BRANCH` is `refs/heads/main`, and pre-commit exports that only from the real +pre-push git hook during an actual `git push` — so the hook reports `Passed` having checked nothing. +Every other pre-push hook does run. + 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`: diff --git a/docs/spec/architecture.md b/docs/spec/architecture.md index 6c9298e..b0d3ca4 100644 --- a/docs/spec/architecture.md +++ b/docs/spec/architecture.md @@ -21,7 +21,7 @@ project repo (local overrides) Skills are **not** deployed by `install.sh`. They are distributed as plugins and installed separately — in this repo by `apm install` against the `dependencies.apm` entries in the root `apm.yml`, which lands them in `.claude/skills/` and `.claude/agents/` (ADR-0018); elsewhere by `claude plugin install @holocron`. -`~/.claude/CLAUDE.md` is a thin adapter, not a content source. It imports `~/.agents/AGENTS.md` (always-on rules) and `governance.md` (always-on governance) and carries nothing else — the content index of on-demand instruction files sits in `core/AGENTS.md`, deployed beside it. All always-on content lives in `AGENTS.md` files so other providers can import the same source without duplication. +`~/.claude/CLAUDE.md` is a thin adapter, not a content source. It imports `~/.agents/AGENTS.md` (always-on rules) and `governance.md` (always-on governance) and carries nothing else — the content index of on-demand instruction files sits in `core/AGENTS.md`, deployed to `~/.agents/AGENTS.md` and imported by it. All always-on content lives in `AGENTS.md` files so other providers can import the same source without duplication. ## Plugin model @@ -31,7 +31,7 @@ Which plugin a new skill belongs in follows from what each one is scoped to. The The second boundary worth stating is `git` vs `gitea`, because both own things called branches and both touch pull requests: `git` is whatever works over the git wire protocol against a local clone, `gitea` is whatever goes through the forge's HTTP API. That is why `git-branches` and `gitea-branches` both exist and are not duplicates. -These are routing boundaries, not inventories — they answer "where does a new skill go", so they deliberately do not enumerate what each plugin ships today. For what a consumer actually gets, read the plugin's published `description` in its `apm.yml`. +These are routing boundaries, not inventories — they answer "where does a new skill go", so they deliberately do not enumerate what each plugin ships today. The plugin's published `description` in its `apm.yml` states the same boundary for a consumer deciding whether to install (ADR-0021); neither carries an inventory. For what a plugin ships today, read `plugins//.apm/skills/` or the plugin list in `README.md`. | Plugin | Scope | |---|---| @@ -76,7 +76,7 @@ This repo also has a `CLAUDE.md` at its root — the Claude Code entry point for ## Reference conventions -The stated convention is that files referencing other files declare those references explicitly: the referencing file carries the forward reference (the content index in `core/AGENTS.md`, `references:` in frontmatter), the referenced file carries a `when:` field describing when it is loaded, and divergence between the two signals staleness. It is aspirational, not a description of the repo today — no file under `core/instructions/` carries frontmatter at all, `when:` appears in two of the 39 skill sources under `plugins/*/.apm/skills/`, and the reference scanner script meant to derive the reverse map ("what files reference this file?") does not exist; `docs/notes/skill-implementation-workflow.md` still lists it as unbuilt work. Treat it as intent for instruction files, skills, and workflow documents, not as a rule the repo enforces. +The stated convention is that files referencing other files declare those references explicitly: the referencing file carries the forward reference (the content index in `core/AGENTS.md`, `references:` in frontmatter), the referenced file carries a `when:` field describing when it is loaded, and divergence between the two signals staleness. It is aspirational, not a description of the repo today — no file under `core/instructions/` carries frontmatter at all, `when:` appears in exactly one of the 39 `SKILL.md` sources under `plugins/*/.apm/skills/`, and the reference scanner script meant to derive the reverse map ("what files reference this file?") does not exist; `docs/notes/skill-implementation-workflow.md` still lists it as unbuilt work. Treat it as intent for instruction files, skills, and workflow documents, not as a rule the repo enforces. ## Provider model diff --git a/docs/spec/gates.md b/docs/spec/gates.md index 0fca311..8a5140d 100644 --- a/docs/spec/gates.md +++ b/docs/spec/gates.md @@ -14,7 +14,7 @@ of the oddities documented here are load-bearing and have already been re-litiga | Command | Scope | |---|---| | `pre-commit run --all-files` | the commit-stage hooks | -| `pre-commit run --hook-stage pre-push --all-files` | the whole push gate, one command | +| `pre-commit run --hook-stage pre-push --all-files` | the push gate, one command — with one caveat below | | `pre-commit run skill-size-check --all-files` | just the ADR-0020 size/context gates | Install hooks via `pc-run`, wiring **all three stages**. This repo's `.pre-commit-config.yaml` has no @@ -23,7 +23,18 @@ and `pre-push` (everything below). The pre-push command reports **16** hooks, not 14. The extra two are pre-commit's own `meta` hooks, `check-hooks-apply` and `check-useless-excludes`: they declare no `stages:`, so they run at every -stage including this one. Fourteen is the count of repo-defined pre-push hooks. +stage including this one. Both are declared in this repo's `.pre-commit-config.yaml` like everything +else — what separates them is `repo: meta` (pre-commit's own built-ins) from `repo: local`. Fourteen +is the count of hooks this repo authors itself. + +**The caveat: one of those 14 is a silent no-op under that invocation.** +`check-release-needed` exits 0 immediately unless `PRE_COMMIT_REMOTE_BRANCH` equals +`refs/heads/main`, and pre-commit exports that variable only from the real pre-push git hook during +an actual `git push`. Running the stage by hand — or from a CI runner — therefore reports it +`Passed` having checked nothing. That is by design for feature branches — pushing WIP must not be +blocked on cutting a premature tag — but it means `--hook-stage pre-push --all-files` is a full +rehearsal of 13 hooks and a skip of the fourteenth. The script's own header records the same gap for +a PR merged through Gitea's merge button, where no local push happens at all. ## The pre-push gate @@ -74,7 +85,7 @@ drift in generated text. | Hook | Guards | |---|---| -| `check-release-needed` | on push to `main` only — fails if files exposed via `.pre-commit-hooks.yaml` changed since the last tag | +| `check-release-needed` | on a real `git push` to `main` only — fails if files exposed via `.pre-commit-hooks.yaml` changed since the last tag. A no-op everywhere else, including under `pre-commit run --hook-stage pre-push` (see [the caveat above](#running-the-gates)) | Four of these shell out to `apm`: `apm-marketplace-check`, `apm-audit-ci`, `apm-pack-check-clean`, and `check-plugin-content-sync` (via `scripts/sync-plugin-content.sh`, which wraps `apm pack`). The @@ -87,8 +98,9 @@ loudly (`Error: jq is required but not installed`). ## Skill and agent context gates (ADR-0020) The `skill-size-check` pre-commit hook, scoped to `^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$`, -runs `scripts/skill-size-check.sh`. That scope means it never lints `docs/research/examples/` -reference skills. It is also shipped to external repos as `kyberforge-skill-size-check` (see +runs `scripts/skill-size-check.sh`. That scope means it never lints the +`plugins/kyberforge/docs/research/examples/` reference skills. It is also shipped to external repos +as `kyberforge-skill-size-check` (see [External consumers](#external-consumers-the-root-pre-commit-hooksyaml)). ### Two independent gate families, neither replaced the other @@ -107,14 +119,13 @@ reference skills. It is also shipped to external repos as `kyberforge-skill-size | `description` characters | 250 | 400 | the YAML-**folded** value | | body words | 600 | 900 | **body only** — everything after the frontmatter's closing `---` | -Plus three hard FAILs with no suggestion tier: +Plus two hard FAILs with no suggestion tier: - **A missing, valueless or `null` `description:`.** Not a skip. The description is the one field preloaded into every session, so a gate that declines to measure it reports green. (This is not hypothetical: `description:` with no value followed by `model: sonnet` let a line regex capture the *next* key, which looked non-empty, so the "missing or empty" branch never fired and every gate below early-returned on the genuinely empty folded value — exit 0, zero output, on a blocking gate.) -- **Every boundary-clause routing target must resolve** to a real skill or agent. - **Every `references/.md` a body names must exist** on disk. A dispatch table pointing at a file that was never written is a silently dead branch, and nothing else in the gate/audit/vale stack notices it. @@ -122,6 +133,26 @@ Plus three hard FAILs with no suggestion tier: A file can sit well inside one family and fail the other. 2,770 whole-file words is a conformance backstop; 900 body-only words is a quality gate. Conflating them is what produced the current state. +### An unresolved routing target is not automatically a FAIL + +A boundary-clause target that resolves to no skill or agent has **three** possible verdicts, not one +(`unresolved_targets()` in `scripts/skill-size-check.sh`): + +| Verdict | When | +|---|---| +| **SUGGESTION** — the default | the target does not resolve and neither promotion condition below holds | +| **blocking ERROR** | the target is **terminal** (not a compound modifier) **and** either written in route notation (`/name`, `-> name`) **or** corroborated by another target in the same sentence that *does* resolve | +| **INFO, "DID NOT RUN"** | no skill universe could be determined for the path at all — the targets are named and left unchecked, exit 0 | + +The default is deliberately soft because a hyphenated word in a boundary clause is as likely to be a +tool, a file format or an English compound as a route: "pre-commit hooks" is prose about a tool and +never reaches the check at all, being a compound modifier rather than a terminal name. The +SUGGESTION text says how to opt in — write it as `/name` or `-> name` and it gets checked properly. + +Corroboration is what makes the soft default safe: a sentence whose *other* target resolves is +demonstrably a routing sentence, so a sibling that does not resolve is a typo rather than a noun, and +gets promoted. + ### Target resolution walk Resolution walks up **from the file being checked** — never from the script's own location. Deriving @@ -176,10 +207,13 @@ Three more, deterministic to measure but judgment to act on: The hook is declared `verbose: true` so the SUGGESTION tier is audible. pre-commit prints nothing at all for a passing hook, and a SUGGESTION deliberately does not fail — without verbose every suggestion is swallowed, which is exactly the invisibility ADR-0013 records for Vale warnings. -ADR-0020's arithmetic depends on it: writing to the 400-char FAIL lands the preload at 39 × 400 = -15,600 chars (a 33% cut off 23,427); writing to the 250-char SUGGESTION lands at 9,750 (58%). The -halving depends entirely on that tier being visible. It costs nothing on a clean file — the script -prints only findings. +ADR-0020's preload arithmetic depends on it: writing to the 400-char FAIL delivers roughly half the +cut that writing to the 250-char SUGGESTION does, so the intended saving depends entirely on that +tier being visible. The numbers, and the measurement method behind them, are not restated here — +they live in ADR-0020's Consequences section, under "A ceiling does not produce an average", whose +figures are pinned to the base commit the decision was taken on (`f9b919d`). Quoting them here would +just create a second copy to go stale. It costs nothing on a clean file — the script prints only +findings. ### Duplicated constants @@ -637,8 +671,10 @@ remote *before* any network call, so it does not join the pair above. enforcement table (deterministic vs. auditor judgment), and every rejected alternative - `docs/adr/0019-session-start-hook-keeps-the-apm-install-current.md` — the `SessionStart` hook, the executable-trust gate, and the version-pinned allow key -- `docs/adr/0017` / `0015` / `0014` — plugin content sync, apm-generated manifests, committed Vale - styles +- `docs/adr/0017-plugin-content-mirror-bridges-apm-to-host-discovery.md`, + `docs/adr/0015-apm-replaces-plugin-marketplace-authoring.md`, + `docs/adr/0014-vale-prefilter-ships-from-the-plugin.md` — plugin content sync, apm-generated + manifests, committed Vale styles - `docs/spec/architecture.md` — directory structure, install pipeline, what is generated and what is hand-authored - `.pre-commit-config.yaml` — the hooks themselves, with inline rationale comments