optimize-skill — optimization passes

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.


1. Split — slim an oversized SKILL.md into linked siblings

Smell. 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.

  1. Identify cohesive, self-contained section clusters (a phase of the workflow, a long reference table, a mode variant) that the body can reference rather than inline.
  2. Move each cluster verbatim into a sibling <topic>.md next to SKILL.md. Use cut-and-paste of the exact bytes; do not re-flow or paraphrase.
  3. Replace the moved region in SKILL.md with a one-line pointer to the new sibling (e.g. “… per gather.md.”, as a real link).
  4. Keep the orchestration — Step 0, the step skeleton, Hard rules, the gates — in SKILL.md. Only the elaboration moves out.
  5. Regenerate the doctoc TOC if the body's headings changed.

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.


2. Config-lift — move concrete values into <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.

  1. For each concrete value, add (or reuse) a knob in <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.
  2. Replace the literal in the skill body with the placeholder / manifest-resolved reference.
  3. Where the lifted logic is more than a value — a whole adapter contract — extract it into a 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.


3. Out-of-context — read/PATCH a field without loading the body

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.

  1. Identify the single field / append the step actually needs.
  2. Route the read-modify-write through the existing tool — github-body-field for one ### Field section, github-rollup for the status-rollup comment.
  3. Replace the in-context fetch-then-edit prose with the tool call; keep the decision about what to write in the skill, the mechanics of writing it in the tool.

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.


4. Fetch-upfront — batch per-item round-trips into one pass

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.

  1. Find the per-item fetch loop.
  2. Replace it with a single upfront query (or the smallest number of batched queries) that returns the whole set, honouring the validator's --limit requirement on list calls (#359).
  3. Iterate over the fetched set; the per-item analysis stays per-item, only the fetching batches.

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.


5. Preflight-classifier — skip obvious no-ops before LLM passes

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.

  1. Identify the deterministic signals that mark an item as a no-op (recent human activity, a skill-written marker, closed-and-aged, bot-only activity).
  2. Run the classifier (existing tool or a small new one) as a Step-0 / pre-flight filter; record per-item the reason it was kept or skipped in the observed-state bag.
  3. Feed only the survivors to the probabilistic pass.

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).


When a pass is not an optimization

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.