Skip to content

V5.4.0 beta - #185

Merged
mxriverlynn merged 47 commits into
mainfrom
v5.4.0-beta
Aug 20, 2026
Merged

V5.4.0 beta#185
mxriverlynn merged 47 commits into
mainfrom
v5.4.0-beta

Conversation

Claude Code 2.1.228 rejects any load-time context probe whose text
contains shell variable expansion, so the personal-config probe carried
by all 42 skills aborts the skill before its body loads.

Record the root cause, the evidence, and a fix that deletes the probe
and resolves the configuration directory during the run instead, where a
failure degrades silently rather than aborting.

Refs #178
Reproduce the abort locally and map what the skill loader actually
accepts by running eleven probe forms through fresh sessions across five
permission modes. Bare $HOME is permitted; brace syntax is refused even
for $HOME, every other variable name is refused, and printenv is refused
outright.

Adversarial validation killed the previous fix: no skill can read an
environment variable at run time, and the Read tool expands ~ to the home
directory, so the run would have applied the wrong profile's config.

Adopt the one form measured to load in every permission mode, and record
that the CLAUDE_CONFIG_DIR override is no longer reachable from a probe.

Refs #178
Section 1 told contributors to run ./install-hooks.sh, which rebuilds a
dist/claude-marketplace/ directory. Neither exists anywhere in the repo.
Point at the contributor guide's environment setup instead, and state
that the checked-in marketplace manifest needs no build step.

Add the claude plugin CLI equivalents of the /plugin click-through
steps, verified against a local and a GitHub-source marketplace.
Measured a third route the earlier passes never tested: a probe can run
a shell script when the skill grants that one command, and a script
reads CLAUDE_CONFIG_DIR in an ordinary shell. That keeps the override
the other two options gave up.

Add evidence E26 through E30 covering the grant as the deciding factor,
the CLAUDE_PLUGIN_ROOT substitution ordering, the mandatory guard, the
install behavior from a directory and a GitHub source, and the grant
scope test. Add V14 and V15 recording where a first reading was wrong
and what corrected it.

Rewrite the Changes section to implement option 3, per the operator's
decision.
The skill loader refuses almost every environment-variable reference in
a load-time probe, so the probe all 42 skills used to locate the
personal config directory aborted the skill before its body loaded.

A probe can still run a shell script when the skill grants that exact
command, and a script reads CLAUDE_CONFIG_DIR in an ordinary shell.
Add scripts/han-config-dir.sh at the repo root, symlink it into the 12
plugins carrying config-consuming skills, and point every probe at it
behind a one-command Bash grant. Installation copies the symlink target
as a regular file, so the script ships without any plugin depending on
another to reach it.

The braces around CLAUDE_PLUGIN_ROOT are required, and so is the
2>/dev/null || echo fallback: a missing or failing script otherwise
aborts the skill the same silent way the old probe did.

Verified across all 12 plugins in default mode, and end to end through
a GitHub-source marketplace install.
V8 read only the first line beginning allowed-tools: in each skill.
Eight skills spell that value as a YAML plain scalar across
continuation lines, so the first line is the bare key and the tools sit
underneath. Parsing the full value shows all eight already carry Bash
grants.

Five skills carry none, not 13, which makes the main cost of options 2
and 3 smaller than every earlier pass recorded. Add V16.
The CLAUDE_CONFIG_DIR promise survives the probe fix, so the contract
bullet describing it needs no change. Three things did need one.

Correct config-rule.md on tilde handling: the Read tool does expand a
leading ~, but always to the home directory and never to a configured
one, so a value meant to sit beside the personal config cannot rely on
it. Record the new degradation path, where a missing or failing
resolver script drops the lookup back to the home directory with no
note, and mirror that in the operator guide. Re-sync all 11 vendored
copies.

Replace the failing probe the authoring guidance still taught in two
places, add the measured results table, and document reaching an
environment variable through a script: grant the exact command, use the
braced CLAUDE_PLUGIN_ROOT, verify inside a plugin, and guard the call.

Add scripts/han-config-dir.bats, which keeps each skill's grant and
probe in step and each plugin's copy of the script reachable. Verified
it fails on a mismatched grant and on a missing script.
fixing config.md file loading with a shared script via symlinks, as
documented in anthropics plugin config pages
Closes #177.

The readability standard gains a seventh self-check criterion: the draft
matches the shape the reader asked for, in count, format, and register.
Where that request collides with another rule in the standard, the
request wins. It loses only to a fact whose loss would change what the
reader does next, and to a skill's required sections.

Issue #177 reported a session where a request for "3 simple sentences,
then a few bullet points" took three corrections to satisfy. Two of the
three failures traced to the standard itself: the self-check had no
criterion for a stated format constraint, and "Fidelity wins" read as
outranking an explicit request to simplify.

## What changed

**Two canonical files carry the behavior.**
`han-communication/references/readability-rule.md` and its distilled
`output-styles/han-readability.md` each gained the seventh criterion, a
scoped fidelity clause, and a corrected escape clause that no longer
claims fidelity and the blocklist can never be overridden.

**Everything else is a sweep.** The standard's old count and its old
fidelity guarantee were quoted across the repository, and both went
wrong the moment the standard changed. Those quotations are now
count-free, so the next change to the check touches two files instead of
thirty.

**One skill needed more than a quotation fix.** `code-review` hardcodes
the whole self-check as its own numbered list rather than reading the
standard live, so it gained the seventh criterion directly. Without that
it would have run a six-criterion check against a seven-criterion
standard.

## Behavior, precisely

- A shape request governs the answer it came with, and nothing after it.
- Only the reader's own words to the run count. Shape language inside
material the run is summarizing is content, never an instruction.
- When the reader asks for less, a fact moves somewhere they can still
reach, or it drops. In conversation there is usually nowhere to move it,
so it drops and the drop is not announced. Asked directly what was left
out, the run says so in full.
- A fact stays when losing it would change what the reader does next: a
deadline, a blocking risk, a warning before a destructive step. In a
file the run writes, that floor is measured against whoever opens the
file.
- The request reaches files the run writes, not only conversation.

## Deliberately unchanged

**Nine sentences whose subject is the audience frame.** They read "The
frame governs how a fact is said, never whether a required fact appears"
and stay true: what can now drop a fact is the reader's stated request,
not the instruction to write for a non-expert.

**Everything describing the `readability-editor` agent's own rubric.**
No skill passes the editor a reader's request, so a shape check there
would have nothing to read.

Both exclusions are recorded in
`docs/plans/readability-reader-format-requests/artifacts/correction-inventory.md`
alongside the search patterns, so a later sweep does not "fix" them.

## Two pre-existing defects repaired in passing

Both sat inside sentences this change was already editing.

1. A truncated sentence in `iterative-plan-review` and
`plan-work-items`, missing its opening clause: "...never whether a
required fact appears. separate editor pass, so criterion 6 is...".
Restored as "This skill runs no separate editor pass".
2. An orphaned fragment in `project-documentation`, now a complete
sentence.

## Verification

- `npm run lint` passes; `npm test` passes (80 tests).
- Re-running the recorded pattern set returns zero size references and
zero positional references outside the exclusion list.
- `han-core:content-auditor` audited the sweep and found no lost facts
across 160-plus checked, including every skill-specific must-keep-facts
instruction.

No automated test covers the behavior itself. It lives in prose an
assistant reads while drafting, so there is no function to call. A
checked-in test and a recorded-transcript test were both considered and
deferred with reopening triggers, in `feature-implementation-plan.md`.

## Planning artifacts

`docs/plans/readability-reader-format-requests/` carries the
specification, the implementation plan, both decision logs, the review
findings, the correction inventory, and the verification analysis.
Sixteen specification decisions, five of them settled by the maintainer
directly against a recommendation, each recording the cost that was
accepted.

## Open items, neither blocking

- **OI-1:** `CLAUDE.md` says every doc in this repository follows the
writing voice with no hype. A reader's request now overrides the
blocklist in a committed file, so the convention needs a matching
carve-out or an explicit statement that it governs regardless.
- **OI-2:** The correction inventory was rebuilt several times during
planning, each time because a search pattern was narrower than the
corpus. The branch-scoped documentation check is what closes it, and it
should run after this merges, when its diff base is right.

## Note on versioning

No plugin version moves on this branch and `CHANGELOG.md` is untouched,
matching how this repository separates feature work from release work.
`/han-release` proposes the bump at release time.
…script

The config-probe migration reached 42 of the 43 skills. pairing kept the
inline `${CLAUDE_CONFIG_DIR:-$HOME/.claude}` probe, which the loader
refuses with "Contains expansion", taking the whole skill down before it
runs. It now calls the same han-config-dir.sh script every other skill
calls, with the matching allowed-tools grant.
…ries

docs/readability.md described the self-check as six criteria and stated
fidelity as an absolute in two places, contradicting its own later
section and the canonical rule. The output-style doc dropped the count
but kept the unqualified fidelity sentence. Both now name the seventh
criterion and the condition under which fidelity yields.
…ributor guide

Every plugin except han gained a scripts/han-config-dir.sh symlink and the
repo gained a root scripts/ directory, neither of which appeared in the
CLAUDE.md layout. The Adding a skill steps also never named the config
probe or its grant, which is how pairing shipped with the old probe form.
…ated shape

The editor's rubric stopped at six criteria, so a synthesis skill that
relayed a reader's format request got back a draft that ignored it: the
editor's progressive-disclosure and first-use-explanation moves could
undo the very shape the reader asked for. The rubric gains a seventh
criterion governing a relayed shape request, and the two fidelity
absolutes gain the same carve-out the canonical rule carries.

edit-for-readability now resolves a shape request beside the reader frame
and relays it in the user's own words. With no request relayed, criterion
7 passes and changes nothing, so every existing dispatch keeps its
preserve-every-fact guarantee.

Also corrects the long-form doc's criterion list, which named 'every fact
preserved' where the editor's sixth criterion is progressive disclosure.
Han Concise carries everything Han Readability does — the audience frame,
the output properties, the writing voice, the vocabulary blocklist, the
prose-only scope, the fidelity guard, the break-a-rule escape, and the
seven-criterion self-check — plus a brevity property the canonical rule
does not carry. A turn drops preamble and recap, spends no sentence that
carries neither a fact nor a needed transition, and reserves headings for
responses with parts worth navigating. Brevity comes out of filler only;
the fidelity guard still holds every fact.

The brevity property stays out of readability-rule.md on purpose, so it
shapes the conversation and leaves every Han skill's deliverable alone.

Han Readability is unchanged. The new style ships with its long-form doc,
a plugin README scent line, and updates to the readability guide, the
plugin index, the coverage rule, the contributor guide, and the project
map.
Fidelity wins is the wrong default for a style whose whole point is
brevity. Claiming every fact survives at full precision forces the
reader to ask for less before they get it, and asks the output to be
anything but concise.

Han Concise now assumes the reader wants less than the source carries.
Supporting detail rolls up into the statement it supports whenever the
detail adds no meaningful value or clarification. A roll-up has to be
true of everything it covers, so it is never a blur: the shorter true
statement wins, the vaguer one never does.

Three things stay at full precision. A fact whose loss would change what
the reader does next, a number they will act on, and a stated condition
that bounds when a claim holds. Self-check criterion 6 is rewritten from
"Every fact preserved" to check the roll-up against that floor.

Han Readability is unchanged and still preserves every fact, so the two
styles now differ on fidelity as well as brevity. Both departures stay
out of readability-rule.md, so Han's skills keep the canonical rule.
…y default

The old wording read as permissive. "Keep it out of the readable
paragraphs, where one must sit inline keep it small" left inline as an
open option, and a first real session produced numbered steps threading
file paths, method signatures, and shell commands through every
sentence. Each detail was correct and the result was unreadable.

Separation is now the default and inline is the exception: one
identifier the sentence is genuinely about, kept only where pulling it
out would leave the sentence pointing at nothing. A paragraph or list
item threading several paths, signatures, or snippets through its
sentences has failed the property, however accurate each one is.

The drafting property alone was what got ignored, so the self-check now
enforces it. Criterion 6 is new, the roll-up criterion moves to 7, and
the shape criterion moves to 8 and keeps its precedence.
…il too

The soft wording that let han-concise thread paths, signatures, and
shell commands through its sentences is the same wording han-readability
carries, and the failure mode is identical. Both styles now make
separation the default and reserve inline for the one identifier a
sentence is genuinely about.

han-readability's self-check gains the same criterion at position 6, so
it runs eight criteria: the rule's seven plus Technical detail
separated. The fact criterion moves to 7 and the shape criterion to 8,
keeping its precedence.

That makes the technical-detail property a departure both styles share
from readability-rule.md, which still leaves inline detail an open
option. The docs record it as deliberate on both sides so a sync pass
does not read it as drift, and Han's skills keep the canonical rule
until we decide to move it there.
… rule

Both output styles had already tightened this property against
readability-rule.md, which still left inline detail an open option. Two
styles disagreeing with the rule on the same point is the signal the
rule is the one that is wrong, so the tightened version moves into the
canonical file and the styles stop departing from it.

The rule's property now makes separation the default and reserves inline
for the one reference a sentence is genuinely about. Its standardized
self-check gains Technical detail separated at position 6, so it runs
eight criteria: the fact criterion moves to 7 and the shape criterion to
8, keeping its precedence.

The readability-editor rubric follows. Its progressive-disclosure
criterion was carrying the technical-detail move as a trailing clause;
that move is now its own criterion 7, and the shape criterion is 8.

Every Han skill that sources the rule now drafts and checks against the
tightened property. The docs that recorded the departure as deliberate
are back to describing the styles as plain derived copies, and
han-concise is down to its two remaining departures.
…rion count

The rubric moved to eight criteria when technical-detail separation was
promoted, but the anti-pattern list still told the agent to apply seven.
The suite ships two output styles now, and the contributor guide had a
checklist for adding a skill and one for adding an agent but nothing for
adding a style. The new section names the file locations, the long-form
doc requirement the coverage rule already carries, the README scent
line, and the rule that a departure from a canonical reference gets
recorded as deliberate.
… full

Both operator-facing docs paraphrased it as "sentence length", dropping
the active-voice half the agent's own rubric asks for.
… technical-detail separation (#189)

## Summary

**This PR adds a shorter-register output style and makes
technical-detail separation a checked criterion of the canonical
readability standard, so you can select a terser session and every
prose-producing skill now keeps file paths and signatures out of its
sentences.**

## Behavior changes

The new `han-concise` output style gives you a shorter register to
select. An output style is a block of text Claude Code appends to the
system prompt once at session start, chosen in `/config`, so it shapes
every turn of that session. `han-concise` carries the same
Human-Readable Output Standard as the existing `han-readability` style,
with two deliberate differences. It drops preamble and recap, and it
rolls detail up by default instead of carrying every fact through. Nine
near-identical passing checks become "all nine passed". A floor holds it
back from vagueness. It keeps at full precision any fact whose loss
would change what you do next, any number you will act on, and any
stated condition that bounds a claim.

The second mechanism reaches further. `readability-rule.md` is the
standard every prose-producing Han skill loads at runtime, so a change
there changes what all of them write. "Technical detail follows the
prose" was already one of its output properties, but nothing checked it
and the wording left inline detail an open option. It now makes
separation the default, and it has its own self-check criterion with a
testable failure. A paragraph or list item that threads several paths,
signatures, or snippets through its sentences has failed, however
accurate each one is.

The `readability-editor` agent and the `han-readability` style carry the
same criterion. Both rubrics grow from seven criteria to eight, which
renumbers the reader's-stated-shape criterion, the one that wins real
collisions, from 7 to 8. In the editor's rubric the technical-detail
move had been a trailing clause on progressive disclosure; it is now its
own criterion. The remaining files are operator-facing documentation for
the new style and the renumbering, plus a CONTRIBUTING checklist for
adding an output style.
…nd bound the empty visual table

artifact-invariants.md ended mid-clause on the Visual Reference
invariant, so a run that received visual material asked the synthesizer
to preserve a rule whose definition stopped after "with". Restores the
missing clause from the pre-extraction SKILL.md text: an inline embed
beside the prose describing each state.

planning-boundary-rule.md showed the Visual Material Received section as
a table with illustrative rows and never said the table is omitted when
nothing arrived. A run following that shape wrote a placeholder row and
the Step 9 completeness gate refused it. States that `None received`
stands alone and the table is omitted, and why.

Closes #187.
…ce material (#190)

Fixes the two defects reported in #187, both in `han-planning` reference
material and both about how a planning run handles visual material.

## The truncated invariant

`plan-a-feature/references/artifact-invariants.md` ended mid-clause on
the `Visual Reference` invariant, stopping after the word "with". Step 8
tells the synthesizer to read that file and preserve the invariants it
carries, so a run that received visual material was asked to preserve a
rule whose definition was unfinished.

The missing clause was lost when the file was extracted from `SKILL.md`
in d01abe6. I recovered it from that commit's parent, where the same
invariant reads in full at line 655. It is restored verbatim: the table
lists every item the boundary record records as received, under that
exact heading, with an inline embed beside the prose describing each
state.

## The empty visual-material table

`planning-boundary-rule.md` said the Visual Material Received section
takes `None received` when nothing arrived, then showed the section as a
table with two illustrative rows. Nothing said the table itself goes
away. A run that followed the shape it was shown wrote a placeholder
row, and the Step 9 completeness gate refused it, because the gate reads
every row as an item claimed on disk.

The rule now says `None received` stands alone and the table is omitted,
and names the gate as the reason.

## Verification

`npm run lint` passes. Both changes are prose in reference files with no
script or schema behavior attached.

Closes #187.
…mated-test-planning

An honestly-evidenced finding at CRIT could walk a production change into a
build that never authorized it. In automated-test-planning, a security item
whose own text named "first add an explicit order" landed in the plan as a
test to write. In tdd, the observed-failure gate confirmed the red was real,
and green arrived by editing a shared engine every application in the monorepo
consumes. Neither skill asked whether the test deserved to exist in that build.

tdd gains a scope gate as the observed-failure gate's companion. Step 1 records
a scope boundary in files and directories. Step 2 tests every list item against
it and works a three-rung resolution ladder when one trips: redesign the test,
defer the item as its own ticket, or escalate when the requested behavior
cannot be delivered without the change. The Red phase names the files green
would touch before touching any of them, and Step 5 reports what the gate
moved. failure-modes.md documents the failure as mode 10, including the way it
compounds when a later test leans on the new behavior for a deterministic
fixture.

automated-test-planning gains a prerequisite sweep before priorities are
assigned. An item that cannot be tested until shipped code changes leaves the
priority tiers for a Blocked by a Production Change section carrying the change,
the file, and the file's other consumers. The sweep runs over security items on
the same terms, because CRIT ranks a finding's severity and says nothing about
whose ticket the fix belongs to. The blocked count sits in the Summary bullets,
where a caveat beneath the Technical Reference would lose the attention contest
to a CRIT label above it.

Closes #188
…mated-test-planning (#191)

Closes #188.

## The defect

An honestly-evidenced finding at CRIT could walk a production change
into a build that never authorized it.

In `automated-test-planning`, every `adversarial-security-analyst` item
lands at CRIT. One such item's own text said the test "requires first
adding an explicit `order`" — a production change to shared code,
packaged as a test to write. Nothing asked whether that change belonged
to the ticket.

In `tdd`, the item entered the behavior list carrying that CRIT label,
which the YAGNI gate read as evidence. The test went red exactly as
designed, the observed-failure gate was satisfied mechanically, and
green arrived by editing a model in a shared Rails engine that every
application in the monorepo consumes. The red was genuine. Nobody
checked whether the test deserved to exist in that build.

## What changed

### `tdd` gains a scope gate

The observed-failure gate proves a red is real. It does not prove this
build owns producing the missing behavior. The scope gate is its
companion, wired at four points:

1. **Step 1 records a scope boundary** in files and directories. Inside:
the files the request names plus their tests. Outside: shared libraries,
engines, packages, anything a second consumer also uses, and code
another team owns per `CODEOWNERS`. When the request names no files, the
boundary is the application the behavior lives in, with its dependencies
outside.
2. **Step 2 tests every list item against it**, hardest on items
arriving from a test plan or agent finding with a severity label. A
three-rung resolution ladder handles the ones that trip: redesign the
test so it does not need the out-of-scope behavior; defer the item as
its own ticket with the file and its consumers named; escalate only when
the requested behavior cannot be delivered without the change.
3. **The Red phase names the files green would touch** before touching
any of them.
4. **Step 5 reports what the gate moved** and which rung resolved it.

`references/failure-modes.md` documents this as failure mode 10,
including how it compounds: a later test leans on the new behavior for a
deterministic fixture, so the out-of-scope change becomes load-bearing
for work that could have arranged its own setup. Rung 1 catches that
case explicitly.

### `automated-test-planning` gains a prerequisite sweep

A new Step 3 sweep, run before IDs and priorities are assigned, pulls
any item that cannot be tested until shipped code changes first. Those
leave the priority tiers for a **Blocked by a Production Change**
section recording the change needed, the file that would carry it, and
that file's other consumers, so a reader can see the blast radius and
open a separate ticket.

The sweep applies to security items on the same terms as every other.
The SEC classification rule now says so directly: CRIT ranks a finding's
severity and says nothing about whose ticket the fix belongs to.

The blocked count sits in the Summary bullets rather than only in the
Technical Reference, because the issue's own report noted that "the
CRIT/HIGH labels got more attention than the caveats did."

## Files

| File | Change |
| --- | --- |
| `han-coding/skills/tdd/SKILL.md` | Scope-gate constraint, Step 1
boundary, Step 2 gate and ladder, Red-phase check, Step 5 reporting |
| `han-coding/skills/tdd/references/failure-modes.md` | Failure mode 10,
plus the closing check now covering both gates |
| `han-coding/skills/automated-test-planning/SKILL.md` | Prerequisite
sweep, SEC classification note, Step 4 section list |
| `han-coding/skills/automated-test-planning/references/template.md` |
Blocked by a Production Change section and Summary bullet |
| `han-coding/docs/skills/tdd.md` | Scope gate in Key concepts, What you
get back, and In more detail |
| `han-coding/docs/skills/automated-test-planning.md` | Prerequisite
sweep in Key concepts, What you get back, In more detail |

## Verification

`npm run lint` (13 hooks) and `npm test` (80 Bats assertions) both pass.
`tdd/SKILL.md` is 285 lines and `automated-test-planning/SKILL.md` is
274, both well under the 500-line progressive-disclosure ceiling; the
narrative failure-mode content went to `references/`, and the gate's
conditional logic stayed in the SKILL body. No version bumped and no
`AskUserQuestion` added to `allowed-tools`.
Closes #182.

`.claude/skills/han-release/SKILL.md` carried a 1435-character
`description` against the documented 1024-character maximum, and a
563-line body against the 500-line guideline. The description is now 791
characters, with the parent-and-child versioning explanation and the
already-bumped-version behavior moved into the body where there is no
budget pressure. Two blocks of domain knowledge moved out of the body
into `references/attribution-rules.md` (the closed-issue attribution
rules) and `references/version-plan-rules.md` (the baseline, bump-level,
and target rules formerly numbered 3a through 3c), leaving the body at
480 lines.

Seventy-four reference files over 100 lines now open with a `## Contents`
list of their sections, so a partial read still shows the file's full
scope. The sixteen output templates are left alone: a contents list on a
skeleton copied whole would land in every document it produces.

Twenty-three of the twenty-five reference links that sat two hops from a
SKILL.md are gone. `yagni-rule.md` and `evidence-rule.md` name each other
in prose instead of linking, so neither is reachable only through the
other; `code-review`, `plan-implementation`, and `han-release` now link
directly from SKILL.md to the files their own reference files were
reaching for. `evidence-rule.md` also pointed at `../../docs/evidence.md`,
which resolves in this repository and breaks for anyone who installs the
plugin, so it now links the public URL. The two remaining pairs run
through `docs/semantic-versioning.md`, a repository document rather than
a skill reference file, and reach authoring guidance no release run needs.

`han-plugin-builder` gains the rules behind all of this.
`skill-reference-files.md` documents the one-level-deep requirement, the
in-plugin path requirement, and the over-100-lines contents list with its
template exception. `progressive-disclosure.md` and
`cowork-specific-skill-instructions.md` point at those rules rather than
owning a partial copy, and `skill-builder`'s review step checks them.
Closes #182.

@kasparovabi opened the issue with measured counts against [Anthropic's
skill authoring
guidance](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices).
I re-measured on this branch, confirmed each item against the source
guidance, and fixed all three.

## `han-release` was past two limits

`.claude/skills/han-release/SKILL.md` had a 1435-character `description`
against the documented 1024-character maximum, and a 563-line body
against the 500-line guideline.

The description is now 791 characters. What came out was the
parent-and-child versioning explanation and the already-bumped-version
behavior, both of which are process detail the body already carries.

Two blocks of domain knowledge moved out of the body into new reference
files, leaving it at 480 lines:

| New file | What moved |
| --- | --- |
| `references/attribution-rules.md` | The closed-issue lookup, the
substantive-comment test, the bot exclusions, and the `$issue_list`
shape (Step 2.6) |
| `references/version-plan-rules.md` | The baseline, bump-level, and
target rules formerly numbered 3a through 3c |

## Reference files over 100 lines now open with a contents list

The guidance asks for a table of contents past roughly 100 lines, so a
partial read still shows the file's full scope. Seventy-four files now
have one, listing the headings that name each file's real sections.

The sixteen output templates are left alone, as the issue suggested. A
contents list on a skeleton copied whole would land in every document it
produces. The new guidance states that exception rather than leaving it
as an unexplained gap.

## Two-hop reference links: 25 down to 2

I measured 25 pairs on this branch, not the 15 counted against `main`.
The fix keeps the cross-references and removes the chains:

- `yagni-rule.md` and `evidence-rule.md` name each other in prose
instead of linking, so neither is reachable only through the other. The
vendored copies across four plugins stay byte-identical.
- `code-review`, `plan-implementation`, and `han-release` link directly
from their SKILL.md to the files their own reference files were reaching
for.
- `evidence-rule.md` pointed at `../../docs/evidence.md`, which resolves
in this repository and breaks for anyone who installs the plugin. It now
links the public URL.

Two pairs remain, both running through `docs/semantic-versioning.md`.
That is a repository document rather than a skill reference file, and it
reaches authoring guidance no release run needs.

## `han-plugin-builder` now carries the rules

None of the three rules were in the repo's own authoring guidance, which
is why the drift went unnoticed. The description-length doc already
covered 1024 thoroughly; the other two were missing or misfiled.

- `skill-reference-files.md` gains the one-level-deep requirement, an
in-plugin path requirement, and the over-100-lines contents list with
its template exception.
- `progressive-disclosure.md` and
`cowork-specific-skill-instructions.md` point at those rules instead of
owning a partial copy. The one-level-deep rule had been living inside
the cowork doc as though it were cowork-specific; it is general.
- `skill-builder`'s review step checks all three on every new skill.

## Verification

`npm run lint` and `npm test` both pass (80 Bats tests, including the
byte-identical check on the vendored rule copies). Re-measured after the
changes: no description over 1024, no body over 500, no non-template
reference file over 100 lines without a contents list.
@mxriverlynn
mxriverlynn marked this pull request as ready for review August 20, 2026 21:14
@mxriverlynn
mxriverlynn merged commit f5ebd74 into main Aug 20, 2026
2 checks passed
@mxriverlynn
mxriverlynn deleted the v5.4.0-beta branch August 20, 2026 21:14
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.

1 participant