The five passes SKILL.md applies, each distilled from a landed refactor of the security-skill suite. For every pass: the smell that triggers it (the read-only diagnostic), the exemplar PR that proved it, the mechanics, the behavior-preservation guarantee, and the validation that confirms it landed cleanly.
Passes are ordered by blast radius — a pure file move is safer than a content lift, which is safer than rewiring how a step executes. Apply in this order so the reviewable diff stays small and each step is independently revertible.
SKILL.md into linked siblingsSmell. SKILL.md exceeds the 500-line P14 cap, or one section dominates the body. Diagnostic: wc -l SKILL.md; flag > 500, and note the largest ## sections as split seams.
Exemplar. refactor(security-issue-sync): extract 4 subdocs to slim SKILL.md 3425 → 658 lines (#410) — “same pattern as setup already [uses]. No behavior change — pure file restructure. Validator stays green.” It lifted gather.md, signals-to-actions.md, apply-and-push.md, and bulk-mode.md out of the body.
Mechanics.
<topic>.md next to SKILL.md. Use cut-and-paste of the exact bytes; do not re-flow or paraphrase.SKILL.md with a one-line pointer to the new sibling (e.g. “… per gather.md.”, as a real link).SKILL.md. Only the elaboration moves out.Behavior-preservation guarantee. The concatenation of the slimmed SKILL.md plus the new siblings contains the same instruction bytes as the original body. A git diff shows deletions in SKILL.md matched by identical additions in the siblings, plus the new pointer lines — nothing else.
Validation. Validator green; SKILL.md now under 500 lines; every sibling linked exactly one level deep from SKILL.md; no unreferenced sibling left behind.
<project-config>Smell. Adopter-specific values are baked into the skill body: a concrete repo slug, a real mailing-list address, a real CVE ID, a project-specific label or milestone name — anything that should resolve from <project-config>/project.md at runtime instead of living in the skill. Diagnostic: run the placeholder linter (tools/dev/check-placeholders.sh) and scan for hardcoded strings outside example: markers.
Exemplar. feat(security): config-driven lifts of 6 skills (#386) and the CVE-authority / forwarder-relay / mail-archive sub-tool extracts (#388, #387) — project-specific knobs lifted into the manifest's Security workflow configuration block so the skill body reads them through placeholders.
Mechanics.
<project-config>/project.md with a # comment stating what it controls, the ASF default, when a non-ASF adopter overrides it, and the consuming skills.tools/<name>/ adapter the skill resolves at runtime (the #387/#388 sub-tool shape).Behavior-preservation guarantee. For the reference adopter the resolved value is identical to the literal it replaced. The skill does the same thing; it now reads the value from config instead of carrying it. Swapping projects becomes a config change, not a code change (Principle 12).
Validation. Placeholder linter green; the reference adopter's manifest supplies every newly-referenced knob; validator green.
Smell. A step pulls a whole issue body, a rollup comment, or another large artefact into the agent context only to read or rewrite one field of it. The full text enters the context window (token cost + a re-injection surface) for a single-field edit.
Exemplar. feat(github-body-field): tool to rewrite one issue-body field without loading the body into agent context (#412) and feat(github-rollup): append helper for status-rollup comments — read/PATCH out of context (#424). Both move a body/comment mutation behind a deterministic tool that fetches, edits one field, and writes back without the body ever entering the agent context.
Mechanics.
github-body-field for one ### Field section, github-rollup for the status-rollup comment.Behavior-preservation guarantee. The field ends up with the same value; only the path it took changed. What the skill proposes to the human and what lands on the tracker are identical — the body simply never enters the context window.
Validation. Validator green; the step's proposal/apply surface unchanged; a measurable drop in context loaded for that step.
Smell. The skill issues N sequential fetches (one per candidate issue / thread / PR) where a single upfront query would return the whole working set. Latency and API-call budget scale with N for no analytical reason.
Exemplar. feat(security-issue-triage): fetch-all-upfront pattern (PR #346 analogue) (#347) — collect the full candidate set in one pass, then iterate over the in-memory result instead of round-tripping per item.
Mechanics.
--limit requirement on list calls (#359).Behavior-preservation guarantee. The set of items processed and the per-item decisions are unchanged; only the number of round-trips drops. Guard against the batch hitting a page cap — surface a “count may be a floor” warning rather than silently truncating.
Validation. Validator green (including the --limit check); same items processed; fewer calls.
Smell. The skill spends an LLM pass per item even though a cheap deterministic check could classify many of them as obvious no-ops (idle, already-handled, out-of-window) up front. Probabilistic effort is spent on what executable code already decides (Principle 5).
Exemplar. feat(security-issue-sync): pre-flight no-op classifier skips obvious-idle trackers in bulk mode (#414) and tune pre-flight classifier — skill-marker detection + relaxed rules (#416) — a deterministic classifier (see tools/preflight-audit) runs first and drops items that need no work, so the LLM pass only sees the candidates that actually require judgment.
Mechanics.
Behavior-preservation guarantee. Items the classifier skips are exactly those the LLM pass would also have classified as no-ops — the classifier is tuned conservatively so a borderline item is kept, not skipped. The final decisions on real candidates are unchanged; the wasted passes disappear. Log what was skipped and why (no silent truncation).
Validation. Validator green; the classifier's skip set is a subset of what the full pass would no-op; replay/eval fixture exercises the classifier rules (the #423 pattern).
Each guarantee above draws the same line: a pass may change how a skill runs, never what it decides or proposes. If applying a pass would change the items processed, the values written, or the prose a human signs off on, it is a behavior change — stop, and route it through normal skill editing and review, not this skill. The green-before / green-after validator gate plus the per-pass behavior-preservation check are what keep that line honest.