diff --git a/docs/skills/han-coding/coding-standard.md b/docs/skills/han-coding/coding-standard.md index d35c91c..0095ca5 100644 --- a/docs/skills/han-coding/coding-standard.md +++ b/docs/skills/han-coding/coding-standard.md @@ -14,7 +14,7 @@ Operator documentation for the `/coding-standard` skill in the han plugin. This - **Three modes.** Creating new, Converting existing (for example, an ADR into a standard), Updating existing. - **Linter-first check.** Before writing anything, the skill asks: should this be a linter or formatter rule instead? Style conventions that tooling can enforce become tooling configuration, not standards documents. -- **Evidence from the codebase via parallel explorers.** Two `codebase-explorer` agents run in parallel. One finds implementation patterns (Correct-usage and What-to-avoid candidates with file:line references); the other finds existing standards and ADRs the new one should link or resolve. Correct-usage examples are drawn from real files. If the pattern is not yet implemented, examples are labeled "Proposed pattern." +- **Evidence from the codebase via parallel explorers.** Two `codebase-explorer` agents run in parallel. One finds implementation patterns (Correct-usage and What-to-avoid candidates with file and symbol name references); the other finds existing standards and ADRs the new one should link or resolve. Correct-usage examples are drawn from real files. If the pattern is not yet implemented, examples are labeled "Proposed pattern." - **Adversarial review before verification.** A `junior-developer` agent stress-tests the draft for ambiguous rules, hidden assumptions, and conflicts with existing standards. An `information-architect` agent audits the draft for findability, scannability, and whether the Rationale is placed where the right reader will find it. - **Hierarchically-prefixed filenames.** `{top-level}[-{second-level}]-{hyphenated-name}.md`. A one- or two-level hierarchy prefix (for example, `svelte-stores-state-shape.md`) discovered at runtime from existing standards and project context, so related standards sort together in a directory listing. - **Path-scoped rules via per-file-type index files.** Each new standard carries `paths:` YAML frontmatter declaring the file globs it governs. The skill routes the standard into one or more per-file-type index files under `.claude/rules/coding-standards/` (for example, `svelte.md`, `typescript.md`, `ruby.md`). The index files are themselves path-scoped rules: when Claude Code reads a file matching an index's globs, it loads only that small index — a short load-on-demand instruction plus a list of standards relevant to that file type, each with a 1-3 sentence description of what it covers and when to pull it. Claude then opens the full text of a standard only if it decides the standard applies. Standards do not appear in the available-skills picker — the rules surface is separate. @@ -98,13 +98,13 @@ The skill walks a ten-step process: 1. **Determine mode.** Creating new / Converting existing / Updating existing. 2. **Evaluate appropriateness.** Should this be tooling instead? If yes, warn and ask. 3. **Discover project structure.** Find the coding-standards directory (or create one), enumerate existing standards, check format compatibility, discover the filename hierarchy taxonomy from existing standards' filenames plus the project's languages, frameworks, and subsystems, and capture the project's primary file-type globs for the `paths:` proposal in Step 6. -4. **Gather context.** Topic, scope, motivation. Dispatch two `codebase-explorer` agents in parallel for implementation patterns and existing standards/ADRs. +4. **Gather context.** Topic, scope, motivation. Dispatch two `codebase-explorer` agents in parallel for implementation patterns and existing standards/ADRs; the patterns explorer returns each candidate's durable anchor (exported symbol or stable heading) alongside the line range it used to navigate, which the author drops by default and keeps only to match an established line-number house style, and the standards explorer returns cross-references by stable heading plus whether the existing standards establish that house style. 5. **Convert source document** (conversion mode only). Map sections using the ADR-conversion-mapping reference; handle the source file (delete if fully subsumed, link if partial). 6. **Write the coding standard.** Hierarchically-prefixed filename (top-level subsystem/framework, optional second level), fill the template with real code examples and actual project language identifiers. Propose a `paths:` glob list scoped to what the standard governs, get user approval, and write it as YAML frontmatter at the top of the file. 7. **Integration.** Determine which per-file-type index files under `.claude/rules/coding-standards/` the standard belongs in (based on the buckets discovered in Step 3.6); for each, create from the [index-file template](../../../han-coding/skills/coding-standard/references/index-file-template.md) or update in place to add a bullet entry with the standard's title, a relative link to the canonical doc, and a 1-3 sentence description of what it covers and when to pull it; ensure the memory file's pointer paragraph exists (added once if missing, never enumerating individual standards); add cross-references in both directions. In update-mode, the skill deltas the standard's entry across index files when its `paths:` changed: removed from buckets that no longer match, added to buckets that newly match, description updated in place when scope shifted. 8. **Adoption-bias audit.** Six structural checks against over-application: primary-rationale visibility, a decision tree near the top, a substantive *When NOT to Apply* section, surfaced (not buried) exceptions, code-example comments that match the primary rationale, and a verification step for defensive adoptions. 9. **Adversarial review.** Dispatch `junior-developer` for ambiguity and assumption checks and `information-architect` for findability and structure. Apply actionable edits. -10. **Verification.** Re-read the file, confirm metadata, template structure, `paths:` frontmatter, index-file membership across every matching bucket (with the entry's link resolving back to the canonical doc and the description in the 1-3 sentence shape that names both coverage and when-to-pull), real file paths in examples, distinct Correct-vs-Avoid examples, that no enumerated entry was added to the memory file, and that Step 8 and Step 9 edits were applied. +10. **Verification.** Re-read the file, confirm metadata, template structure, `paths:` frontmatter, index-file membership across every matching bucket (with the entry's link resolving back to the canonical doc and the description in the 1-3 sentence shape that names both coverage and when-to-pull), durable references in both the standard body and its index entry (ensure that standard references don't get outdated upon next code change), real file paths in examples, distinct Correct-vs-Avoid examples, that no enumerated entry was added to the memory file, and that Step 8 and Step 9 edits were applied. ## YAGNI diff --git a/han-coding/skills/coding-standard/SKILL.md b/han-coding/skills/coding-standard/SKILL.md index e3e5042..2c2f234 100644 --- a/han-coding/skills/coding-standard/SKILL.md +++ b/han-coding/skills/coding-standard/SKILL.md @@ -92,15 +92,16 @@ If no accepted evidence applies, recommend deferring the standard with the trigg Skip these agents when the user has already provided full Correct-usage examples and conflicting-pattern evidence. Otherwise, launch both in parallel — one finds what the codebase already does, the other checks what the project has already documented. Include the topic, scope, and docs/coding-standards directories from Step 3. -1. **Launch han-core:codebase-explorer agent (implementation patterns)** — prompt: "Explore the codebase for existing implementations related to {topic} across {scope}. Return: concrete file paths and line ranges that illustrate the convention in practice (Correct-usage candidates), file paths that violate or contradict the convention (What-to-avoid candidates), and any places where the convention is applied inconsistently across the codebase. Focus on real files; do not invent examples." +1. **Launch han-core:codebase-explorer agent (implementation patterns)** — prompt: "Explore the codebase for existing implementations related to {topic} across {scope}. Return concrete places that illustrate the convention in practice (Correct-usage candidates), places that violate or contradict the convention (What-to-avoid candidates), and any places where the convention is applied inconsistently across the codebase. For each place, return a file path, a line range, and one or more greppable durable anchors — read `${CLAUDE_SKILL_DIR}/references/durable-references.md` and follow its Rules 1 and 2 to derive them; where Rule 2 reaches escalation, return the place flagged for engineer review instead of an anchor. Focus on real files; do not invent examples." -2. **Launch han-core:codebase-explorer agent (standards and ADRs)** — prompt: "Explore {docs-directory} and {coding-standards-directory} for existing coding standards, ADRs, or project docs that touch {topic} across {scope}. Return: any standards or ADRs that already cover this topic (in full or in part), cross-references that the new standard will need to link, and any contradictions between existing docs that the new standard will need to resolve or cite." +2. **Launch han-core:codebase-explorer agent (standards and ADRs)** — prompt: "Explore {docs-directory} and {coding-standards-directory} for existing coding standards, ADRs, or project docs that touch {topic} across {scope}. Return: any standards or ADRs that already cover this topic (in full or in part), cross-references that the new standard will need to link — each as a document path plus a stable section heading — and any contradictions between existing docs that the new standard will need to resolve or cite." -After both agents complete, merge their findings into a context block with: -- **Correct-usage candidates** (file:line references) for Step 6 -- **What-to-avoid candidates** (file:line references) for Step 6 +After both agents complete, merge their findings into a context block: +- **Correct-usage candidates** — durable anchor(s), plus the line range (Step 6 drops it unless a house style keeps it) — for Step 6 +- **What-to-avoid candidates** — same pairing — for Step 6 +- **Flagged candidates** — places the rule could not cleanly anchor, carried as engineer-review items for Step 6; no reference is emitted for one without engineer input - **Inconsistency notes** — areas where the convention is applied unevenly (these become Rationale material, not examples) -- **Existing-doc cross-references** for Step 7 +- **Existing-doc cross-references** (document path plus stable section heading) for Step 7 ## Step 5: Convert Source Document (skip if creating new or updating) @@ -113,6 +114,8 @@ When converting an existing document into a coding standard: ## Step 6: Write the Coding Standard +Read the **durable-reference rule** in [durable-references.md](./references/durable-references.md) and apply it throughout this step — this is the rule's **authoring mode** — all rules apply. For any candidate Step 4 flagged for engineer review — or any example you cannot cleanly anchor yourself — surface it to the engineer with a recommended resolution rather than emitting a coarse or anchorless reference; do not silently resolve it. + 1. Copy the template from [template.md](./references/template.md) 2. **File name:** `{top-level}[-{second-level}]-{hyphenated-name}.md` — a one- or two-level hierarchy prefix followed by the standard's specific name. The hierarchy must come from the taxonomy discovered in Step 3.5, never invented or hardcoded. - **Top-level (required):** the highest-level grouping the standard belongs to (e.g., `svelte`, `rails`, `postgres`, `api`). Reuse an existing top-level prefix from Step 3.5 when one fits; only introduce a new top-level when no existing prefix applies, and prefer one that matches a language, framework, or subsystem already named in CLAUDE.md or project-discovery.md. @@ -122,7 +125,7 @@ When converting an existing document into a coding standard: 3. **Location:** place in the directory determined in Step 3 4. **Fill in metadata:** - **Status**: per Step 1 mode (`proposed` for new, `accepted` for converted) - - **Applies To**: free-text matching the project's terminology + - **Applies To**: a membership criterion for entities governed by this standard, per durable-reference Rule 3. - **Date Created / Last Updated**: current date and time 5. **Propose the `paths:` glob list and get user approval.** The `paths:` field in the YAML frontmatter is the canonical declaration of which file globs the standard governs. Step 7 uses each glob to route the new standard into one or more per-file-type **index files** under `.claude/rules/coding-standards/` — the standard itself is never loaded directly as a path-scoped rule. The index files are what Claude Code loads via [Claude Code path-scoped rules](https://code.claude.com/docs/en/memory), and they then point Claude at this standard on demand. Cross-cutting standards whose `paths:` span multiple file-type buckets get listed in each matching index. - **Build the candidate list** from the Applies To text and Scope section of this standard, intersected with the project file-type globs discovered in Step 3.6. Scope a glob no broader than the standard actually governs — if the standard applies only to Svelte stores, prefer `src/**/stores/**/*.ts` over `**/*.ts`. @@ -237,12 +240,13 @@ Read back the coding standard file and confirm: 1. All metadata fields are filled in (no `{placeholder}` values remain) 2. Template structure from [template.md](./references/template.md) was followed 3. YAML frontmatter is present at the top of the file with a `paths:` list, every glob is double-quoted, and the frontmatter closes with `---` before the `# {Title}` heading. When updating an existing standard, confirm no pre-existing frontmatter keys were stripped. -4. **Index-file membership.** The standard is listed in every index file under `.claude/rules/coding-standards/` whose `paths:` overlaps the standard's `paths:`, and is **not** listed in any index file whose `paths:` does not overlap. For each index file the standard was added to, confirm: the file's `paths:` frontmatter still parses; every pre-existing entry is still present (none were dropped while editing); the instruction paragraph between the heading and `## Available standards` is unchanged from the template (verbatim); the new entry's relative link resolves to the canonical standard file; the new entry's description is 1-3 sentences and names both what the standard covers and when a reader should pull the full file. In update-mode, additionally confirm the standard's entry was removed from any index file whose `paths:` no longer overlaps. -5. `CLAUDE.md` (or `AGENTS.md`) was **not** given a new enumerated entry for this standard. If a pointer paragraph was added because none existed, confirm it appears exactly once and that its wording describes the per-file-type index-file mechanism (not the prior one-symlink-per-standard mechanism). -6. Code examples reference real files that exist (verify with Glob) — cross-check against the Correct-usage candidates returned in Step 4 -7. "What to avoid" examples are distinct from "Correct usage" examples -8. If converting (Step 5): confirm source document was handled (deleted or updated with link) -9. Confirm agent configuration file references are correct -10. Confirm each of the six Adoption-Bias Audit checks (Step 8) has a cited satisfying section in the final draft, or that the failing check produced an applied revision -11. Confirm actionable edits from Step 9 were applied, or that any skipped edits were surfaced to the user -12. If any issues are found, fix them +4. The document follows the **durable-reference rule** in [durable-references.md](./references/durable-references.md). Re-read it, then apply its authoring mode as a verification scan over the *whole prose*, not just the citations — reframing each temporal hit and both Rule 4 idioms to the timeless property, and flagging any reference you cannot re-anchor for the engineer. +5. **Index-file membership.** The standard is listed in every index file under `.claude/rules/coding-standards/` whose `paths:` overlaps the standard's `paths:`, and is **not** listed in any index file whose `paths:` does not overlap. For each index file the standard was added to, confirm: the file's `paths:` frontmatter still parses; every pre-existing entry is still present (none were dropped while editing); the instruction paragraph between the heading and `## Available standards` is unchanged from the template (verbatim); the new entry's relative link resolves to the canonical standard file; the new entry's description is 1-3 sentences, names both what the standard covers and when a reader should pull the full file, follows durable-reference Rule 3. In update-mode, additionally confirm the standard's entry was removed from any index file whose `paths:` no longer overlaps. +6. `CLAUDE.md` (or `AGENTS.md`) was **not** given a new enumerated entry for this standard. If a pointer paragraph was added because none existed, confirm it appears exactly once and that its wording describes the per-file-type index-file mechanism (not the prior one-symlink-per-standard mechanism). +7. Code examples reference real files that exist (verify with Glob) — cross-check against the Correct-usage candidates returned in Step 4 +8. "What to avoid" examples are distinct from "Correct usage" examples +9. If converting (Step 5): confirm source document was handled (deleted or updated with link) +10. Confirm agent configuration file references are correct +11. Confirm each of the six Adoption-Bias Audit checks (Step 8) has a cited satisfying section in the final draft, or that the failing check produced an applied revision +12. Confirm actionable edits from Step 9 were applied, or that any skipped edits were surfaced to the user +13. If any issues are found, fix them diff --git a/han-coding/skills/coding-standard/references/durable-references.md b/han-coding/skills/coding-standard/references/durable-references.md new file mode 100644 index 0000000..268e7c1 --- /dev/null +++ b/han-coding/skills/coding-standard/references/durable-references.md @@ -0,0 +1,135 @@ +# The durable-reference rule + +A committed document that cites code — a coding standard, a piece of project documentation — +must stay accurate as the code it cites evolves. Two failure modes break that. A bare +`file:line` citation in the document goes stale the moment a line is inserted above it. A +snapshot roster of "current consumers" goes stale as consumers change. This rule prevents both. + +It is read in two modes: + +- **Research mode** — gathering evidence from the live code, for example a dispatched explorer + agent. Research resolves the durable anchor and its scope and reports them back, along with the + file and line range it used to find them. Only **Rules 1 and 2** apply; a researcher may ignore + Rules 3 and 4 — its job is to provide concrete code examples, along with anchors + they can be cited by. +- **Authoring mode** — writing or revising the committed document itself. Authoring cites each + place by the durable anchor research resolved, frames applicability as a membership criterion, + and removes temporal phrasing. **All four rules** apply. + +## Rule 1: The committed document cites a durable anchor, never a bare line number + +Every code reference that lands in the document names a durable anchor: the exported symbol the +code illustrates (function, constant, interface, type) or, for a documentation reference, a +stable section heading. A bare line number must never appear as a citation in the committed +document. The only allowed exception is if there's an established house style for citing code +references; even then, an anchorless reference is not permitted: ensure that there's something +greppable for when line numbers shift. + +A documentation-heading anchor is durably better than a line number but is not fully +rename-proof: a heading rename is deliberate but not compiler-visible, so it carries a residual +silent-break risk a symbol rename does not. Prefer a symbol anchor when both are available. + +Line numbers are not banned from research. Research mode reads the live code at a file and line +range to find the anchor, and reports that range back so the author can open the exact code and +verify it. The range is a navigation aid that stays in the research findings — it is not pasted +into the committed document. Authoring opens the code at the range, then cites the anchor. + +## Rule 2: Choose the anchor's scope + +Cite the smallest scope that captures the pattern without bundling in substantial unrelated +context. Walk this decision tree for each reference: + +1. **Pick the smallest enclosing named scope that contains the pattern** — a function, class, + module, or, worst case, the file. +2. **Is that scope clean** — is its single responsibility the pattern (see the clean-scope test + below)? + - Yes → cite it by its stable name (file plus symbol, or file plus heading). Done. + - No → go to step 3. +3. **Can one or more stable, greppable, public anchors *within* the scope isolate the relevant part** — + a decorator, a called function, a specific identifier? A single reference may name several + anchors to capture a multi-part pattern. + - Yes → name those in-scope anchors. Done. + - No → go to step 4. +4. **No greppable anchor isolates the relevant part at an acceptable granularity** → flag the example + and escalate to the engineer rather than emitting a coarse or line-number reference. + +A boundary always exists (worst case, the whole file), so "no boundary exists" is never the +escalation trigger. The trigger is "the cleanest available scope still drags in unrelated +context and nothing greppable narrows it." + +### Clean-scope test (quantifying steps 2 and 3) + +A named construct is clean — citable as-is — when the pattern is its evident, primary subject: +a reader who opens it to learn the pattern finds the pattern occupies most of it, not one +concern among several. Narrow further when any tripwire fires: + +- **Whole file or whole module is a red flag *when it stands in for a construct inside it*.** + When the reference illustrates a pattern that lives inside a file or module, that scope is an + arbitrary, growth-prone container — its current small size never justifies it — so narrow to + the named construct(s) that embody the pattern. A whole file or module is a legitimate anchor + only when it is *itself* the referent, in two cases: (a) the rule governs the file or + module as a unit (file naming, directory or file structure), so the file or path is exactly + what the rule is about; (b) the reference is a whole-surface pointer rather than a pattern + illustration — "the canonical module or API for working with X" — where the cohesive module is + the durable anchor and there is no finer construct to point at because the module's role is the + point. The discriminator: is the scope itself the referent, or a container for a referent + inside it? +- **Mixed responsibility.** The construct does several unrelated things and the pattern is only + one — narrow to the specific identifier(s) for the pattern. +- **Minority occupancy.** The part the reference is actually about is a small fraction of the + construct, so a reader would scroll past substantial unrelated code to reach it — narrow to a + greppable anchor for that part. + +There is no fixed line count. Cohesion to the pattern, not length, is the test: a long +single-responsibility function may be citable whole, while a short multi-purpose one should be +narrowed. Size is a smell that triggers this check, not a threshold. Calibration: the cited +scope should be about "the illustrative snippet the reference highlights plus its immediate +named home." If it is much larger than the snippet the reference is actually about, narrow. + +### Worked examples + +- **Coarse → precise.** "Module A registers hooks, discovered by module B" — citing whole files + A and B bundles unrelated behavior. Narrow to the in-scope anchors: "A: hooks `H1` and `H2` + registered via the `@register` decorator; B: `get_hooks`." +- **Behavior-paired reference.** "`payments/refund.ts`, function `issueRefund` — see how it + uses `onTransactionSuccess` to emit the `refund.issued` event only after the refund row is + durably committed to the database." + +## Rule 3: State applicability as a membership criterion, not a roster + +**Authoring mode only — research has no applicability text to apply this to.** Write who the +document applies to as a membership criterion — the property that defines the set ("any module +that does X", "any class that has trait Y") — not a snapshot roster of the current members. +This governs the `Applies To` metadata and any inline applicability statement. A roster pins +the document to a momentary codebase state the same way a line number pins it to a momentary +file layout; the criterion is what actually defines who the rule governs and does not need updating +as members change. + +A concrete list may remain only when its named examples are structurally distinct — they cover +different case shapes — not when they are simply the complete current set named as a sample. +Mark any surviving list non-exhaustive ("e.g., …") and strip the temporal word from it. + +## Rule 4: Remove temporal phrasing or references to "current state" + +**Authoring mode only — research produces no committed prose to clean.** Remove snapshot-in-time +phrasing that pins the document to "right now." The words "today", "currently", "now", +"existing", "as of this writing", "the current set of", "already", "still", "pre-existing", +"legacy", "older", "no longer", "so far", "for now", and "in this version" are illustrative examples +of it, not an exhaustive list — the rule is that any phrasing pinning the document to a momentary +roster or moment is removed. Lead with the timeless criterion (Rule 3) instead. + +Two idioms produce most leaks; check for both explicitly, because both read as natural prose and +slip past a word scan: + +- **The known-offender aside.** A standard often points at a real violating construct as its + "What to avoid" example or migration target, then narrates it by its momentary backlog status — + "one pre-existing item … tracked for rework". The durable-anchor citation is correct (Rule 1); + the status narration is the pin. State the timeless property of the construct instead: cite it + by its anchor and say what it *is* ("`X` does Y, which violates this standard"), not where it sits + on a cleanup queue. Whether it has been fixed yet is not the standard's concern. +- **The roadmap or version reference.** A coding standard states a rule that holds across every + version and rollout phase; it is not itself versioned. A roadmap or version state must not enter + the standard at all — neither restated inline ("for now we only ship X") nor used to condition + the rule ("until phase 2, do Y"). Strip it and state the rule unconditionally. Linking a durable + decision doc or ADR to explain *why the rule exists* is fine and durable; letting + the *current rollout state* that doc describes appear in or condition the rule is the pin. diff --git a/han-coding/skills/coding-standard/references/template.md b/han-coding/skills/coding-standard/references/template.md index ea0c1ef..878c029 100644 --- a/han-coding/skills/coding-standard/references/template.md +++ b/han-coding/skills/coding-standard/references/template.md @@ -80,7 +80,8 @@ paths: ``` **Project references:** -- `path/to/implementation` — {brief note on what this file demonstrates} + +- `path/to/file`, `{stable anchor}` — {brief note on what this example demonstrates} ### {Cross-Cutting Guideline Name} @@ -102,8 +103,8 @@ paths: ... **Project references:** -- `path/to/implementation-a` — {brief note} -- `path/to/implementation-b` — {brief note} +- `path/to/file-a`, `{stable anchor}` — {brief note on what this example demonstrates} +- `path/to/file-b`, `{stable anchor}` — {brief note on what this example demonstrates} ## Additional Resources