Skill Authoring for Weaker Models
Institutional files are read by a model with zero context and less judgment than the author. Write for that reader. Every rule must survive being followed literally, without the author present.
1. The executable-rule format
An abstract demand ("keep quality high", "be careful", "verify appropriately") without a judgment criterion is noise — it costs context and changes nothing. Every load-bearing rule states:
- Trigger — the observable condition under which it applies.
- Steps — what to actually do, imperative, copy-pasteable where possible.
- Done — the completion definition; how the follower knows it worked.
And where misreading is costly or the judgment boundary is subtle, add:
- One positive and one negative example — the fastest way to transmit a judgment boundary to a weaker model. The strongest negative example quotes a rationalization actually observed ("tests are probably fine — the change is small") and names why it fails.
- On failure — the next step when it does not work (retry differently, escalate, mark unresolved), so failure does not improvise.
Placement is part of the format: an eligibility or refusal hard-exit
gate precedes the first artifact-producing step, not mid-procedure. A
refusal or scope check positioned after
generation has begun gets blown past by mid-build momentum — the executor
already has an artifact to protect and reads the gate as an obstacle. The
same check asked first costs one sentence and holds. Smoke-measured
fail-then-fix at the source (n=1 per cell): a weak-tier executor ran 68
tool calls and escaped its sandbox past a mid-procedure scope check; with
the identical check moved before the first generation step, six tool
calls, nothing generated, correct early exit. When a rule refuses or
scopes the work — "refuse red-line domains", "no adapter when the sector
is coding in disguise" — the skill's step order puts that test before
the executor has produced anything; done when every eligibility or
refusal test precedes the first artifact-producing step in the skill's
ordering. A verification gate whose input IS the produced work (tests
pass, a ship check) stays terminal — this rule moves eligibility and
refusal checks, not verification.
❌ "Stage 4: before finalizing, confirm the sector needed an adapter at
all" — by Stage 4 the adapter exists and the check reads as waste.
✅ the same sentence as Stage 1's first bullet, before any
artifact-producing step.
(unprobed in-house; external evidence — see Provenance.)
Before a rule earns its place — the author-time pruning pass (unprobed
— see Provenance). The format above says how to write a rule; this is the
cheap pre-filter for whether to, and where. Ask five questions — each hands
off to the section that owns the deep or empirical resolution; the pass
screens, it does not re-run those sections:
- No-op — would the weaker, zero-context reader this pack is written for behave any differently with the rule than without it? Judge at that target tier, not a strong model's; the empirical form is §7's bare-vs-ruled probe.
- Default-delta — is the behavior already the base default, or already compelled by a standing higher rule? A rule earns its line only in the delta over that default, measured at a stated tier, never assumed (§7).
- Placement — where does this rule belong, and is the always-loaded body really it? Decide with §4's Placement test. (Frontmatter/trigger metadata carries routing conditions, never rule content.)
- Retrieval-cost — does a clause only a minority of loads needs sit in the always-loaded body? Move the rare case on-demand (§4's earn-the-line).
- Contradiction-pressure — does it obviously collide with an existing authority, a stated exception, or an adjacent skill's rule? A detected collision escalates to §6's rule-by-rule doctrine walk — the screen here is that walk's trigger, not the walk itself. Disposition: a rule earns its place only when all five questions pass. One it fails sends the rule back — drop, re-home, or rewrite — before it lands; a detected contradiction blocks it pending §6. Nothing here overrides §6's review or §7's probe, which stay the empirical backstop. Guardrail: this pass never deletes, weakens, or relocates a load-bearing clause — a safety, verification, fail-closed, or authorization-boundary rule, the eligibility/refusal/scope hard-exit gates §1 keeps before the first artifact-producing step, or the user-confirmation gates §7 keeps for destructive, spending, or publishing actions. Such a clause, however rarely it fires, stays at the executor's decision point; that retrieval-cost is paid on purpose. When you cannot tell whether a clause is load-bearing on one of these axes, treat it as load-bearing — the pass fails closed.
2. Ground truth only
- Verify every command, flag, path, and claim against the actual repo/system
before writing it down. A wrong runbook is worse than none, because it is
trusted. That includes the pointer inside a rule you are writing: a
cross-reference's section number is a claim about the target file — derive
it by opening the file, or from search output that is current and shows
the enclosing heading (a bare quoted body line does not establish its
section), never
from recall of the file's structure. Two mis-targeted §-references were
written
by one author in one day, each from memory while the correct location sat
already-quoted in that author's working context. One reached main and cost
a maintainer fix commit, verifiable in this repo as
8f8413f; the second was caught in the author's own draft before submission (contributor-reported). (This clause shipsunprobed— see Provenance.) ❌ "the sync contract is in §4" (recalled; the quoted line was in §3). The same discipline covers an incident NARRATIVE, not just a pointer: draft a Provenance paragraph from the re-opened primary source (the transcript, the PR thread, the finding file), never from a paraphrase carried across sessions — narrative drift reads plausible and passes a review scoped to the rule's text, and a reviewer told not to re-verify quoted incident details will not catch it; the discipline sits with the author at draft time. (unprobed— see Provenance.) - Verifying the incident does not verify the prescription. Distilling an
incident into a rule is a lossy transform that can introduce a bug the
incident never had: the rule cites a real failure yet prescribes a
mechanism that itself fails on exactly the case it targets (
git cherryfor squash-merge residue on a multi-commit branch — its per-commit patch-ids never match the single squash commit, canonical rule in operational-rigor §2; "ack a webhook before durably recording it" — a post-2xx crash then loses the event; "peek-then-commit" a spend cap — a TOCTOU overspend race under concurrent fan-out). One reviewed batch of 27 incident-mined rules had 4 of exactly this shape, each passing the author's own self-review and caught only by a cross-family mechanism review before merge. So when a rule's fix is a specific mechanism — a command, protocol, or algorithm distilled from a failure — before it ships for an agent to execute verbatim: (1) fix the correct OUTCOME in advance for both its own motivating scenario — traced through the failure mode it names (the crash, the concurrent fan-out, the squash), not merely confirmed that the incident was real — AND the nearest variant with one property flipped (multi-commit → single-commit, crash → no crash, concurrent fan-out → one worker); (2) run the mechanism against both and confirm it matches each — correctness that flips across that boundary is the trap this catches (git cherry is wrong on the multi-commit squash it targets yet right on a single-commit branch); (3) get a cross-family mechanism review (cross-model-review) attacking the MECHANISM, not the prose. No second family available →cross-model-review§6's fallback (same-model fresh-context critic, gap recorded) applies here too. - What cannot be verified is labeled
unverifiedoruser-must-provide— never silently invented. Unproven ideas stay labeled open/candidate; no oversell. - Verify-before-you-write-it bites hardest on a capability you describe for a
weaker executor (
unprobed— private incident as shape; see Provenance). "The engine auto-searches every category", "this flag does X" — a weak model executes such a line verbatim, with none of your context to catch it when it is wrong, so an unverified capability claim is a false instruction to the one reader least able to notice. When the behavior is new or just-patched, order it: prove it against a real gate (ground-truth-gates), THEN finalize the doc that describes it. A scarce live session (an auth window about to expire, a costly remote setup) argues for verifying FIRST, not for shipping the doc on the theory it probably works. ❌ drafting "the engine now self-heals across categories" into the skill while the self-heal fix is still unproven, to spend the login session before it expires. - Embed the knowledge itself; do not make private paths or one person's
memory a load-bearing reference. A hard-coded machine-absolute path is worse
than a broken link: a stale duplicate clone resolves silently to an
outdated copy and gets trusted (more dangerous than a 404, which at least
fails loud). Anchor to the VCS root (
git rev-parse --show-toplevel) and verify the path prefix before reading.
3. Provenance and decay
- Date-stamp volatile facts (versions, flags, model names, defaults).
- Staleness concentrates in the world-fact rules — audit there first (first in-house probe 2026-07-27 did NOT discriminate — both arms produced the scoping, the bare one unaided; one run screens for a large effect only, so it is flagged for a demotion judgment rather than demoted — see Provenance). A rule encoding a mutable fact about the outside world (which model or tool is default, how a CLI behaves, a numeric threshold someone measured) can go stale within days; a rule encoding method (reproduce before trusting, verify by execution) goes stale rarely and slowly. A staleness audit therefore concentrates the search on the world-fact files and treats the method-encoding files as low-yield — not as exempt. One sweep of four rules files found every stale instance — 8 of 8 — in the single file that carried world-facts; the three method files were clean. This scopes the SEARCH only: a method file still gets corrected when something does surface in it.
- Capability-negative claims rot the worst (
unprobed— private incident as shape; see Provenance). About to write "no such flag", "only works interactively", "the API can't do X" into an instruction file — or about to act on one already there: these are version-scoped observations that read as timeless rules. A stale positive claim is far likelier to be exercised and exposed the first time someone follows it; a stale negative fails silent — it steers every later session away from a capability that now exists, and nothing ever exercises it to expose the rot. One playbook's "model switching only works in the interactive UI; no flag" was actively wrong at the tool's current version and had been routing sessions into a degraded path. A negative about a hosted model's BEHAVIOR is the one class a version pin cannot hold — hosted endpoints drift behind unchanged strings; that class follows delegation-and-review §1's pinned-string rule, not this protocol: date-stamp the recorded claim where written, and any session acting on it — routing decision or not — re-probes at decision time before repeating or relying on the negative, the re-probe satisfied only under the pinned-string rule's own attribution and unknown-property-fallback clauses, carried verbatim: "an unattributed answer measures an unknown model, not the slug's", and probe unavailable or failing → "assume the ADVERSE plausible state for this decision"; on any wording disagreement, the pinned-string rule wins. Writing a tool-interface negative: pin it to the version and probe it was observed on — and a capability controlled server-side (an API feature, an account rollout, a remote configuration) additionally pins the instance/account and observation date, because it can flip with no version change. Acting on one: read its pin; the tool's version has changed, the pin is missing, or the capability is server-side and any pinned dimension (instance, account, configuration, or simply time since the dated observation) may have drifted → re-verify with one probe (--helpfor a local interface claim; a server-side capability probes against the CURRENT decision's resolved instance/account — re-probing the former pin is comparison evidence, never the acting gate, and a local help screen proves nothing about an account-controlled feature; an existence claim — a flag listed, a field accepted — settles on--helpor a schema read, while a FUNCTIONAL claim needs a trial invocation exercising the claimed-absent capability, and a trial whose success would be consequential (a send, a delete, a purchase) runs as a safe synthetic or dry-run form, or under its own authorization (operational-rigor §2) — no safe form and no authorization → the capability stays unknown), recording the newly observed dimensions, before obeying it; probe unavailable or inconclusive → the capability is unknown, not absent — record that where the claim is used and do not repeat the negative as fact. Done: writing — the claim carries its version pin, the probe that observed it, and (server-side) its instance/account and date; acting — every applicable pinned dimension is matched current, or re-probed, or recorded unknown. ✅ "playbook says no flag (pinned v0.2.98); current binary v0.2.101 — --help lists the flag now (existence), a dry-run invocation accepted it (function); corrected the playbook in place." ❌ "the playbook says there's no flag, so drive it through the UI." - A recorded environment remedy is a hypothesis on reuse, not a fact —
verify it fired this time, and retract it in place when it doesn't
(
unprobed— private incident as shape; see Provenance). A fix for an environment quirk (a process restart, a service bounce, a config toggle) gets written to memory once it worked, then reused across sessions on the strength of that one success — but the underlying cause can be a different bug next time the same symptom appears, or the environment can have moved out from under the remedy entirely. Applying a recorded remedy without confirming the symptom actually cleared repeats the capability-negative failure above in the opposite direction: a false negative fails silent, a false remedy fails LOUD the first time someone trusts it and it doesn't work — but only if the session checks; skipped, it just re-applies the broken fix next time too. Before writing "X fixes Y" into a durable file: confirm Y actually cleared, not merely that X ran without erroring. Before reapplying a recorded remedy: confirm it fixed THIS occurrence before moving on, and when it doesn't, correct the rule in place (per the correction discipline below) rather than leaving the disproven fix for the next reader. ❌ "restart the service — that's the documented fix" written once, applied unverified in three later sessions, until a session that checked found the symptom persisted and the note was stale. - Correct a stale rule in place — never append the correction below the old line. A zero-context reader obeys whichever sentence it reads first, not the latest one.
- End each skill with a short provenance note and a one-line re-verification command for anything that may drift. A skill without a re-verification path decays into exactly the stale-instruction problem it was meant to solve. And that command has to hang off something the work already touches (first in-house probe 2026-07-27 discriminated: bare arm documented the condition with nothing reading it, ruled arm bound it — see Provenance): an invalidation condition needing a separate act of remembering is inert no matter how precisely it is written. Bind it to a surface the next pass crosses anyway — a line in the maintenance entry that pass must read, an assertion in a gate that already runs, a trigger on a file someone edits regardless. A measured finding carrying the clause "re-test if either file grows past ~250 lines" sat at 297 and 318 for days, still cited as current: the threshold was right, the condition was true, and nothing was reading it. ❌ "the invalidation condition is documented at the end of the finding."
- A merged upstream integration is not necessarily the end of the
campaign (
unprobed— the upstream half of the incident is verifiable in this repo's PR history, the sync half contributor-reported; see Provenance). Before diff-verifying a local file against "upstream final" and closing the sync, check for continuation on the synced surfaces — a maintainer's review can continue in follow-up PRs rather than concluding in the one that first merged, and at sync time those rounds may not have merged YET. The synced surfaces are every file the sync contract couples (the change-X-update-Y pairs), not only the file in hand. Check BOTH lists, each with its own invocation, on the SAME upstream repo and target branch lineage (a backport into another release branch is not a hit) — OPEN first, then MERGED, so a PR that merges between the two queries leaves the first set only by entering the second: ALL currently-open PRs (no creation-time bound — a follow-up opened BEFORE the anchor merged still counts; e.g.gh pr list --repo <upstream> --base <branch> --state open), then PRs merged after the anchor by MERGE TIME, not PR number (e.g.gh pr list --repo <upstream> --base <branch> --state merged --json number,mergedAt— the repo/base flags and the mergedAt field are load-bearing: an unflagged query can read the wrong fork or default branch, and PR numbers do not order by merge time). A CLOSED PR is not automatically a non-hit. Some maintainers land contributions by rebuilding them (no cherry-pick) into a consolidated branch grouped by target file, merging that branch, then closing the original PRs with a disposition comment — GitHub never marks the originals MERGED even though their content is live on the anchor branch, and the OPEN/MERGED queries above never surface them (observed:F-e-u-e-r/opus-packPRs #173–181, closed individually, landed via consolidated PRs #194–197). CLOSED ≠ rejected — read the disposition comment, or diff the PR's own changes against the anchor branch, before excluding it as a non-hit; a closed PR with no disposition comment and no matching content on the anchor branch is the only shape that safely reads as declined. Each list is enumerated to EXHAUSTION — the tool's default page size (gh's is 30) silently truncates, and a date bound does not lift the cap: paginate until the last page is short, and record the total counted. "Touching" is decided from each candidate's CHANGED FILES read mechanically, with the per-PR query ALSO repo-scoped — PR numbers are repository-local, so an unflagged view from a fork checkout reads the wrong PR (e.g.gh pr view <n> --repo <upstream> --json filesorgh pr diff <n> --repo <upstream> --name-only), never from titles or bodies — a continuation PR's title may carry no path token while it edits the synced file. File enumeration has its own caps (gh's files query returns the first 100; hosted diffs truncate around 300): verify the retrieved file count equals the PR's changedFiles total, and when completeness cannot be proven, treat that PR as TOUCHING (conservative) or keep the sync provisional. One OPEN+MERGED pass is a snapshot with blind windows at its edges — a PR can change state between any two queries — so REPEAT the pass until a full OPEN+MERGED pass adds NO new TOUCHING-OR-UNCLASSIFIED candidate versus the previous pass — every newcomer gets its changed-files classification, AND every still-open candidate is reclassified each pass — an open PR's files mutate with new commits (track head OIDs to skip provably-unchanged ones); a transition to touching-or-unclassified destabilizes, while non-touching classifications never do (else a busy repo livelocks into provisional despite zero synced-surface hits); each pass's merged query re-covers whatever the prior open query lost to a merge. Still unstable after three passes → record the sync provisional, no further queries owed. A rename touches when EITHER path side matches a synced surface — path-oriented file listings can hide the old path, so where the tool does not expose both sides, treat renames conservatively as touching. Any touching hit → do not close the sync as final: re-anchor to the newest touching merged state, RE-DIFF the local files against that new state, and re-run the checks, or — when touching rounds are still open — record the sync as provisional with the follow-up fold owed. The re-diff is a GATE, not a citation: final closure requires zero unexplained sync-contract differences (differences → fold them and re-run; unresolved → provisional). Done when the sync record cites the stable-pass checks (commands + date + totals) with ZERO TOUCHING HITS — the candidate lists may be nonempty — and a clean local diff against the anchor state; that makes the anchor safe AS OF the check, never forever; otherwise it carries the provisional label. - Contributing a rule is not adopting it — a merge of your own rule
into a shared library opens an adoption debt (
unprobed— see Provenance). The campaign bullet above runs upstream; the same merge leaves a SECOND thing open in the other direction, and the author is the likeliest reader to miss it. The merge closes the contribution while your own always-loaded files still do not carry the rule — and because you have been applying it by hand all along (you wrote it; it is in your working context), nothing feels missing. It is not adopted, it is remembered, and remembering ends with the session; the next one reverts to whatever the files say. Bind the debt to a surface something re-reads — the same ledger row or sync record that logs the merge carries it, and the row closes only on the port done or a reasoned decline recorded; probe-then-port keeps it open (or moves the debt to another surface something re-reads). A debt parked only in a plan, a summary, or an owed-line nothing re-opens is the invalidation-clause failure this file already warns about, wearing a different hat. ❌ "the rules merged upstream and I have been following them all session, so that batch is done" — followed from conversation context, by the one reader who cannot notice their absence. - When two files must agree, write the sync contract down ("change X → update Y") in the canonical file. Prose inventories rot; prefer "read the directory" over hand-kept lists, and pin unavoidable lists with a rule or test. Do not paraphrase a load-bearing clause in a secondary location — quote it verbatim or point to the canonical copy (a paraphrase drifts silently), and the sync contract must name which file wins on disagreement.
- A skill's internal citations are addressed to ITS library — re-resolve
every one against the destination on install (
unprobed— contributor incident as shape; see Provenance). A distributable skill cites siblings by section number and by name ("delegation-and-review §3", "the author-is-not-the-judge rule"). Those addresses are relative to the library it was written in. Installed into a library that numbers its sections differently, or that never adopted the sibling, each one still READS as valid and now points somewhere else — the silent failure the §2 absolute-path rule names, in citation form: a stale§3resolves to a real section with the wrong content, where a 404 would at least fail loud. So on install, resolve every citation against the DESTINATION file — first pin what it addresses upstream (the section heading or the named rule; a bare§Ncarries no greppable name until you do), then grep the destination for that, never the number alone — and classify each: retargeted (the local address differs), unchanged, or absent-here (the sibling rule does not exist locally; delete the pointer — rewriting the sentence to stand without it, not to absorb the missing sibling's semantics (the no-paraphrase rule above holds) — or replace it with a non-resolving gap marker, never a live§Nthat resolves locally to unintended content; the port note records either, so nothing is dropped silently or left dangling). Then record the retargets in the port note as upstream-citation → local-target pairs — each carrying its heading or named anchor, since numbers alone go stale on the next renumber; re-resolve anchors on every re-sync rather than replaying numeric pairs — and each absent-here outcome as delete-or-gap, because the edits are now local divergences from upstream: unrecorded, the next diff-against-upstream reads your own retargets or deletes as drift and a re-sync silently restores the broken pointers. The port note is what makes them re-applicable and lets the diff exclude them. Done when no citation in the installed copy resolves to a section the author did not mean, and every difference from upstream is either in the port note or a real drift. ❌ "the port is byte-identical to upstream" — byte-fidelity is the wrong test: into a differently-numbered library, byte-identical IS the bug. - Package a set with its own honesty ledger. Alongside its START-HERE router
(§4), a multi-skill project library ships two more companion files — a
MANIFEST (one line per skill → what it is + the evidence backing it, so the next
maintainer can re-verify and knows what would falsify it) and an UNCERTAINTY
register that quarantines everything not settled, each item bucketed and ending
in a safe default; the three together are the packaging trio. A one-off handoff
needs neither companion file — just an uncertainty / safe-default section when
claims are unsettled. Bucket shapes and the trio:
references/project-skill-templates.md.
4. Memory architecture
- One source of truth per fact. One canonical instruction file per repo; other entry files include or point to it. Never maintain the same content in two places.
- The always-loaded file is a short router. CLAUDE.md/AGENTS.md holds only what every session needs plus pointers; long content lives in load-on-demand skills/docs. Every always-loaded line taxes every future session — it must earn that.
- Fix log: one incident per file (problem / root cause / fix, with frontmatter for search), written right after the incident while the cause is fresh. Batch-imported backlogs produce a pile, not a log.
- Memory, notes, and fix-log files never hold secrets — no keys, tokens, or credentials; name where a secret lives, never its value.
- Compile, don't retrieve. When a fix-log entry reveals a default rule, promote the rule into the standing instructions; the entry remains as the record of why. Retrieval re-derives the answer every session; compilation pays once.
- Sort a durable note by its future reach, not its topic (
unprobed— see Provenance). Three tiers, one admission test each: useful only for the current task → that task's own working file; a record of what happened this session, of no standing use later → a session journal; something to be followed every future time this kind of work is done → the standing spec/rules. The tier is set by how far forward the note applies — not by how long it stays literally true (a session event stays true forever yet still belongs in the journal, not the standing rules), and not by what it is about. A "fix" that is really a permanent convention belongs in the spec (compile-don't-retrieve above), not left in a task file the next task never opens. - Flipping a current-state order does not retire the old one on its
own — sweep the whole file (
unprobed— private incident as shape; see Provenance). The instruction-file analog of operational-rigor §3's call-site sweep: a flipped default is an interface change whose call sites are every older verdict block in the same file. Updating the top summary or the newest paragraph is not enough: an older evidence block can still carry its own bold imperative verdict ("KEEP X AS DEFAULT") lower in the same file, and a future reader — or a weaker model that greps by the old term, lands mid-file on a retrieved chunk, or reads a bottom-appended log in order — can meet that older verdict first and follow the superseded order. After any default/order flip: grep the file for the superseded term(s) and their aliases — an empty grep is not a clean sweep (§5's keyword-grep-absence rule: a stale verdict can phrase the incumbent without the term), so read every verdict-bearing block — and neutralize each stale verdict IN PLACE: rewrite the verdict line itself, never a note appended below it (§3's correct-in-place rule — a zero-context reader, or a retrieved chunk that starts at the old bold line, obeys whichever sentence it reads first). The old imperative stops being one: "KEEP X AS DEFAULT" becomes "SUPERSEDED<date>— was: keep X as default — see<new order's anchor>; this block is provenance, its verdict is no longer the order". Rewrite rather than delete — history stays legible, but only one verdict reads as current. ✅ "promoted the new default at the top, then grepped the file for the old model's name — found two older 'KEEP AS DEFAULT' blocks, rewrote both verdict lines in place as SUPERSEDED-with-date pointing at the new order." ❌ "updated the current-state summary; the old benchmark write-up down below is just history, nobody reads that far" (a weaker executor does). - A contradiction between two verified results is not automatically a
supersession — diff their run conditions before either claim wins
(
unprobed— private incident as shape; see Provenance). Two results that disagree can both be true, each on its own scope (task difficulty, version, environment, input shape); a bare recency heuristic ("pick one — more recent / more tested — say why, flag the other") is the right move only once you've confirmed the results are actually measuring the same thing. Before applying it: name the candidate explanation for the disagreement, then verify it — don't assume the first plausible story. A recorded finding said a model scored 0/20 on headless file-edits; a fresh bench on what looked like the same task scored 2/2 clean. The first guess was "version drift" — checked, and the binary build was identical between both benches, which killed that explanation; the real difference was task complexity (the old bench drove harder multi-file edits, the new one a single-file edit). Both scores stayed true, on different task shapes. The fix is to scope-annotate BOTH findings with the condition that actually differs, not to overwrite the older one — a naive recency pick would have retired the 0/20 finding and mis-taught every future reader that the model handles complex edits. ✅ "0/20 (harder multi-file dir-mode edits, frontier bench) vs. 2/2 (simple single-file edit, this bench) — same binary build, different task shape; both stand, scoped." ❌ "the new bench says 2/2, so the model actually works now" — recency applied without checking whether the two benches tested the same thing. - Two-strike promotion trigger: the second time a lesson's trigger
fires, that event promotes it — into a standing rule, or a hook where
machine-checkable — and the entry gets a
promoted-to:line. One occurrence is an anecdote; two is a pattern. - Placement test for any new rule: can it be a hook (machine-enforced)?
If not, can it live on-demand (skill / fix log)? Only when both answers
are no does it earn an always-loaded line. An always-loaded line still
owes a second placement question: which existing bullet already owns
its topic? Confirm the SECTION first — check the addition against the
section's own scope, not merely the nearest bullet's — then read the
target section's bullets in full and default to folding the new clause
into the host bullet that owns it (a trailing sentence, an added
example, an extra clause); a standalone new bullet is the fallback for
content with no existing host, not the default shape. Genuinely unclear
whether it is this bullet's topic or a neighbor's → flag for the §6
reviewer rather than defaulting to standalone. (
unprobed— see Provenance.) - Log recurring slop the same way — agent output that compiles and looks plausible but is subtly wrong (the six patterns are canonical in operational-rigor §5). One category captured once prevents it forever.
5. Skill-set design
- One skill, one topic; no duplicate homes for a fact — cross-reference the sibling instead. Each skill states when NOT to use it and which sibling to use.
- Keyword-grep absence is not absence (
unprobed— private incidents as shape; see Provenance). About to add a new fact or rule to an instruction file, or to conclude one does not cover a fact (wording-only and provenance edits are out of scope): an empty grep is not the dup-check the no-duplicate-homes rule above needs — rules phrased differently from the search term repeatedly produced false "not covered" verdicts in the contributor's private log (see Provenance), one a proposed addition whose content already sat in the target file under other wording, caught only by reading the section at drafting time. The check: grep the target file and its sibling skills (the skills shipped beside it — list the parent skills directory, don't recall it, and include each searched skill's references files when the topic plausibly lives there; when the repo also carries router or entry instruction files — CLAUDE.md, AGENTS.md, a memory index — those join the search too, since a fact canonical in an entry file makes any skill addition a second home; a router file like CLAUDE.md as the TARGET has no siblings — its "siblings" are the files it points into) for the concept's name plus at least two alternates drawn from how the file might phrase it (the outcome it produces, the operation's other names, its domain jargon); list the actual section headings of the target AND of every file searched; from that real outline — never from memory — name the candidate homes (every section with a hit, plus every section the fact would live in if it existed) and read each in full before any verdict. A headingless file is read in full. When the candidate reads end with no duplicate found — and always when every search came back empty — read every searched file in full before any absence verdict: the trigger for the full read is failing to find the duplicate, never grep emptiness (one irrelevant hit must not disable the fallback), and the incidents' catch was the read, not the grep. Duplicate found → no second home, wherever it lives: in the target, no addition; in a sibling, cross-reference it — the "A cross-reference is not a load" rule below still applies as written. Otherwise the change record — the PR description or commit message when one is being created, otherwise the completion report — carries the result line: the terms searched, each file searched with what was read of it (named sections, or "read in full"), and "not found under the searches and sections listed". For a landing addition, the fresh-context reviewer (§6) re-runs those searches against the pre-addition text (the file at the revision the change branches from AND at the landing target's current pre-merge state — the base can gain an equivalent rule after the branch point; never the edited working copy) and reads at least one candidate of their own choosing — and before CONFIRMING an absence verdict, runs the author's own fallback: every searched file read in full when the duplicate was not found (a one-file sample confirms nothing — short of the full read, the verdict stays provisional and says so); a batch landing multiple additions — to one file or across files in the search set (targets, siblings, routers/entry files) — also READS each added rule body against the other additions in the batch, searches alone never discharging it (two additions can express one doctrine with disjoint vocabulary, exactly the empty-grep blind spot this rule opens with), plus searches the merged result across all added hunks (two additions can duplicate each other while neither exists in any base); a standalone not-covered verdict with no reviewer stays provisional in the report until a fresh-context reader without the author's session confirms it there. A bare "not covered" backed only by empty greps is the failure this rule exists to stop; no plausible home in the outline for a fact the task says is covered or being relocated, or doubt that the candidate list is complete → the placement is unresolved — escalate it, and under those conditions never assert absence. ✅ "grep for 'revert', 'rollback', 'undo' across the playbook and its two siblings returned nothing; read all three files end to end — the rule exists in the playbook under 'restore': duplicate found, no addition; cross-referenced the playbook's rule instead." ✅ "all searches empty — read both searched files end to end; recorded 'not found under the searches and sections listed: revert, rollback, undo; playbook.md (read in full), helpers.md (read in full)' — then added the rule." ❌ "grep returned nothing, so the file doesn't cover it." ❌ "three synonyms, all empty — not covered" (no file was ever read). - A cross-reference is not a load (
unprobedin-house; external evidence — see Provenance). On weak tiers, discovering that a sibling skill applies is a judgment act: fable-method published a smoke-grade negative on exactly this — in-skill pointers went essentially unpicked-up by a weak executor across their rewordings (shape cited; their log carries the numbers). A clause a specific decision cannot afford to miss travels WITH the trigger point — quoted verbatim at the site that fires (§3's no-paraphrase rule; the quote inherits §3's sync contract naming which copy wins), not only pointed at; the cross-reference serves the strong reader. - The frontmatter
descriptionis the trigger: write it as the exact conditions under which a model should load the skill, not as a title. Phrase triggers as observed states ("a test failed twice"), not topic labels ("debugging") — states fire; labels drift. A skill that never fires is dead weight; a skill that always fires is a tax. - A description is a rule too — probe its ROUTING, never just its
prose (
unprobed— see Provenance). The description is the layer loaded before the skill fires at all, and a rule §6's behavioral probe never reaches: that probe hands a fresh reader the file already loaded, which tests whether the loaded content is u
…(truncated)