docs: trim CONTEXT.md to a glossary and demote VISION.md
Why: CONTEXT.md is an instructed every-session read at 33,799 bytes (~8,450
tokens), and CONTEXT.md:17 layered docs/VISION.md on top. After the AGENTS.md
trim, that left ~9,650 tokens of instructed reads sitting behind a file whose
own format contract — grill-with-docs/CONTEXT-FORMAT.md:43, "Keep definitions
tight. One sentence max." — it was not following. 22 glossary entries averaged
190 words; the top four were 55% of the file, and one ("Vale audit prefilter")
was 1,444 words of pre-commit forensics filed as a definition.
Implementation notes:
- CONTEXT.md rewritten to CONTEXT-FORMAT.md's prescribed template: ## Language
(five clusters, 22 terms with _Avoid_ aliases), ## Relationships,
## Example dialogue, ## Flagged ambiguities. 33,799 -> 9,762 bytes.
- Vale prefilter forensics moved to docs/spec/gates.md under nine subsections:
the two-copy style layout, vale-wrap.sh and the Vale 3.15.2
text.frontmatter.description limitation, the --config argv defect (E100),
glob scoping, the rule inventory, and 0-files-means-NOT-RUN.
- Instruction-file format and the bidirectional-reference principle moved to
docs/spec/architecture.md, the latter flagged as aspirational: no instruction
file carries a `when:` field, only 2 of 39 skills do, and the reference
scanner script it names does not exist.
- Added a per-plugin scope table to architecture.md so "which plugin owns this
skill" survives the trim, and corrected its claim that the content index
lives in providers/claude-code/CLAUDE.md — that file is now two import lines
and the index sits in core/AGENTS.md.
- "Repo and Gitea are the only source of truth" moved from CONTEXT.md's
Principles into AGENTS.md session rules; it was operative guidance in a
glossary. docs/VISION.md and LESSONS.md added to the on-demand list.
- The apm-consumed install entry (432 words) was deleted outright: all four
consequences are already carried by README.md, ADR-0018, gates.md and
AGENTS.md.
- ADR-0013, ADR-0015 and ADR-0017 each pointed at CONTEXT.md sections that
moved; amended with dated supersession notes rather than left dangling.
Impact: the every-session set drops from ~15,700 to ~4,140 tokens across both
passes (-74%). The deleted "lint plugin" entry was also stale on a safety
claim — it said lint-runner inherits Edit, but ADR-0016 restored
`disallowedTools: Edit, Write, NotebookEdit` and CONTEXT.md was never updated.
Refs: #99
This commit is contained in:
@@ -87,7 +87,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`.
|
||||
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
|
||||
[External consumers](#external-consumers-the-root-pre-commit-hooksyaml)).
|
||||
|
||||
### Two independent gate families, neither replaced the other
|
||||
|
||||
@@ -279,6 +281,81 @@ is needed: the `Kyberforge` styles are **committed** under
|
||||
`plugins/kyberforge/.apm/skills/{skill-audit,agent-audit}/assets/vale/styles/`, not downloaded
|
||||
packages (ADR-0014).
|
||||
|
||||
### Two copies, one canonical
|
||||
|
||||
Wiring Vale as a deterministic prefilter for `skill-audit`/`agent-audit`'s Description dimension
|
||||
(motivation: issue #84) is repo-specific, not part of the generic `lint` plugin, so it does not live
|
||||
in `plugins/lint/` — and per ADR-0014 it no longer lives at the repo root either. It lives **twice**,
|
||||
one copy per skill, both under `plugins/kyberforge/.apm/skills/`:
|
||||
|
||||
| Copy | Styles | `.vale.ini` sections |
|
||||
|---|---|---|
|
||||
| `agent-audit/assets/vale/` — **canonical** | `Kyberforge`, `KyberforgeCopilot` | `[**/agents/*.md]`, `[**/*.agent.md]` |
|
||||
| `skill-audit/assets/vale/` — smaller duplicate | `Kyberforge` | `[**/SKILL.md]` |
|
||||
|
||||
Duplicated rather than shared because a plugin's cache-install copies only each skill's own files —
|
||||
there is no cross-skill sharing to point at. `check-vale-style-sync` at pre-push is what keeps them
|
||||
from drifting; `KyberforgeCopilot` is the one deliberate inequality, being scoped only to `.agent.md`
|
||||
files for the Copilot-only "`Use proactively` has no effect" check.
|
||||
|
||||
### What Vale owns, and what stays LLM judgment
|
||||
|
||||
Eleven rule files across the two copies, six distinct rules:
|
||||
|
||||
| Rule | Vale scope | Bans | From |
|
||||
|---|---|---|---|
|
||||
| `Kyberforge.DescriptionOpener` | `text.frontmatter.description` | non-imperative openers ("This skill/agent…") | issue #84 |
|
||||
| `Kyberforge.VagueWording` | `text.frontmatter.description` | vague capability wording ("helps with", "utilize", …) | issue #84 |
|
||||
| `Kyberforge.PaddingPhrase` | `text` | generic "see `references/` for details" padding | issue #84 |
|
||||
| `KyberforgeCopilot.ProactivePhrase` | `text.frontmatter.description` | `Use proactively` (no effect in Copilot) | issue #84 |
|
||||
| `Kyberforge.SentenceOpenerThereIs` | `sentence` | "There is/are" sentence openers | ADR-0013 |
|
||||
| `Kyberforge.CompositionNote` | `text.frontmatter.description` | architecture and composition prose in a description | ADR-0020 |
|
||||
|
||||
Vale covers the **pattern-matchable** sub-checks named in issue #84 plus, per ADR-0013, one
|
||||
cherry-picked body-wide prose-pattern rule. Everything else stays LLM judgment: defaults-vs-menus,
|
||||
why-rationale, the non-pattern-matchable body-discipline calls, near-miss exclusion strength, and
|
||||
control calibration. New rules land directly in `styles/Kyberforge` and block immediately — there is
|
||||
no trial tier.
|
||||
|
||||
The cherry-pick record, so it is not re-litigated:
|
||||
|
||||
- `Kyberforge.SentenceOpenerThereIs` **landed** — 22 held-out hits, both in-corpus hits clean
|
||||
rewrites, zero suppressions needed.
|
||||
- `Kyberforge.VagueQualifier` was cherry-picked and then **deleted**. 2 hits across the corpus as it
|
||||
stood on 2026-08-08 (before the `.apm/` restructure): one marginal, and one unfixable false
|
||||
positive — `caveman/SKILL.md` quotes `of course` as an example of filler, a mention rather than a
|
||||
use — which forced the repo's only Vale suppression comments.
|
||||
- `governance.md` and `CONTROLS.md` were evaluated as rule sources and **excluded**: nothing
|
||||
prose-pattern-matchable to mine.
|
||||
|
||||
### Why every rule is `level: error`
|
||||
|
||||
Every alert is a FAIL, with no ignorable tier — same all-or-nothing model as shellcheck, the test
|
||||
suite, and conventional-pre-commit. Graded severities do not work here: **Vale's exit code keys on
|
||||
`error` alerts alone**, so a `warning` or `suggestion` rule exits 0, and pre-commit swallows a
|
||||
passing hook's output. Such a rule would be invisible and would block nothing.
|
||||
|
||||
`MinAlertLevel` and `--minAlertLevel` are correspondingly **absent** from both `.vale.ini` files and
|
||||
from the hook definitions. Under this model they are no-ops; adding one is not a missing knob.
|
||||
|
||||
The `verbose: true` escape hatch that makes `skill-size-check`'s SUGGESTION tier audible has no
|
||||
analogue here — Vale has no tier to make audible.
|
||||
|
||||
### External consumers: the root `.pre-commit-hooks.yaml`
|
||||
|
||||
The root `.pre-commit-hooks.yaml` exposes both Vale copies (`kyberforge-vale-audit-skill`,
|
||||
`kyberforge-vale-audit-agent`) plus `kyberforge-skill-size-check`, so any external repo can enforce
|
||||
the same rules with `repo: <this-repo-url>, rev: <tag>` in its own `.pre-commit-config.yaml`.
|
||||
pre-commit clones the pinned rev into its own cache, independent of whether Claude Code or the
|
||||
`kyberforge` plugin is installed at all; the same mechanism covers CI via `pre-commit run
|
||||
--all-files`. `skill-size-check` has no external asset dependency, so it needed no relocation under
|
||||
ADR-0014 — only exposure.
|
||||
|
||||
This repo's own `vale-audit-prefilter-skill` / `-agent` hooks consume the **identical**
|
||||
plugin-bundled copies via `repo: local`. Deliberately not a third root copy, and deliberately **not a
|
||||
pinned self-reference** — a pinned self-reference would lint working-tree edits against the last
|
||||
tagged release rather than against the change being made.
|
||||
|
||||
### Pre-commit
|
||||
|
||||
Two prefilter hooks, with `.apm/`-scoped `files:` patterns:
|
||||
@@ -292,6 +369,92 @@ Only the **authoring source** triggers them. A `SKILL.md` in the generated flat
|
||||
neither pattern, so prose findings surface only when you edit the file you are supposed to be
|
||||
editing. Without the binary the hooks fail with a bare "command not found" and no install pointer.
|
||||
|
||||
**Two hooks, not one combined hook.** Both manifests split the prefilter in two precisely because a
|
||||
single hook can point at only one copy, and that copy would silently 0-file-skip the other file
|
||||
shape (see [A 0-file Vale run is NOT RUN](#a-0-file-vale-run-is-not-run)).
|
||||
|
||||
### The `.vale.ini` globs do no scoping
|
||||
|
||||
Each `.vale.ini`'s section globs are **path-agnostic** — `[**/SKILL.md]` for skill-audit's copy,
|
||||
`[**/agents/*.md]` and `[**/*.agent.md]` for agent-audit's — and constrain filename *shape*, not
|
||||
location: Vale's `*` crosses `/`. A `SKILL.md` outside `plugins/` (a project-scope
|
||||
`.claude/skills/foo/SKILL.md`, say) still matches `[**/SKILL.md]` and gets linted normally.
|
||||
|
||||
All scoping therefore comes from the pre-commit hook's own `files:` regex and from the audit skills
|
||||
passing one explicit file per invocation. The two manifests scope **differently on purpose**:
|
||||
|
||||
| Manifest | `-skill` | `-agent` |
|
||||
|---|---|---|
|
||||
| `.pre-commit-config.yaml` (pins this repo's layout) | `^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$` | `^plugins/[^/]+/\.apm/agents/[^/]+\.agent\.md$` |
|
||||
| `.pre-commit-hooks.yaml` (layout-agnostic for consumers) | `(^\|/)SKILL\.md$` | `(^\|/)agents/[^/]+\.md$\|\.agent\.md$` |
|
||||
|
||||
Narrowing a `.vale.ini` glob to a `plugins/`-shaped path to "tighten" it breaks the consumer case,
|
||||
and `check-vale-style-sync`'s probe set is built to catch exactly that.
|
||||
|
||||
### `vale-wrap.sh`, never bare `vale`
|
||||
|
||||
Both audit skills' Step 1 and both pre-commit hooks call **each copy's own**
|
||||
`scripts/vale-wrap.sh`, not `vale`. It works around a confirmed **Vale 3.15.2** limitation:
|
||||
`text.frontmatter.description` silently stops matching on most — not all — multi-line descriptions.
|
||||
|
||||
Verified by reproduction on a deliberately-bad fixture, not assumed:
|
||||
|
||||
| Description scalar spanning 2+ lines | Vale's behaviour |
|
||||
|---|---|
|
||||
| `>` folded block | 0 alerts, exit 0 — **broken** |
|
||||
| plain (unquoted) continuation lines | 0 alerts, exit 0 — **broken** |
|
||||
| single- or double-quoted, wrapped | 0 alerts, exit 0 — **broken** |
|
||||
| `\|` literal block | alerts fire, exit 1 — lints normally |
|
||||
|
||||
The wrapper flattens the three broken forms to a single-line scalar in a scratch copy — or, for the
|
||||
rare value no inline scalar can spell verbatim, a `|-` block with one content line — padding with
|
||||
blank lines so **every other line number is unchanged**. `|` literal blocks and single-line
|
||||
descriptions pass through untouched. Most descriptions in this repo are `>` blocks, so before the
|
||||
wrapper a bad description in any of the three broken forms sailed straight through the prefilter.
|
||||
|
||||
### The `--config` argv defect
|
||||
|
||||
Handed **no `--config` at all**, the wrapper falls back to its own sibling `assets/vale/.vale.ini`,
|
||||
located from `${BASH_SOURCE[0]}` rather than from the cwd. That is why both manifests' `entry:` is
|
||||
now the bare script path with **no argument after it**.
|
||||
|
||||
pre-commit prefixes only `entry[0]` with the hook-repo clone path (`cmd = (prefix.path(cmd[0]),
|
||||
*cmd[1:])`), so every later argument resolves against the **consuming** repo's root. A `--config` in
|
||||
`.pre-commit-hooks.yaml` therefore pointed at a path no consumer has and hard-failed every external
|
||||
run with `E100 [--config] Runtime error`.
|
||||
|
||||
`.pre-commit-config.yaml` drops the argument too, deliberately keeping the two entries identical.
|
||||
The local `repo: local` hook resolved its `--config` correctly only because the consuming repo *was*
|
||||
this repo — and that divergence is why three review rounds exercised a path no external consumer
|
||||
takes and missed the defect. **Do not reintroduce a `--config` to either manifest to make the local
|
||||
run "explicit".**
|
||||
|
||||
An explicit `--config` from any other caller still wins, in all three argv forms (`--config X`,
|
||||
`--config=/abs`, `--config=rel`), and a relative one resolves against the caller's cwd — matching
|
||||
bare `vale`, not the repo root.
|
||||
|
||||
Both audit skills' Step 1 passes no `--config` either. Step 1 resolves the script relative to the
|
||||
skill's own directory so the call works from an installed plugin cache; a relative `--config`
|
||||
alongside it would resolve against the cwd instead, yielding `E100 Runtime error … does not exist`
|
||||
and exit 2 — which both skills' fallback misreads as "vale unavailable" and silently downgrades to
|
||||
full LLM judgment.
|
||||
|
||||
`tests/test-vale-wrap.sh` regression-tests this against **skill-audit's** copy specifically: its
|
||||
fixtures are all `SKILL.md`-shaped, and only skill-audit's `.vale.ini` carries that glob section.
|
||||
|
||||
### A 0-file Vale run is NOT RUN
|
||||
|
||||
Vale reports 0 files only when the path it is handed matches **no glob section at all** — a
|
||||
differently-named file, or a directory argument holding nothing that matches. That run prints
|
||||
|
||||
```
|
||||
✔ 0 errors ... in 0 files.
|
||||
```
|
||||
|
||||
and exits 0, indistinguishable from a clean pass. Both audits therefore treat a 0-file Vale run as
|
||||
**NOT RUN** and fall back to full LLM judgment rather than reporting the Description dimension
|
||||
clean.
|
||||
|
||||
### Pre-push
|
||||
|
||||
`vale` is a **pre-push** dependency too, not only pre-commit. `check-vale-style-sync` runs **six
|
||||
@@ -314,6 +477,16 @@ Use it only on a machine that genuinely cannot install `vale`, and read that lin
|
||||
was not checked", not as a pass. The hook is `verbose: true` for exactly that reason — its clean
|
||||
output is a single line, so it costs one line per push.
|
||||
|
||||
### Mentioning banned phrasing without tripping the rule
|
||||
|
||||
House convention: banned phrasing that must be **mentioned** rather than used goes in backticks or a
|
||||
fenced code block. Vale skips code spans and fences, so no suppression is needed — which is why this
|
||||
document quotes `Use proactively` and "There is/are" the way it does.
|
||||
|
||||
Inline `<!-- vale Rule = NO -->` is the fallback **only** where backticking is impossible. Use the
|
||||
HTML-comment form; the MDX `{/* */}` form does not work in plain Markdown. The one time a rule forced
|
||||
suppression comments, the rule was deleted instead (see the `VagueQualifier` entry above).
|
||||
|
||||
## Tests
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user