fix(kyberforge): align the audit word ceiling and drop the fragile --config

Three divergences between what the audit skills claim and what the hooks
enforce, each of which fails silently rather than loudly:

- `skill-size-check.sh` blocked at 2900 words while `validate.sh` checked only
  the 500-line ceiling, so `/skill-audit` could report a skill ready to ship
  that the commit hook then rejected. `validate.sh` now checks the same pair on
  the same inclusive terms; the constants are duplicated with a comment naming
  the other file, because a plugin skill's scripts cannot read outside the
  plugin directory once installed to the cache.
- Both audit skills' Step 1 passed `--config assets/vale/.vale.ini`, which is
  redundant (the wrapper self-locates its sibling config) and fragile: an agent
  that resolves the script path against the skill directory but not the config
  path gets E100, exit 2, which the surrounding fallback clause misreads as
  "vale unavailable" and downgrades to full LLM judgment with no signal.
- The external-consumer test registered only the two Vale hooks, never the
  third shipped hook, so a lost executable bit would have broken every consumer
  while the local suite stayed green. Verified by mutation: `chmod 644` on the
  copied script now turns three passes into two failures.

Also corrects the size hook's calibration comment, which claimed ~5.7-6.5
characters per word against a corpus whose measured median is 6.79 — the stated
upper bound sat below the median, so the "calibrated with margin" claim was
inverted for prose-dense files. MAX_WORDS is unchanged pending a decision; the
comment is now explicit that the gate holds under 5,000 tokens for typical
prose density, not for any file.

Refs: #85
This commit is contained in:
2026-08-09 15:44:16 +00:00
parent aa8cc22695
commit 4ae2429840
6 changed files with 85 additions and 22 deletions

View File

@@ -36,12 +36,12 @@ metadata:
```bash
bash scripts/validate.sh <path-to-agent-file>
bash scripts/validate-provenance.sh <path-to-agent-file>
scripts/vale-wrap.sh --config assets/vale/.vale.ini <path-to-cc-file> <path-to-copilot-file>
scripts/vale-wrap.sh <path-to-cc-file> <path-to-copilot-file>
```
The script accepts either the CC file or the Copilot file. It detects provider from extension, derives the counterpart, and runs all structural checks. Note FAILs and SUGGESTIONs for the `### Structure` and `### Provider safety` report dimensions. Findings about missing fields, bad name format, empty body, or missing frontmatter → `### Structure`. Findings about CC-only fields in a Copilot file, Copilot-only fields in a CC file, plugin-silently-ignored fields, body length, or subagent-unavailable tools → `### Provider safety`. A missing counterpart file → `### Pair consistency`.
`vale-wrap.sh` and `.vale.ini` ship inside this skill's own `scripts/`/`assets/` — resolve them relative to this skill's directory the same way `scripts/validate.sh` is resolved above, so the invocation works whether this skill is running from this repo or from an installed plugin cache. Run it against both files of the pair (not just the one passed in). `Kyberforge` applies to both files; `KyberforgeCopilot` applies to the `.agent.md` file only, since its one rule (`Use proactively`) flags CC-specific phrasing that's meaningless in a Copilot description — there's nothing to flag in the CC file, so it isn't scoped there. Every Vale alert is a `FAIL` — all rules are graded `error` — so report each one in the `### Description` / `### Body` dimensions citing its rule ID (e.g. `KyberforgeCopilot.ProactivePhrase`). Skip and fall back to Step 2 judgment if vale or `.vale.ini` is unavailable. If Vale reports `0 files` scanned, treat the pass as NOT RUN — not as clean — and fall back to full Step 2 judgment for the dimensions it would have covered.
`vale-wrap.sh` ships inside this skill's own `scripts/` — resolve it relative to this skill's directory the same way `scripts/validate.sh` is resolved above, so the invocation works whether this skill is running from this repo or from an installed plugin cache. Pass no `--config`: handed none, the wrapper loads its own sibling `assets/vale/.vale.ini`, located from the script's path rather than from the cwd. Adding an explicit relative `--config` breaks exactly the case the self-location covers — a resolved script path plus an unresolved config path yields `E100 Runtime error ... does not exist`, exit 2, which the fallback below then misreads as "vale unavailable". Run it against both files of the pair (not just the one passed in). `Kyberforge` applies to both files; `KyberforgeCopilot` applies to the `.agent.md` file only, since its one rule (`Use proactively`) flags CC-specific phrasing that's meaningless in a Copilot description — there's nothing to flag in the CC file, so it isn't scoped there. Every Vale alert is a `FAIL` — all rules are graded `error` — so report each one in the `### Description` / `### Body` dimensions citing its rule ID (e.g. `KyberforgeCopilot.ProactivePhrase`). Skip and fall back to Step 2 judgment if the `vale` binary is unavailable. If Vale reports `0 files` scanned, treat the pass as NOT RUN — not as clean — and fall back to full Step 2 judgment for the dimensions it would have covered.
`validate-provenance.sh` validates the provenance chain between the agent pair's `source_keys` and the plugin-scoped `sources.md` (plugin root — see ADR-0010). It exits 0 silently for non-plugin-scope agents and when no provenance data exists. Note FAILs from this script for the `### Provenance` dimension — surface them verbatim with Why and Fix.

View File

@@ -34,14 +34,14 @@ metadata:
```bash
bash scripts/validate.sh <skill-dir>
bash scripts/validate-provenance.sh <skill-dir>
scripts/vale-wrap.sh --config assets/vale/.vale.ini <skill-dir>/SKILL.md
scripts/vale-wrap.sh <skill-dir>/SKILL.md
```
Note any structural FAILs — they will appear in the report as a `### Structure` dimension. If the script cannot execute (python3 unavailable, Bash denied, or permission error), perform structural checks manually: name format, name matches directory, description length ≤1024 chars, SKILL.md ≤500 lines, no unfilled `FILL IN:` placeholders, scripts executable and free of interactive prompts.
Note any Provenance FAILs and INFO findings from `validate-provenance.sh` — they surface in the report as a `### Provenance` dimension (separate from `### Structure`). The script embeds full FAIL/INFO format with Why and Fix per finding; surface them verbatim.
`vale-wrap.sh` and `.vale.ini` ship inside this skill's own `scripts/`/`assets/` — resolve them relative to this skill's directory the same way `scripts/validate.sh` is resolved above, so the invocation works whether this skill is running from this repo or from an installed plugin cache. It applies `.vale.ini`'s `Kyberforge` style — a deterministic prefilter for a subset of the Description/Patterns/Body dimensions below, not a replacement for Step 3. Every Vale alert is a `FAIL` — all rules are graded `error` — so report each one citing its rule ID (e.g. `Kyberforge.DescriptionOpener`). Skip and fall back to Step 3 judgment if vale or `.vale.ini` is unavailable. If Vale reports `0 files` scanned, treat the pass as NOT RUN — not as clean — and fall back to full Step 3 judgment for the dimensions it would have covered.
`vale-wrap.sh` ships inside this skill's own `scripts/` — resolve it relative to this skill's directory the same way `scripts/validate.sh` is resolved above, so the invocation works whether this skill is running from this repo or from an installed plugin cache. Pass no `--config`: handed none, the wrapper loads its own sibling `assets/vale/.vale.ini`, located from the script's path rather than from the cwd. Adding an explicit relative `--config` breaks exactly the case the self-location covers — a resolved script path plus an unresolved config path yields `E100 Runtime error ... does not exist`, exit 2, which the fallback below then misreads as "vale unavailable". It applies that config's `Kyberforge` style — a deterministic prefilter for a subset of the Description/Patterns/Body dimensions below, not a replacement for Step 3. Every Vale alert is a `FAIL` — all rules are graded `error` — so report each one citing its rule ID (e.g. `Kyberforge.DescriptionOpener`). Skip and fall back to Step 3 judgment if the `vale` binary is unavailable. If Vale reports `0 files` scanned, treat the pass as NOT RUN — not as clean — and fall back to full Step 3 judgment for the dimensions it would have covered.
## Step 2 — Read all skill files

View File

@@ -133,12 +133,28 @@ else:
if desc:
ok("description has no unfilled placeholders")
# SKILL.md line count
# SKILL.md size ceilings (agentskills.io skill-authoring.md: 500 lines,
# ~5,000 tokens). Both constants are DUPLICATED from the repo-root pre-commit
# hook scripts/skill-size-check.sh — a plugin skill's scripts cannot read files
# outside the plugin directory once the plugin is cache-installed, so there is
# no single source to share. Keep the two in sync by hand: if they drift, this
# audit will report a skill ready to ship that the commit hook then rejects.
MAX_LINES = 500
MAX_WORDS = 2900 # word-count proxy for the ~5,000-token ceiling
line_count = len(content.splitlines())
if line_count <= 500:
ok(f"SKILL.md line count {line_count} (limit: 500)")
if line_count <= MAX_LINES:
ok(f"SKILL.md line count {line_count} (limit: {MAX_LINES})")
else:
fail(f"SKILL.md line count {line_count} — exceeds 500-line limit")
fail(f"SKILL.md line count {line_count} — exceeds {MAX_LINES}-line limit")
# str.split() with no argument splits on runs of whitespace, matching the
# `wc -w` the hook uses, and counts the whole file including frontmatter.
word_count = len(content.split())
if word_count <= MAX_WORDS:
ok(f"SKILL.md word count {word_count} (limit: {MAX_WORDS}, proxy for ~5,000 tokens)")
else:
fail(f"SKILL.md word count {word_count} — exceeds {MAX_WORDS}-word limit (proxy for ~5,000 tokens)")
# Body unfilled placeholders
body = content[body_start:]