Skip to content

chore(skills): add the repository's agent skills - #1143

Merged
OmarAlJarrah merged 137 commits into
mainfrom
chore/agent-skills
Oct 29, 2026
Merged

OmarAlJarrah merged 137 commits into
mainfrom
chore/agent-skills

Conversation

@OmarAlJarrah

@OmarAlJarrah OmarAlJarrah commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Summary

Reviews and fixes here spend much of their time rediscovering how work in this repository is verified: which probe answers a claim, which controls a differential needs, which reader of a construct a change missed. Those lessons live only in reviewers' heads and in the history of PRs that fixed defects after their first push. This adds them as skills under .claude/skills, which coding agents load when a task matches. Each skill's body loads on demand and its reference files load one at a time.

Skill Loads when Holds
morphic-verifying-changes writing a test, a verifier, a lint or archtest rule or a skill's script, fixing a defect, claiming something is caught, comparing with main, stating a count what done means here (nothing left to find, a defect main shares fixed or filed, a fix at the type that owns the broken state); claim → probe table; rules for claims, counts that drift, sweeping every site, tests, assertions, checks that reach what they claim, planting defects in a throwaway worktree and reading the test command's own output, differentials, toolchain traps; sweep.py, which prints facts and leaves their meaning to the reader
morphic-reviewing-prs reviewing a PR or its final state the review passes, a first round that checks every class of claim in full and reviews its own fixes before pushing them, probes that found what earlier rounds missed, and the verifying and shipping rules a review applies, by path
morphic-shipping-prs committing, writing commit messages or PR text, opening or updating a PR, merging main in, splitting a change into commits commit, title, body and closing-keyword rules, what a commit may hold, recorded decisions, what a merge of main can lose, and the review's first round run by the author before the first push; the push checklist of commands, which a review also runs
morphic-changing-the-ir work under ir/ or pass/, the IR and emitter design documents, IRVersion the design steps and what an IR change drags along, version collisions between open PRs, the hand lists a new field meets, invariants a type held, what the verifier and codecs must reach
morphic-measuring-performance a speed or memory change, a cost or allocation test, a benchmark, a claim that a change is faster or cheaper claim → measure table; tests that pin a cost, and where cost hides
morphic-openapi-compiler touching compilers/openapi, its corpus or its keyed docs where the rules, library behaviours and tests live; lowering, diagnostics, grammar and load-phase rules
morphic-curating-skills a lesson worth keeping, or an edit under .claude/skills where a lesson goes, the rule format and limits; skills_lint.py, which checks both

The rules come from two sources: defects past PRs fixed after their first push, and probes that later review rounds found effective. Every rule has one format (a heading, a stable key, then Rule, Why and an optional Probe), stays within 150 words, and cites in its Why the PRs or issues whose work showed the failure, which an item's record may not describe. A lesson has one home: rules teaching the same thing from both sources are merged, and each sits in the skill whose trigger fires when it applies, so the counting and sweeping rules an author needs live in verifying-changes, and the commit and PR rules in shipping-prs, which reviews read by path. A skill links to CLAUDE.md, docs/compilers/openapi.md and docs/parser-library.md rather than restating them; parser-library behaviours stay in that page. CLAUDE.md gains two paragraphs. One says where the skills are, for agents that do not discover them on their own. The other says they are experimental: an agent treats what a skill says as a claim to check, confirming what a script prints with a probe of its own and a rule's fact about the code on main, says after using a skill how it served, and files each flaw as a skills: issue under the repository's filing rules. The tracker and label commands CLAUDE.md and docs/micro-compiler-plan.md quote now pass --limit 1000, as the skills' copies do: gh stops at a default number of results and, outside a terminal, says nothing of the rest.

Decisions to look at

  • Tracking part of .claude/. refactor: remove duplicated code and scaffolding across the repository #97 ignored .claude/ as agent scratch space, and repo: static coverage badge and internal gitignore entries #91 asked that .gitignore list only project content. Skills are reviewed project content that agents read in place, so .gitignore now ignores .claude/* and re-includes .claude/skills/; everything else there stays ignored. A clone whose own .git/info/exclude lists .claude/ has to narrow it to .claude/*, because an excluded directory is never searched for the negation: such a clone keeps the tracked skills, but a new skill file is missing from git status and refused by git add. The comment beside the rule in .gitignore says so.
  • Skills stay apart from the codebase. Nothing in the gate reads .claude/skills: archtest's walks and the keyed-doc citation checks skip dot-directories, and the scripts are not Go. skills_lint.py holds every path in a code span, test, § section and #key a skill names to the tree, and the scripts to their form; morphic-curating-skills says to run it after every edit, and how to run each skill's script tests. The push checklist chains each push on the same lint, and a review runs it on the PR's tree, so a code change that renames a test or path a skill names meets it before its push and in its review.
  • Reviews run on the tree as it will land. The review skill checks the PR out merged into main, with the commands it gives, so every probe and the comparison with main see what merging would produce, not a branch that also undoes main's newer commits. A head that already contains main merges as a no-op, and CI's run on it covers that tree; a head that conflicts is reviewed alone and the conflict reported.
  • The first review round is the review. Before that rule existed, this PR's own review took nine rounds. Rounds sampled a class of claim before a later one swept it: citations were read for 58 of the 203 rules before all 383, and the full read found a misattribution the sample had missed. A finding fixed in one file, restated library behaviour, met the same in another six rounds later. Fixes went out unreviewed, so later rounds found defects they had added: a body example the scripts listed as a closing reference, a command that failed from the repository root, and a sentence saying the lint ran on every branch. The review skill now has the first round check every member of every class of claim and review its own fixes before pushing them, sweep-every-site-of-the-mechanism now triggers on a review's finding as on a fix, and the shipping skill has the author run that round before the first push.
  • A citation names the work, and the Probe checks the rule. Many process lessons came from the work on a PR rather than from its record: feat!: model OAuth flows fully and validate every lowered object #878's record never mentions the fold that reverted fix(compilers/openapi): warn on a construct a later version added #870 while it was prepared, and pass: give transform passes a sanctioned way to replace and remove types #966's and fix(compilers/openapi): refuse a tagged mapping before it faults #475's never mention the stash a pop applied. Their records show the fix and not the slip behind it, so marking each citation by whether its record shows the clause would rest on judgments with no crisp answer, which a later comment on the item could undo. A Why's #N names the work a failure showed in instead, which still exposes a citation naming the wrong item, and a rule is checked by its Probe; the curating skill states it in the rule format, and CLAUDE.md beside the skills.
  • Every finding is fixed or justified. The review skill ranks findings issue:, nit:, suggestion: and question: to order the work, not to decide what may ship: as CLAUDE.md requires, every issue: and nit: is fixed or justified in writing before merge.
  • Breaking API is read with apidiff. Whether a change needs ! turns on the exported API it removes or changes. The breaking-change rule's probe runs the Go team's apidiff, pinned to a version, on export data written at the merge base and at the head: it type-checks both sides and applies Go's compatibility rules, so it sees interface methods, fields, grouped members and declarations moved to another package. It misses a struct tag, which TestIRVersion_PinsTheEncodedShape holds in ir. A script that parsed go doc instead took 283 lines, and review found it crashing on a valid signature layout; the command replaced it. The rule calls a method added to an exported interface breaking only when the interface has no unexported method: apidiff reports the same addition compatible on an interface sealed by an unexported method, as each sum in ir is, and incompatible on an open one, where the rule had called every such addition breaking.
  • Two scripts, where twelve were written. A script stays only where it does what an agent cannot do reliably by running a command and reading its output: hold an invariant through a crash, count or compare exactly, or check every member of a large set. It prints facts, never a verdict parsed from a tool's output. Two meet that bar: sweep.py, which builds two binaries and compiles a corpus with each in parallel, comparing their outputs byte for byte, and skills_lint.py, which holds every name the skills cite to the tree. Ten more were dropped. Seven, about 1,400 lines with 1,150 lines of tests at ee6fdd2f, gave confident wrong answers where a command or an existing tool did the job. Three, mutate.py, preflight.py and artifact_scan.py, about 650 lines with 900 lines of tests at 8beb0309, each ran a tool and parsed its output into a verdict, and that parsing was where review kept finding defects; an agent reading the output itself does better. A defect is now planted in a throwaway worktree, which makes unnecessary the restore mutate.py existed to guarantee, and the push checklist and the artifact grep are commands in the shipping skill: the checklist holds each commit subject and the title, as the squash lands it, to the form and the cap, as preflight.py did, and the body's sections, which it also checked, are a rule the shipping skill states. The rules that named the dropped scripts give the commands instead, and the curating skill holds any new script to the same bar.
  • Scripts are Python 3.14, standard library only. Each declares requires-python = ">=3.14" in a PEP 723 script block, which uv run and pipx run read and the lint requires, and every command runs it as python3.14. Each is also executable and starts with #!/usr/bin/env python3.14, which the lint requires too, so one run as ./script.py takes the same interpreter. An older python3, such as the 3.9 macOS ships with its developer tools, fails at import with exit status 1 or 2, and 1 is the status each script gives a finding or a difference, so no command leaves the interpreter to chance; a missing python3.14 exits 127. Python is not among CONTRIBUTING.md's prerequisites; CLAUDE.md names 3.14 beside the skills. A first version in bash kept hitting differences between macOS and Linux shells (sed -i, timeout, awk record separators, bash 3.2 arrays); the standard library covers the same work on both. Each script has a unittest file beside it. The Agent Skills specification's own validator, skills-ref, needs third-party packages, so the lint checks the specification's frontmatter rules itself.

Test plan

  • skills_lint.py is clean, and catches each planted defect: a missing path in backticks or in prose, a missing test, § section, #key and skill; a machine path; a memory-style link; a reference file no SKILL.md names; a script that is not Python 3.14, has no test beside it, declares no requires-python = ">=3.14" script block, has no #!/usr/bin/env python3.14 first line, or is not executable; and an import outside the standard library. Frontmatter is held to the Agent Skills specification and to what a YAML parser reads whole: a name of 1-64 lowercase letters and digits joined by single hyphens, only the specification's fields and each once, and no plain value holding an unquoted : , a trailing : or #, which a YAML parser refuses or reads as a comment that cuts the value; ten planted defects in those checks each turn the suite red, and a : planted in a real skill's description is reported at its line. A quoted name or description is read as YAML reads it, so a description quoted as the lint advises passes, where the lint had read the quotes raw and reported it as not starting "Use when"; a quoted value is matched as YAML closes it, so one ending in an escaped quote, which YAML cannot parse, is refused, and a comment may follow the closing quote. A block scalar is refused with the advice to write the value as one plain or quoted line, and the body's word limit is still checked. Ten planted defects in that reading each turn the suite red. A bad link target is reported once. A file named without its directory, such as sweep.py, must be a file the tree holds, and each script must be named in its SKILL.md; that check once found a script named in no skill. Paths inside commands are checked too, placeholders such as <spec> and {owner} aside. A POSIX bracket class is not taken for a link. A rule is cited as references/<file>.md#<key>: a citation whose file holds no such rule is reported, so is a bare span naming a rule key without its file, and renaming a key or citing the wrong file on the real tree is caught at the citing line. Each rule is held to the format: a heading with no key, a key used twice across the skills, an extra or missing bullet, a stray line, a Why citing no PR or issue, and a rule over 150 words are each reported, and planting a defect in each of those checks turns the suite red. The prose check found one rule still naming scripts/check-coverage.sh, removed in build: judge the coverage profile with golang.org/x/tools/cover #828. A code span a line break splits is reported once, outside fenced blocks, because every check reads one line at a time and never reads such a span: the check found one, a gh issue list command no sweep of the skills' commands had read. It reads a fence as CommonMark does (backticks or tildes, indented up to three spaces, opened by no backtick run whose info string holds a backtick, and closed only by a bare run of the same character at least as long), and three planted defects in the check and six in its reading of fences each turn the suite red. A § section citation holds spaces a wrap can fall on, so its check reads on into the next line and reports a citation once, at the line it starts on; none is split today. Two planted defects in it each turn the suite red, and a third, continue for its break, survives because it changes nothing. A path in a code span or a command is held to the tree whatever its first segment: with barrier/ renamed to guard/ the rule citing barrier/text.go read clean, and so did a misspelt compiler/openapi/..., and both are now reported. What a span holds that names no path is told apart by its form (a pointer, flag, pathspec magic, pattern, URL or placeholder) or by a named first segment (a remote's branch, a Go package, a diagnostic code), and seventeen planted defects in that each turn the suite red. A § that follows no document's name, as the second of "ir-design §15 ... and §16" did, is reported, an RFC counting as a document; three planted defects in it each turn the suite red. A description over 500 characters, the limit the curating skill now states with its reason, is reported at 501 and not at 500; the 1,024-character frontmatter cap, which no source gave and no test reached, is gone. The lint takes no argument: --help prints its usage and any argument is refused with exit 2, where --help had run the whole lint and an option it lacks, such as --strict, had exited 0 as a clean lint; two planted defects in that refusal each turn its suite red. A path, link target, skill or command name and cited reference is held to the files git lists, untracked ones included, and to the directories above them, where the lint had read the disk: with an ignored file on disk under docs/superpowers/, a code span and a link naming it had read clean, and both are now reported as ignored by git, and a link out of the repository as missing. Ten planted defects in that holding, a fallback to the disk at each of its six sites among them, each turn its suite red. A span under .git/ names git's own files, which every clone has and a worktree keeps elsewhere, and is left out of that holding: the lint had called .git/info/exclude ignored by git in a clone and missing in a worktree, and dropping the exemption turns its suite red. Git's file list is read whole, with ls-files -z: git quotes and escapes a name holding a non-ASCII letter, so a tracked file named that way had been missing from the tree, and dropping -z turns its suite red. The lint holds a path in a span up to its # entry, :line or glob, and the real skills rely on the entry and the glob, without which it reports 11 and 2 false findings; no test held the three cuts, nor the skip of a tracked test file deleted from disk, without which the lint exits 2 in the middle of a change that removes one. The clean fixture now names each form and the tree test deletes a tracked test file, and a planted defect in each of the four turns the suite red.
  • sweep.py over testdata (213 specs, compiled in parallel): with base equal to head, nothing differs, it exits 0, and both sides' exit-code distributions and IR byte totals print. With IRVersion planted at 9.9.9 in a throwaway worktree, 210 of them differ and it exits 1; the other 3, refused alike on both sides, agree, and results.tsv shows their exit codes. A document written as JSON, even one saved as .yaml or .yml, is judged by its top-level keys, and any other by a version key ending at its colon: the sweep had taken the 56 conformance goldens for specs, since their coordinate keys such as "openapi:schema" read as version keys, and counted them among 269, and it missed a minified spec in a corpus of about two thousand published ones. Over that corpus it now gains that spec, drops eight files that are not specs, and keeps a JSON spec saved as .yaml. A spec, YAML or JSON, saved with a byte-order mark is found too, as the compiler reads past the mark; six planted defects in the detector each turn its suite red. A -t or -j under 1 is a usage error, and five planted defects in that logic each turn its suite red. The sweep judges nothing: under an unknown compile flag every spec exits 2 on both sides, and it reports that no spec differs and exits 0, while the distributions it prints, 2 for all 213 and no IR bytes, show that neither build compiled anything; the differentials reference gives morphic's exit codes as compile --help states them, where it had said a spec with findings exits 1, though one with only warnings exits 0, and says that agreement on 2, 137 or a negative code says nothing. A spec file given as the corpus is read as one document, where it had been searched as a directory and drawn "no OpenAPI or Swagger documents", and dropping that read turns its suite red; run from ir/ on one conformance spec at -b HEAD, the sweep reads no difference. Its hint for a spec that differs names the IR pair beside the diagnostics, where it had named the diagnostics alone, which show nothing for a difference in the IR only; reverting the hint turns its suite red. Its report is now pinned whole: four planted defects, printing no IR bytes, reading both totals from one side, dropping the list of differing specs and writing absolute paths, had passed its suite, whose test of results.tsv keyed on spec names, and each now turns it red, the test resolving its root as the sweep resolves each spec. --help prints the docstring, whose usage line runs from the repository root and which says what each option does and which files are read, where it had listed the option letters alone. The base worktree is removed afterwards.
  • The revert probe in tests-that-bite.md, run on fix(cmd/morphic)!: report a stop only for a run it stopped #1136's fix, reverts its code alone in a detached worktree, keeping its tests and fixtures, and six cmd/morphic tests go red.
  • The rules' statements about how the code behaves were probed on main rather than read: where harness.Check stops, how the canary classifies a child's crash and quiets the collector, how the TypeDef kind lists find members, what OnVerify wraps, how costAround averages, what the shape pin prints, and that no conformance spec holds an alias all hold. One did not: a stale entry is refused in idPositions and stringLists and passes only in integerFields, which the IR skill now says, where it had said no table notices one. Each Why changed since the citation reading was checked against its item's work: one credited feat(ir)!: flatten composition with defined order and override #1141 with a full disk emptying a file, where feat(ir)!: flatten composition with defined order and override #1141's full disk left a mutant planted and the emptied file was a probe on chore(skills): add the repository's agent skills #1143, and one cited chore(skills): add the repository's agent skills #1143 without saying what its work showed; both now say it. Two more named no failure at all, for feat!: own I/O in the engine and lower each file of a split spec #1009 and feat(compilers/openapi)!: resolve schema $refs in the compiler #1016, and now say what each showed. irverify: a stale integerFields entry for a deleted field passes #1148 tracks making integerFields refuse a stale entry too, and the rule cites it until that lands.
  • Each quoted probe runs from the repository root as written. The go test and go build templates that named no package failed there with "no Go files", and now name ./<pkg>; the version-claim loop reads every open PR rather than gh's default 30; and the apidiff comparison is the pinned go run command, which reports feat(ir)!: type the inferred-marker list and verify its form #1142's changes to ir, where it had named an apidiff binary no setup installs. recorded-base's probe reads from the merge base: in a scratch repository, a two-dot diff against origin/main listed main's newer file after a correct fold, while the three-dot diff lists the branch's file alone there and the reverted file after a fold onto a moved main. count-work-not-wall-clock leaves its assertion to marginal-growth-on-one-path: it had prescribed work(2n) <= 2*work(n) + c, while the test it cites asserts a ratio of totals and the next rule says such a ratio lets a quadratic term hide behind fixed cost. perf-guards-measure-the-defect had a cost test time the function under test, and nondeterministic-tests-proven-stable said how to build a timing ratio, where the measuring skill pins a cost by counting work and no test on main times a ratio; a benchmark now times and a cost test counts, and the timing sentence points at the counting rule. The tracker searches the skills quote pass --limit 1000: outside a terminal gh lists 30 with no sign of the cut, while a search for Unmodeled matches 131 issues.
  • Every rule and skill body was reread against what a sentence must do: be checkable, live in one home, and be specific. Five rules whose Rule is no check gained a probe, three of them run on main (both planted defects in barrier's cut fail a test, a file referenced as ./a.yaml and ./x/../a.yaml is one source, and load refuses an alias cycle as openapi/cyclic-ref). acyclic-trees now cites the parser-library entry it rests on, environment-independent-tests covers every platform branch, two hedges became statements, and a new rule, fix-at-the-invariants-owner, records openapi: a $ref back into a schema still being lowered crashes the compiler #749's lesson. verifying-changes and the OpenAPI skill now open with the bar their work is held to. No rule puts a count beside the list it counts, as derive-never-maintain asks: four did ("the other two tables", "all three sizes", "all four documents", "two suffixes"), and a scan of every number in the skills' prose outside the Why lines found no other.
  • The push checklist's artifact grep finds thirteen of the fourteen lines the dropped scan's tests planted, all but a generated-by line naming no assistant, flags none of the eight they kept clean, such as .claude/skills, CLAUDE.md, a brief description and a golden generated with -update, does not match its own text, and finds nothing in this PR's commit messages, added lines, title or body. Run as the shipping skill gives it under bash and zsh on this branch, the checklist prints only the lint's clean line. Its subject check reads the title too, with the number the squash appends, where the checklist had measured the title's length alone: a prose title, one of 65 characters before the number and one ending in a period each print, and one of 64 does not. It passes --no-merges, since a first version flagged the branch's merges of main, whose subjects no form fits.
  • The commands that replaced the review, version-claim and API scripts were run as the skills give them: the review setup on this PR (the second fetch leaves the PR's head in FETCH_HEAD, and the merge is a no-op), the gh pr diff loop for version claims (it lists feat(ir)!: scope servers to operations by ID, home wire facts #1144 among the five open PRs, and names a PR whose diff did not load, tried with a number no PR has), and the apidiff commands on fix(compilers/compile)!: give a shared primitive no source #551's commit (NewTypes: changed from func(int) *Types to func() *Types), on fix: close the losslessness gaps and the guards that missed them #185's, which moved CanonicalWords into ir (CanonicalWords: removed), and on refactor(compilers/openapi): extract the lowering context #218's, which retyped the grouped constants GroupByPathPrefix and GroupByTags in compilers/openapi onto an internal type, each reported. After the cut the lint is clean and each kept script's suite passes.
  • A script that crashes exits 2, which neither script uses for a finding or a difference: a test runs sweep.py with git missing and the lint with an unexpected error, and dropping either guard turns its suite red.
  • The scripts' unit tests pass under Python 3.14, none skipped, and planted defects (the bare-name and script-mention checks and load_tree in skills_lint.py) each turn a suite red, as do four planted in the lint's 3.14 checks (the script block, the standard library, the rule bounds and the parse target) and four in its first-line and mode checks (either check removed, the first line matched anywhere in the file, and the shebang naming python3); a script made not executable on the real tree is reported. Both scripts were run on the final tree from ir/, both as python3.14 <script> and through their own first line; the runs above that predate the 3.14 requirement were made under 3.9.
  • Each suite was run under the standard library's trace, and every decision path it left unexecuted gained a test: in skills_lint.py, a skill with no SKILL.md, a frontmatter that never opens or never closes, the skips for URL links and __pycache__, the wiring that hands the real tree to the checks, a tree declaring no test, each exit status and an unexpected error; in sweep.py, an exit-code difference, flags after -- reaching each compile, the warning for a base the branch lacks, the run that builds both sides and removes the base worktree, and each exit 2. Re-planted on the final tree in a throwaway worktree, with each suite's own output read, thirty-five planted defects, twenty-four in skills_lint.py and eleven in sweep.py, each turn a suite red, and a comment-only control in each passes. That control first failed in the sweep's suite: a test expected spec paths relative to its temporary directory, which under macOS's default /tmp, a symlink, they are not, and it now resolves that directory as the sweep resolves each spec. Fifteen more planted on the final tree each turn a suite red, seven in skills_lint.py and eight in sweep.py, among them the eight that the first two bullets name as having passed; two survive that are equivalent: a relative import, which fails in any script run as __main__, and -j, which changes no output. What trace still lists as unexecuted runs only in the suites' subprocesses.
  • The gh commands CLAUDE.md quotes were run with and without a limit. gh label list read 30 of the 34 labels and left out priority:P1 to priority:P4, which every issue carries; gh issue list --state open --json title read 30 of 250 open issues; and the decision search, which read open issues only, found 2 of the 16 issues for ir.Naming, without compilers/openapi: the webhooks group is named as if the source declared it #184, the closed issue its own example cites. With --limit 1000, and --state all for the decision search, each reads every result, compilers/openapi: the webhooks group is named as if the source declared it #184 included.
  • make gate passed on the first commit; no step reads the new files. The branch's later changes touch only .claude/skills, CLAUDE.md, the status line of docs/micro-compiler-plan.md and the comment on the .claude rule in .gitignore; of those, internal/archtest reads the two documents, and internal/keydoc and TestLoweringSpec the citations in them, and each passes on the final head. main is merged in, and the lint and the artifact grep are clean on the merged tree.

Reviews and fixes in this repository spend much of their time
rediscovering how work here is verified: which probe answers a claim,
which controls a differential needs, which reader of a construct a
change missed. The lessons exist only in reviewers' heads and in the
history of PRs that fixed defects after their first push.

Add four skills under .claude/skills, which coding agents read in place:

- morphic-verifying-changes: a table from claim to probe, rules for
  planting defects, differentials and misleading toolchain behaviour,
  and scripts that plant one defect and restore it (mutate.sh), compare
  two builds of cmd/morphic over a corpus (sweep.sh), and list the
  packages a change can affect (affected-packages.sh).
- morphic-reviewing-prs: the review passes, the probes that found what
  earlier rounds missed, and scripts that set a PR up in its own
  worktree (review-setup.sh) and scan what it ships for attribution,
  session words and machine paths (artifact-scan.sh).
- morphic-openapi-compiler: where the compiler's rules, library
  behaviours and tests live, and its recurring traps.
- morphic-curating-skills: where a new lesson goes, the limits a skill
  keeps, and skills-lint.sh, which holds every path, test, section and
  keyed-doc entry a skill names to the tree.

Each rule keeps the PR or commit that showed it. Skills link to the
normative documents rather than restating them, name nothing specific
to one machine, and their scripts run under bash 3.2 and on Linux.

.gitignore keeps the rest of .claude/ ignored as agent scratch space;
only skills/ is tracked.
A final pass over the skills against the review procedure and the
lessons they were drawn from found gaps:

- The review skill had no pass for the CLAUDE.md invariants and the Go
  style rules no linter enforces, and did not say that a regression is
  never a limitation, that an oddity is checked for intent before it is
  called a defect, or that a review posts nothing unless asked.
- Nine lessons were missing: fuzzing a rewritten reader against the old
  one, query differentials, the failure form of re-attributed records,
  re-rooting generated sweeps, a test two mechanisms keep green, library
  mutants through -modfile, table entries before tidying fixtures,
  instrumenting with -v, validating a source reader against history, and
  probing the external-document reader by counting fetches.
- One rule still named scripts/check-coverage.sh, removed in #828.
  skills-lint.sh checked only paths in backticks; it now checks paths
  written in prose too, which is how it was found.
- review-setup.sh assumed the PR lives on origin; REMOTE overrides it
  for a clone of a fork.

CLAUDE.md now says where the skills are, for agents that do not
discover them on their own.
The six skill scripts were bash, with perl for in-place edits and time
limits, and the differences between macOS and Linux shells kept turning
into bugs: awk record separators, pipefail ending a script on an
expected non-zero status, bash 3.2's handling of empty arrays. Python's
standard library covers what the scripts do on both systems, and keeps
the skills' tooling apart from the Go codebase and its gate.

Each script is now Python 3.9, standard library only, with a unittest
file beside it. skills_lint.py holds the scripts to that form: each
parses as Python 3.9, has its test beside it, and, where the running
interpreter lists its standard library, imports nothing outside it. It
also reports a bad link target once rather than twice. sweep.py now
compiles specs in parallel.
- skills_lint.py checked paths only in single-word spans, so a path
  inside a command (python3 scripts/x.py ...) went unchecked. It now
  checks each path-like word of a command, leaving placeholders such as
  <spec> and {owner} aside and reading ./x/... as the package x. That
  found a rule naming a ./cmd/foo that does not exist.
- affected_packages.py compares real paths, so git and go spelling the
  checkout through different links cannot empty the list, and lists
  everything when a changed Go file maps to no package rather than too
  little.
- sweep_test's timeout case timed both stand-ins under a one-second
  limit, so a slow interpreter start could kill the healthy one; it now
  times only the hanging one.
- sweep.py documents -j, and mutate.py says its verdict is read from
  go test output.
- Every script and test now wraps at 100 columns.
- sweep.py clears a base worktree a crashed run left in its output
  directory, which made a rerun into the same directory fail.
- affected_packages.py says on stderr when no Go package reads the
  changed files, rather than printing nothing for go test to read as
  the current directory.
- A rule's probe named a function that never existed; it now names the
  check it meant, byte identity.
- The scripts are executable, matching their shebang lines.
Each script gives exit code 1 a meaning (SURVIVED, specs differ, hits
found, findings), and an uncaught Python exception also exits 1. A test
command that cannot start made mutate.py report SURVIVED, and a missing
go made sweep.py report differences and affected_packages.py print
nothing for go test to read as the current directory. Every script's
entry point now prints the traceback and exits 2, which never means a
verdict; a test per affected script runs it with the tool missing.

review_setup.py reads the PR before creating its worktree, so a failing
gh leaves none behind.

A rule told readers to rescan after every rebase of a change; this
repository merges main into a branch instead, and the rule now says so.
mutate.py judged one mutant per call and never ran the test command on
the clean tree, so a suite that already failed read every mutant as
killed, and a set of mutants cost one invocation each.

--batch reads a JSON-lines file of mutants and refuses the whole batch if
any entry is malformed, missing, ambiguous or a no-op, so nothing runs on
a typo. It then requires the command to pass on the clean tree, plants
each mutant in turn through the existing restore-always path, and prints
one line per mutant (the tests that killed it, or why there is no
verdict) and a tally. Exit 0 only when every mutant was killed.

The tests now capture what main prints, so a run leaves the console
clean instead of printing verdict text that read like a failure.
The references converted from the review catalog and those written from
past lessons followed two formats. The converted rules carried truncated
"Seen in" quotes and "Also" bullets, many ran past the 150-word cap the
curating skill sets, and several restated a lesson another file already
held. Nothing checked the format, though the curating skill ranks a check
above prose.

- Every rule is a heading, a key, and Rule, Why and an optional Probe.
  Each Why cites the PRs or issues that showed the failure; the quotes
  are gone, their PR numbers kept.
- Rules teaching the same lesson are merged into one, with the evidence
  of each: untaken guards, bounds lowered by one, instrument checks,
  mutation claims in PR bodies, scheduling flakes, the merged-tree
  review, writers of a mechanism, doc inventories, library-mirror caps
  and one report per malformed construct.
- Rules move to the skill whose trigger covers the moment they apply.
  Counting and sweeping rules move from reviewing-prs to
  verifying-changes, which fires on counts and every/never claims and
  which reviews load too. Rules about verifiers, lint and archtest rules,
  codecs and loaders form checkers.md there, out of the OpenAPI skill
  whose trigger never fires on them. OpenAPI-specific sweep rules move
  the other way. spec-and-grammar.md is split between the two.
- skills_lint.py holds each rule to the format: a key unique across the
  skills, the bullets in order, a citation in Why, the word cap.
- verifying-changes now also fires on writing a verifier or a lint or
  archtest rule, and on fixing a defect.
The review skill's cost probe, a regression sweep and an issue's
priority all measure against the golden spec (CLAUDE.md, "Priority"),
and each fetched it by hand. golden_spec.py fetches it once per commit
of cloudflare/api-schemas into a cache outside the repository, resolves
HEAD to the commit measured, refuses to cache a response that is not
JSON, and prints the file's path or how many objects carry each key.
Its counts agree with jq's on the spec at 1a7b2e42.
A skill cites a rule by its key in a code span, but nothing checked that
the key exists, so renaming or merging a rule would leave its citations
pointing nowhere. The lint now reads a code span of lowercase words
joined by hyphens as a rule key, other than a skill name, and reports
one that no reference defines.

The curating skill states the rule format as the references use it: a
heading is the rule in one line, not necessarily an imperative; a Probe
is left out when the Rule is itself one; a Why may carry the counts its
citation pins. It also closes two gaps in where a lesson goes: a check
that is filed rather than written keeps a rule naming its issue until it
lands, and a lesson no skill's trigger reaches widens a description or
adds a skill rather than sitting where nothing loads it. Paths are
relative to the repository root bar those inside a skill's own folder,
and commands run from the root. The caps read "within", as the lint
measures them.
review_setup.py checked out the PR head as pushed, so a review of a
branch behind main compared main with a tree that also undid main's
newer commits, and the merged state was left to a last pass. It now
checks out the head merged into main when they merge cleanly, the head
itself when it already contains main, and the head alone, reported, when
they conflict. The merge commit is made under a fixed identity, so a
clone with no configured user can make it. The skill's passes follow:
the gate is run in that worktree unless CI already ran the same tree,
behaviour is compared with origin/main, and new commits on either side
mean rerunning the setup.

Findings ranked nit or suggestion read as optional, against the
repository's bar that every known defect is fixed or justified in
writing before merge. The rank now orders the work only; every issue
and nit is fixed or justified.

process.md measured the title against a three-digit PR number, one
character short for four-digit ones; review_setup.py measures it with
the PR's own number, and the rule points there. The skill pointed to
process.md for closing keywords, which held no rule on them; it now
does, from #948 and #1016.
The rule that a new bound in the load scan warns and lets the compile go
on described a convention main no longer has: the reference-chain bounds
that warned are gone, and nesting past the scan's bound is refused. A
new bound now takes its outcome from the rules entry for its kind, and
joins that entry.

The conformance-case rule names the package of the -update run it asks
for, defers to CONTRIBUTING.md's recipe rather than restating part of
it, and names the tests a careless case fails. The skill's traps cite
the rule that holds each.
mutation.md had become the home of rules that are not about planting
defects: subtests, fixture order, cut alignment, crash oracles, -v
instrumentation and typed testify calls. An agent writing a subtest or a
differential would never open it. Each moves to the reference about its
subject, and the two that repeated a rule already there are merged into
it. A cancellation claim moves from the toolchain traps to the claims.

A Probe that only read code now runs it, a Probe that restated the
script is dropped, a coverage command gains the -race the Makefile
uses, and the order-oracle rule names orderInvariant. The skill's guard
probe forces each side of a guard rather than one, and its description
fires before a change is called done.
The paragraph pointing at .claude/skills said it held procedures for
working in "a compiler", where one skill covers the OpenAPI compiler
alone.
The lint read every hyphenated code span as a rule key. In this
repository's skills that is too wide: conformance case names, rules-entry
keys, validation classes and x- extensions are hyphenated too, and the
OpenAPI skill is where they will be written. A bare key also leaves the
reader to search for the file that holds the rule.

A rule is now cited as references/<file>.md#<key>, from another skill
with its path from the repository root, and the lint holds each citation
to a rule in that file, so a rule renamed or moved to another file leaves
no citation pointing nowhere. A bare span naming an existing key is
reported with a request for its file. A code span holding a placeholder
such as <file> names no path.
Four probes measured something other than what their rules claimed:

- The two-order rule said to compare diagnostics by their message, "as
  internal/harness does". The harness leaves the message out on
  purpose, since some messages list keywords in source order; comparing
  it reports order dependence that is not there. The rule now keys the
  multiset as diagnosticSet does.
- The test-move rule diffed `go test -list . -v` for subtest names, but
  -list names top-level tests alone, so a dropped table row never shows.
  It now diffs a verbose run's PASS lines.
- The golden rule reverted a fix with git stash; it now reverts through
  mutate.py, which restores whatever happens, as the restore rule asks.
- The OpenAPI skill compiled without --verify, which checks each stage's
  IR against its invariants and is meant for developing a stage.

The lexical file-reference rule now cites the references entry it
applies and keeps only what an agent does with it, and a Why that stated
a success states the failure the probe prevents.
The restore rule sends instrumentation and measurement switches through
mutate.py, but mutate.py printed only its verdict, so a planted println
or a timing printed nothing anyone could read, and an UNVERIFIED verdict
showed only the first lines of why. Each run's whole output is now kept
in a file whose path is printed; a batch keeps the baseline's and one
file per mutant, numbered in batch order. The rule says the same, and
that a scratch file added for a probe is deleted before a commit.
internal/keydoc reads a citation of a keyed document's entry on every
Markdown line and in every Go or YAML comment, and the OpenAPI e2e and
canary tests hold each one to its document. A change adding such a
citation outside the packages those tests import was missed: a comment
citing docs/compilers/openapi.md#no-such-rule in cmd/morphic-covergate
drew only that package and internal/archtest, while the e2e test fails
on it. When the change adds a citation, the script now lists the
packages whose tests call keydoc.Citations or keydoc.Walk, found by
grep rather than kept by hand. It also says why `go test` with no
package fails when nothing reads the change.
CLAUDE.md asks a PR whose title marks a break with ! for a Breaking
section in its body. review_setup.py checked for Summary and Test plan
alone; it now reports a missing Breaking section under a ! title, and a
Breaking section under a title that marks no break.

The review skill fires for a branch or a commit range, but its setup
takes only a PR number; it now says to build the same worktree by hand
for those. The body was tightened to stay within its cap.
Every change ends in commits, a PR body and usually a merge of main,
and the lessons for that step lived in two wrong places: the review
skill's process.md, which nothing loads while an author writes a commit,
and notes outside the repository.

morphic-shipping-prs loads when committing, writing a commit message or
PR text, opening or updating a PR, merging main in, or splitting a
change into commits. Its commits-and-prs.md takes process.md's author
rules (message form, closing keywords, scope, recorded decisions,
stated limitations, the coverage gate) and the toolchain rule about
what a commit may hold, and adds rules for gating a commit on exit
status, marking an exported API change as breaking, testing each commit
of a split, editing a posted body from its live copy, and re-reading
prose after a redesign. merging-main.md holds what a merge of main can
lose. process.md's review rule moves to review-probes.md, and the
review skill reads the commit rules from their new home.

preflight.py runs before a push: each subject, the title measured with
the number GitHub appends, the body's sections against the title's !,
the exported functions, methods and types the branch removes or
changes, untracked files, the merge with main, and the artifact scan.
It reuses the review scripts' checks, so the lint now accepts an import
of another skill's script. On #551's commit it lists the changed
compile.NewTypes, and on a pure refactor it lists nothing.
An IR change meets things ir-design does not spell out: another open PR
claiming the same IRVersion, which merges silently on the constant;
classification tests that a new field, ID class or sum kind must pass,
and the one gap among them, a stale entry that passes; guarantees a map
held that a list does not; and predicates that name a struct's fields
one by one.

morphic-changing-the-ir loads for work under ir/ or pass/, the IR and
emitter design documents, and IRVersion. It links ir-design §2.1 and
architecture §2.5 for the bump and landing rules rather than restating
them, and names the bump no test sees: a change of meaning with no
change of shape. The four checker rules about the IR's own verifier and
codecs move here from verifying-changes.

irversion_peers.py lists the open PRs that claim an IRVersion past
main's and reports a collision or a claim that trails main; with the
working tree set to 0.22.0 it reports #1144's claim of the same.
Recent work is cost-heavy, and its lessons were specific to this
repository: count work rather than time it, assert marginal growth,
count allocations in the instrumented build the gate runs, give an
optimization a counting seam, and quote instructions retired rather
than wall time on a shared machine.

morphic-measuring-performance loads for a speed or memory change, a
cost, allocation or scaling test, a benchmark, or a claim that a change
is faster, cheaper, linear or allocation-free. Its two references hold
the tests that pin a cost and the places cost hides; the two cost rules
in verifying-changes move into them. Each cited test, helper and seam
was checked against main, and the rules name today's ones.

compare_cost.py builds a command at a base revision and at the working
tree, runs them alternately and prints the median, range and ratio of
instructions retired, CPU time, peak memory and wall time; on a spec
with no change between the builds every ratio came out within 4% of 1.
The review's price-the-common-path probe uses it, and review_setup.py
names the IR and performance skills for the PRs they apply to.
The curating skill listed the skills by subject and CLAUDE.md named
three of them; both drift each time a skill is added. Each skill's
description already says when it loads, so the curating skill gives the
command that prints them, and CLAUDE.md names two procedures as
examples.
@OmarAlJarrah OmarAlJarrah changed the title chore(skills): add review, verification and OpenAPI skills chore(skills): add the repository's agent skills Oct 27, 2026
A review of the shipping skill found rules that said more, or other,
than they meant:

- It said never to rebase the branch; CLAUDE.md forbids rebasing a
  pushed one. It now says so, and cites the merge rule rather than
  repeating its steps.
- The body-edit rule applied a body with `gh api --input <file>`, which
  sends the file as the whole request: a Markdown file fails. It now
  uses `-F body=@<file>`, which reads one field from the file, as gh's
  own help gives it.
- The coverage rule's "every new branch ships with its test" meant a
  code path but read as a git branch here, and it required make coverage
  where CI's run of the same tree serves as well.
- The merge rule asked for a merge of main before any push following a
  change on main, beyond what CLAUDE.md asks; it now asks for one before
  a review round and before calling the branch ready. The rule pairing
  a normative-text check with reusing what main landed is two rules.

preflight.py ignored a body given without a title. It now judges the
body's Summary and Test plan, leaving the Breaking section to the title.
The hand-list note pointed at "what none does" for the one gap no
classification test closes; it now names it, a stale entry left for a
deleted field. The breaking note said a shape change breaks every
consumer, where ir-design §2.1 says every bump does, a change of meaning
included.

irversion_peers.py listed a PR whose head declares no IRVersion as
claiming "(none)"; such a head claims nothing and is left out.
compare_cost.py and sweep.py compare a build of origin/main with the
working tree, and on a branch that does not hold main the difference
also counts what main gained since: the mistake that read a speedup on
main as a regression on a branch (#1131). Both now warn when HEAD does
not hold the base, compare_cost.py through sweep.py's function rather
than a copy, and the performance skill holds the lesson as
check-the-base-build, taking it from the shipping skill's merge rule.

compare_cost.py also left its two builds, about 55 MB, in the temporary
directory after every run; it now removes them. The cost-test rule for
renders says to measure one visitor per call, since on main a visitor's
pruning is every visitor's, and the performance table cites both rules
a linear claim rests on.
sweep.py and compare_cost.py each resolved a base commit, warned when
HEAD did not hold it, built a command at the working tree, built it
again in a detached worktree of the base, and removed the worktree:
the same steps written twice, each copy missing what the other did.
sweep.py cleared a worktree a crashed run left behind; compare_cost.py
captured the build's output, so a failed build said why.

sweep.py now holds resolve_base and build_pair, which do both, and
compare_cost.py imports them as it already imported the warning. A
missing package now reports go build's own error from either script.
compare_cost.py's table also computes each side's median once rather
than twice.
preflight.py defined a run() helper identical to the one in
artifact_scan.py, which it already imports; it now uses that one. The
performance skill's table restated the review probe for pricing the
common path, input for input; it now cites the probe.
skills_lint.py read no argument at all, so --help ran the whole lint and
an option it does not have, such as --strict, exited 0 as a clean lint
that had never applied it. It now parses its arguments: --help prints
the usage, and any argument is refused with exit 2 before anything is
linted, as the other scripts refuse theirs.
After listing the specs that differ, sweep.py suggested diffing their
diagnostics files, which show nothing for a spec whose IR alone differs,
so following it showed a real difference as none. It now names the IR
pair and the diagnostics pair beside it.

Its spec detector reads a version key first on a line, indented or not,
which is how it finds a JSON spec a parser refuses; the docstrings said
a key starting a line.
gh issue list and gh label list stop at a default number of results, and
outside a terminal they say nothing of what they left out. The commands
CLAUDE.md quotes for searching the tracker, reading the area prefixes in
use and reading the label vocabulary took that default: the label list
read 30 of the 34 labels and left out priority:P1 to priority:P4, which
every issue must carry, and the prefixes came from 30 of 250 open
issues. The search for a recorded decision also read open issues only,
so for ir.Naming it found 2 of 16, without #184, the closed issue its
own example cites.

Each now passes --limit 1000, the decision search also --state all, and
the tracker-search bullet says why. docs/micro-compiler-plan.md's status
line, which calls gh issue list --state open the truth about what
remains, gets the same limit.
A rule's Why cited the PR or issue that showed its failure, and the
skills asked that each item show what its clause says. Many process
lessons came from the work on a PR rather than from its record: #878's
record never mentions the fold that reverted #870 while it was prepared,
nor #966's or #475's the stash a pop applied, nor #591's the backup
restored from the wrong copy. Their records show the fix but not the
slip behind it, so whether a record shows a clause has no crisp answer
for most of them, and marking each would rest on judgments a later
comment could undo.

A citation now names the work a failure showed in, which its record may
not describe, and a rule is checked by its Probe. The curating skill
states it in the rule format and its upkeep step asks that each item be
the work its clause describes, which still catches a citation naming the
wrong item. CLAUDE.md says it beside the skills, where every reader of a
rule meets it.
derive-never-maintain says never to put a count beside the list it
counts, since the count goes stale when the list changes, and four rules
did: classify-ir-fields said "the other two tables" after naming them,
marginal-growth-on-one-path said "all three sizes" after n, 2n and 4n,
test-co-declared-keywords-together said "all four documents" after
listing the compiles, and transform-differential said "two suffixes"
before naming them. Each now names the members or reads "each" and
"the". A scan of every number in the skills' prose outside the Why
lines, where a count is evidence its citation pins, found no other: the
rest are limits, parameters, sections, steps, versions and pairs such as
before and after.
mutate.py held the original only in memory and wrote it back with one
truncating write. When the disk filled during the test run, which
writes to the go build cache, that write failed partway and left the
file empty: on a 2 MB disk image the test command filled, a 220 KB file
was left at 0 bytes and the script ended in a traceback, so an
uncommitted edit in it was gone. Its own rule cites a full disk that
stopped a restore mid-batch.

The original is now copied beside the kept output before anything is
planted, and nothing is planted when that copy cannot be written. The
restore runs with SIGINT, SIGTERM and SIGHUP ignored, so a second
interrupt cannot cut it short, and drops the copy once the file reads
back as the original. When it does not, the run ends with exit 2 and
names the copy, and a batch stops there rather than judge later
mutants on a tree that still holds one.

go heads a build failure with "# <package>", and one in a test file
with "# <package> [<package>.test]". mutate.py took any line starting
"# " for such a header, so a failing test whose code printed a heading
or a YAML comment read as a package that did not build rather than as
a kill. It now matches the header forms alone. It still reads them,
since a pipe can cut the "[build failed]" line that follows.
skills_lint.py checked a path, link or skill that a skill names against
the disk, so a file git ignores passed: with an ignored file on disk
under docs/superpowers/, a code span and a link naming it read clean,
though no clone holds it. Bare file names were already held to git's
listing, untracked files included and ignored ones not, so the two
checks disagreed.

Every path, link target, skill or command name and cited reference is
now held to the files git lists, untracked ones included, and to the
directories above them. A path on disk that git ignores is reported as
such, and one outside the repository as missing.
The breaking-change rule called any method added to an exported
interface breaking. Its own probe disagrees: the pinned apidiff reports
a method added to an interface that also declares an unexported method
as a compatible change, since no type outside the package can declare
that method, and reports the same addition to an open interface as
incompatible. Each sealed sum in ir is such an interface, so the rule
asked for a ! that no caller or implementation outside the repository
needed.

The rule and the shipping skill now say an exported interface with no
unexported method. The probe's sentence restating that an incompatible
change needs ! is dropped, keeping the rule within its word limit.
skills_lint.py held a code span naming .git/info/exclude to the tree.
Once the lint held paths to git's listing, it called that file ignored
by git and held by no clone, though every clone has it; in a worktree,
where .git is a file, it called the file missing. Skills can name it:
a clone whose info/exclude lists .claude/ hides new skill files. A
first segment of .git now marks git's own files, which no listing holds
and a worktree keeps elsewhere, as origin marks a remote's branch.
The test of the restore's signal handling read the handlers during the
write, though its name says no signal can cut the restore short. It now
sends SIGINT, SIGTERM and SIGHUP to the process as the restore starts
writing, and checks that the file comes back and that the handler is
put back. Leaving all three live, SIGINT alone or SIGHUP alone, or not
putting the handlers back, each fails it.
skills_lint.py read git ls-files line by line. Git quotes and escapes a
name holding a non-ASCII letter, so a tracked file named that way was
missing from the tree the lint holds names to, its tests went unseen
and a skill naming it read as citing a file git ignores. The lint now
reads the list with -z.

The suite now reaches the paths no test ran under trace: a skill with
no SKILL.md, a frontmatter that never opens or never closes, the skips
for URL links and __pycache__, the wiring that hands the real tree to
the checks, a tree declaring no test, each exit status of main and an
unexpected error, which exits 2 rather than as a finding. A defect
planted at each turns the suite red.
Run under trace, the scripts' suites left decision paths that no test
executed, so no test could notice a defect there; seven planted first
passed every suite. sweep.py could stop reporting exit-code differences
or passing compile flags to the build, print no warning for a base the
branch lacks, keep its base worktree, and exit 0 when it could not
compare. preflight.py could stop marking a conflicting merge or an
artifact as a fix, mark a held or cleanly merging base as one, scan no
commit message, measure a draft title without the next number, or exit
0 or 1 wrongly. artifact_scan.py's exit statuses had no test, nor had
mutate.py's Go build check, usage errors, interrupts, a unittest run
without OK, the reason it gives a mutant with no verdict, or an
unexpected error in three of the five scripts. Each has a test now,
and a defect planted at each turns its suite red.
sweep.py decided what a run meant: a spec that exited 2, hit the time
limit or was killed on both sides was listed as not compared, and the
sweep then exited 2 even when every compiled spec agreed. That reading
belongs to whoever reads the sweep, alongside morphic's own exit codes.

The sweep now prints each side's exit codes and IR bytes, each spec
whose exit code, IR or diagnostics differ, and a row per spec in
results.tsv: its number, both exit codes, what differs and its path.
It exits 0 when no spec differs, 1 when one does and 2 when it cannot
run. The differentials reference now says what morphic's codes mean,
and that a spec agreeing on 2, 137 or a negative code was compiled by
neither build.
mutate.py, preflight.py and artifact_scan.py each ran a tool and parsed
its output into a verdict, and that parsing was where review kept
finding defects: a test's printed heading read as a build failure, a
pipe hid a failing test, a title was judged by the words of its own
report. Reading that output is what the agent does well, so the skills
now give the commands and say how to read what they print.

A defect is planted in a throwaway worktree made with git stash create,
which holds uncommitted work and leaves the stash list alone, so no
restore can fail in the tree being worked in, and mutation.md says what
counts as a kill. The shipping skill's push checklist gives the subject,
title, status, conflict and artifact checks as commands, with the push
chained on the skills lint, and a review runs it on the PR's tree. The
curating skill keeps a script only to hold an invariant through a
crash, count or compare exactly, or check every member of a large set,
and has it print facts.
The test of results.tsv expected each spec's path relative to its
temporary directory. Under macOS's default /tmp, a symlink, the sweep's
resolved spec paths are not relative to it and the table writes them
whole, so the test failed wherever TMPDIR was left unset, and a
comment-only control planted in sweep.py failed with it. The test now
keys the table on each spec's name.
The push checklist measured the PR title's length alone, where the
preflight script it replaced also held the title to the Conventional
Commits form. The title is the one subject that lands on main, since
the squash takes it, so a prose title passed every line of the
checklist.

The subject check now reads the title too, with the number the squash
appends, and prints it as it prints a commit subject: past 72
characters, off the form, or ending in a period. The message-form
rule's probe says so.
The differentials reference said morphic exits 1 when a spec has
findings. It exits 1 when a diagnostic reaches --fail-on, errors by
default, or when the spec lowered to no document, so a spec with only
warnings exits 0, as compile --help says.
sweep.py --help listed its options without saying what each does, how
compile flags reach the build or what the exit codes mean, so an agent
running it learned only the option letters. It now prints the
docstring, which says all three, under option names that match it,
and the verifying skill points at --help rather than at the source.
The report test checked the exit-code distributions and the hint, so a
sweep that printed no IR bytes, read both totals from one side,
dropped the list of differing specs or wrote absolute paths still
passed. It now compares the whole report and every results.tsv row.

The spec paths are relative again: the test resolves its root as
find_specs resolves each spec. An earlier fix for macOS, where /tmp is
a symlink, had keyed the table on spec names and so stopped checking
the path.
The lint holds a span's path to the tree up to a #entry, a :line or a
glob, and the real skills rely on the entry and the glob: without
those cuts the lint reports 11 and 2 false findings. No test held any
of the three, and none held the skip of a tracked test file deleted
from disk, without which the lint stops with exit 2 in the middle of a
change that removes one. The clean fixture now names each form, and
the tree test deletes a tracked test file.
The planting rule credited #1141 with a full disk emptying a file
mid-restore. On #1141 the full disk failed the restore and left a
mutant planted; the emptied file was a probe on #1143. The rule on
sweeping every site cited #1143 with no clause saying what its work
showed: a review's fix that missed a second file.
A rule line in the differentials reference and a line of sweep.py ran
past the 100 characters the skills' prose and scripts keep to.
Two Whys named no failure: one said only that both shapes survived
every corpus sweep, the other that two regressions in #1016 were
invisible to every corpus. Each now says what went wrong. On #1009 a
file got two labels and a shared request body was named by whichever
operation lowered first; on #1016 readers followed references the
library never resolved and read a component by its YAML shape.
The review skill's setup said to probe "only there" after naming both
the worktree and the domain skill, and its findings section ranked
"each issue:" with no noun; it now says to probe only in the worktree
and ranks each finding. The curating skill's script step ended on an
"otherwise" with no clear antecedent, and its sentence on descriptions
ran three predicates off one subject. Both are reworded, and the rules
they state are unchanged.
sweep.py's docstring, which --help prints, said every corpus file was
read, where only .yaml, .yml and .json files are. The lint's docstring
gave every finding a line number, where one about a whole file has
none and a summary line follows, and missing() named only the glob
among the cuts it makes before holding a path, leaving out a #entry
and a :line.
The paragraph that says where the skills are named "the scripts both
use" after a list of three things, and began a sentence "Their
scripts" after one whose subject was a single skill. It now says "the
scripts they use" and "The scripts".
The usage line sweep.py's --help prints read "python3.14 sweep.py",
which fails from the repository root, where the skills run every
command. It now names the script by its path from the root, as the
lint's usage line does.
@OmarAlJarrah
OmarAlJarrah merged commit acf5e3b into main Oct 29, 2026
2 checks passed
@OmarAlJarrah
OmarAlJarrah deleted the chore/agent-skills branch October 29, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants