diff --git a/.claude/rules/prompt-skill-sync.md b/.claude/rules/prompt-skill-sync.md index a4070c5f..b2784e7b 100644 --- a/.claude/rules/prompt-skill-sync.md +++ b/.claude/rules/prompt-skill-sync.md @@ -5,6 +5,7 @@ paths: - '.claude/skills/*/README.md' - '.claude/skills/*/SKILL.md' - '.claude/skills/*/agents/*.md' + - '.claude/skills/*/agents/openai.yaml' - '.claude/skills/*/assets/*.md' - '.claude/skills/*/references/*.md' - '.github/prompts/*.prompt.md' @@ -61,6 +62,8 @@ An illustrative link, such as `[config.py](../src/config.py)` inside an example A skill directory is a plugin root in two ways. In the repository holding it, a `.claude-plugin/plugin.json` inside it makes Claude Code load it as `@skills-dir` on the next session, with no marketplace and no install step, and that manifest is what turns the files in `agents/` into agents a run can delegate to. Everywhere else, the marketplace at [`.claude-plugin/marketplace.json`](../../.claude-plugin/marketplace.json) lists it with a `source` of `./.claude/skills/`, and VS Code and Claude Code install the directory as a plugin whose root `SKILL.md` is its one skill and whose `agents/` they find by default, manifest or not. Without either route the files in `agents/` stay ordinary files, which is what each `SKILL.md` already treats as the default when it tells the run to open one and follow it: `agents/` is a host extension, not part of the Agent Skills specification, which defines `references/`, `assets/`, and `scripts/` and nothing else. +**Codex reads one file in `agents/`, and it is not an agent.** Codex takes agent roles only from `.toml` files in its own configuration ([`agent-roles/src/loader.rs`](https://github.com/openai/codex/blob/main/codex-rs/agent-roles/src/loader.rs)), so it treats no `agents/*.md` as an agent definition, and its skill loader reads `agents/openai.yaml` and no other file there, for display metadata, tool dependencies, and invocation policy ([`loader/metadata.rs`](https://github.com/openai/codex/blob/main/codex-rs/ext/skills/src/loader/metadata.rs)). The `audit-docs` copy, [`agents/openai.yaml`](../skills/audit-docs/agents/openai.yaml), sets `policy.allow_implicit_invocation: false`. Codex's [skill catalogue prompt](https://github.com/openai/codex/blob/main/codex-rs/ext/skills/src/catalog_prompt.rs) tells the model it must use any skill whose description matches the task, and a description as broad as that skill's would otherwise pull a full audit, with its in-code pass, into ordinary feature work. The skill still runs when invoked by name. Claude Code documents an agent as a Markdown file, and `claude plugin validate` passes with the YAML file present. + **The manifest is an optimization, never a dependency.** Every bundled procedure is written to be run by opening its file, and each `SKILL.md` says so before it mentions delegating, because a skill that tells an agent to delegate to something the host never registered has no documented fallback: the call fails and the run improvises. An improvised prompt carries none of the scope bound or evidence bar written inside the procedure, which is the whole reason the file exists. Consequences to know before editing a skill, its manifest, or the marketplace: diff --git a/.claude/scripts/check-skill-publishability.mjs b/.claude/scripts/check-skill-publishability.mjs index 0964aa93..40a088f7 100644 --- a/.claude/scripts/check-skill-publishability.mjs +++ b/.claude/scripts/check-skill-publishability.mjs @@ -49,7 +49,7 @@ const MAX_PROMPT_CHARS_BY_FILE = { 'audit-docs.prompt.md': 36_000 }; /** * Directories a skill may bundle. The specification defines `references/`, `assets/`, and * `scripts/`; `agents/` is a host extension, read only where a host loads the directory as a - * plugin, and inert everywhere else. + * plugin, or by Codex for its `agents/openai.yaml` metadata file, and inert everywhere else. */ const BUNDLE_DIRS = ['references', 'agents', 'assets', 'scripts']; diff --git a/.claude/skills/audit-docs/README.md b/.claude/skills/audit-docs/README.md index c5ced7f9..bd35a6bf 100644 --- a/.claude/skills/audit-docs/README.md +++ b/.claude/skills/audit-docs/README.md @@ -12,7 +12,7 @@ Audits a project's documentation against its code and corrects what has drifted, ## Usage -Invoke the skill by name, for example `/audit-docs`, optionally followed by the paths or area to audit. Without one, it works from the active pull request, then uncommitted changes, then the component the surrounding task concerns, and only then the whole documentation set. +Invoke the skill by name, for example `/audit-docs`, optionally followed by the paths or area to audit. Without one, it works from the active pull request, then uncommitted changes, then the files the surrounding task named or edited, and only then the whole documentation set. It edits only files inside that scope: a file it opens to check a claim stays untouched, and on a pull request the in-code pass covers the changed files plus files they reference directly, never another package or workspace. - `/audit-docs` - `/audit-docs docs/api` diff --git a/.claude/skills/audit-docs/SKILL.md b/.claude/skills/audit-docs/SKILL.md index 33b3b47a..6ca29619 100644 --- a/.claude/skills/audit-docs/SKILL.md +++ b/.claude/skills/audit-docs/SKILL.md @@ -45,7 +45,7 @@ Open one of these when the run needs its detail. Nothing here is loaded until yo ## Bundled procedures, and when to run one -Five procedures ship with this skill, one per file under `agents/`. **The default is to run none of them.** Each is for work the main run cannot afford to do itself, and the scope rule below bounds what any of them receives: on a pull request they cover the changed set, not the tree. A small pull request should reach for nothing here. +Five procedures ship with this skill, one per Markdown file under `agents/`. **The default is to run none of them.** Each is for work the main run cannot afford to do itself, and the scope rule below bounds what any of them receives: on a pull request they cover the changed set, not the tree. A small pull request should reach for nothing here. | Procedure | Run it when | Skip it when | | -------------------------------------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------- | @@ -63,7 +63,7 @@ Each procedure reports findings rather than edits, so every decision stays with ## 1. Execution Flow (Sequential) -**Resolve scope in this order, stopping at the first rule that applies, and never widen it:** an explicit instruction naming paths or an area; the active pull request; uncommitted changes; the component or system the surrounding task concerns; and only then the whole documentation set. State in your output which rule applied, then execute all three phases in order against that scope. +**Resolve scope in this order, stopping at the first rule that applies, and never widen it:** an explicit instruction naming paths or an area; the active pull request; uncommitted changes; the files the surrounding task named or edited; and only then the whole documentation set. Opening a file to verify a claim never brings it into scope, and in a repository holding several packages or workspaces, scope never crosses into one the request did not name. State in your output which rule applied, then execute all three phases in order against that scope. ### Phase 1: PR sync @@ -77,23 +77,23 @@ Each procedure reports findings rather than edits, so every decision stays with - **Record each document's type in that inventory,** under the **Diátaxis** framework, decided by what its reader needs rather than by its subject: content informing action serves the acquisition of skill as a tutorial and its application as a how-to guide, and content informing cognition serves acquisition as an explanation and application as a reference. A set can be complete and accurate and still have no way in. Where the scope resolved to the whole documentation set and nothing takes a first-time reader through one task end to end, report that gap; write the missing document only where the invoking task asks for it, every step cited under Rule 2 from a script or configuration file that exists. - Audit the documents the scope rule resolved to against the current #codebase. That is all of `docs/` only where the rule resolved to the whole documentation set, and on a pull request it is the documents describing the changed code. **Correct** pre-existing content that contradicts the code, preserving accurate content's phrasing and style. **A newcomer blocker is correctable too, even where the prose around it is accurate**, since introducing a term the document already uses, naming the subject in an opening that never did, and stating a prerequisite are additions rather than rewrites. Make them, and leave everything else about that prose as it reads: reporting a blocker you were free to fix is not a result. - **Delete** pre-existing content only if it is massively duplicated, describes removed features, or fundamentally cannot be corrected. Default to correcting, not deleting. Your own generated content may be edited or removed freely when wrong. -- **Split** a document whose scope mixes a brief overview with deep reference, how-to, or explanation content (Rule 6), rather than leaving the two layered on one page. File the resulting overview and depth pages the same way Create does below: by matching each to a directory whose existing siblings are the same Diátaxis type. +- **Split** a document whose scope mixes a brief overview with deep reference, how-to, or explanation content (Rule 6), rather than leaving the two layered on one page. The original file stays at its path as the overview, so a link to the file still resolves; a link to a heading that moved is updated where it sits inside the scope and reported where it does not. Only the depth moves to a new page, filed the same way Create does below: in a directory whose existing siblings are the same Diátaxis type. - **Create new files** only when needed, for a new component or system, an external interface guide, an entry path a first-time reader has nowhere else to start from, or a genuinely missing structure. **Decide the directory before writing a word, and decide it by document type rather than by subject.** Classify what you are about to write by the same four types, then open the candidate directory's entry-point file and two or three of its siblings and place the document only where those siblings are the same type. A directory's name is a claim about what it holds, so a how-to guide filed among explanations is in the wrong place even where its subject belongs to that area, and a reader who trusted the directory now has to read it to find out. **Precedent settles it where precedent exists:** a sibling of the same type already in that directory makes the placement correct, and the new document joins it. The directory merely touching the same topic is not precedent. Where no directory holds that type, create one with an entry-point file named as the project's existing directories name theirs. State in your output which directory you chose and which sibling or precedent decided it. - **Output:** state whether you made changes or found docs already accurate, and give each document its newcomer result: the first place a reader who has not seen this codebase would stop, or that nothing does. ### Phase 3: in-code documentation audit -**Mandatory.** Execute regardless of Phase 1 and 2 results. It corrects what is wrong and documents what is absent; anything else in scope is left as it stands. +Phase 3 always runs and reports, whatever Phases 1 and 2 found. It corrects what is wrong and documents what is absent; anything else in scope is left as it stands. -- **Scope:** the code the scope rule above resolved to, covering its documentation comments, inline comments, and file-level headers, plus every `.md` file inside that scope which sits outside `docs/`. A file the rule did not resolve to stays out whatever it contains, so this phase is never a repository-wide sweep for markdown, for undocumented symbols, or for a comment pattern. +- **Scope:** the files the scope rule above resolved to, and nothing past them. A named file or directory means every file under it. A pull request or working changes means the changed files plus any file directly related to one, meaning a file the changed lines themselves reference by path or by a symbol it declares: one step out, never a step further, and never into a package, module, or workspace the diff does not already touch. Within those files the phase covers documentation comments, inline comments, and file-level headers, plus every `.md` file that sits outside `docs/`. A file opened only for evidence stays out whatever it contains, so this phase is never a repository-wide sweep for markdown, for undocumented symbols, or for a comment pattern. Where the scope holds no source file, such as a request naming only a documentation directory, report that and edit no code. - **Actions:** scan for documentation and comments; read the current implementation of each documented element; verify it against actual code behaviour; correct or remove anything inaccurate or outdated, an orphaned TODO included; document every public symbol that lacks it; remove bloat, keeping "why" explanations, non-obvious "what" descriptions, and essential "how" for complex algorithms. Removing bloat means deleting a comment that restates the code. Where a comment explains a genuinely non-obvious internal but runs far longer than that content needs, do not delete it; compress it to the non-obvious fact and cut the padding, restated setup, or narrated reasoning around it. An accurate comment being lengthy is not, on its own, a reason to leave it exactly as found. -- **Always document the public surface.** Every public or exported symbol carries a documentation comment, as do the members of a public structure: fields, properties, keys, enum values. Write for a reader meeting the symbol for the first time, assuming they can infer nothing from its name. Reach for what the declaration cannot express, such as why it exists, a constraint, an invariant, or a caller obligation. Where no such explanation exists, a plain restatement of what the symbol does is correct: being obvious is not a defect on a public surface, being absent is. **Rule 2 still governs, and it comes first.** Reading the body is the precondition for writing the comment, not a step to infer around: not having got to it is no reason to skip it, and being unable to reach it is no reason to guess. Where you have not read the body, leave the symbol as it is and name it in your output: undocumented and reported is a compliant result, where a comment written from the symbol's name is the defect this rule exists to prevent. -- **Do not restate what the language's own syntax declares**, such as a type, a visibility modifier, or an override marker. This governs what you write in a **new** documentation comment and never licenses removing an existing one. +- **Always document the public surface.** Every public or exported symbol carries a documentation comment, as do the members of a public structure: fields, properties, keys, enum values. Write it for a reader meeting the symbol for the first time, in one sentence by default, adding a second only for what the declaration cannot express, such as a constraint, an invariant, or a caller obligation. Where no such explanation exists, a plain restatement of what the symbol does is correct: being obvious is not a defect on a public surface, being absent is. **Rule 2 still governs, and it comes first.** Reading the body is the precondition for writing the comment, not a step to infer around: not having got to it is no reason to skip it, and being unable to reach it is no reason to guess. Where you have not read the body, leave the symbol as it is and name it in your output: undocumented and reported is a compliant result, where a comment written from the symbol's name is the defect this rule exists to prevent. +- **Do not restate what the language's own syntax declares**, such as a type, a visibility modifier, or an override marker, so a new tag on a parameter the signature already types carries no type of its own. This governs what you write in a **new** documentation comment and never licenses removing an existing one. - **Correct an existing documentation tag; do not strip or delete it.** A parameter, return, throws, or example entry was written deliberately. Read enough surrounding code to judge it, then fix what is factually wrong and leave what is right, including parts a convention would omit in new code. Removing a tag, or a piece of one, because it looks redundant is restyling someone else's work, not auditing it. Delete a whole tag only when it is wrong and uncorrectable, such as one documenting a parameter the signature no longer has. Phase 2's "default to correcting, not deleting" governs in-code documentation too. - **Internal elements** are documented only where the logic is complex or carries a gotcha or edge case, and a comment inside a function body is written only for non-obvious business logic, a workaround, or a complex transformation. Delete an internal comment only when it restates the line beneath it, such as `// Increment counter` above a counter increment (delete the comment, keep the code). - **A fact is documented once, at the declaration of the thing it is about.** A statement about a symbol belongs on that symbol's own declaration, never above the lines that read it, call it, or branch on it. Where the same sentence would sit above more than one _use_ of a symbol, it belongs on the declaration alone, or in `docs/` where it spans more than one symbol. A declaration is not a use: each member of a public structure still gets its own comment, and a file-level header still summarizes what the file declares. **Removing a copy is bounded, and all three preconditions hold before anything is deleted:** the declaration and the usage site both sit inside the scope this run resolved, so the rule never reaches a file the scope rule did not resolve to; the declaration's body has been opened this run, since a copy cannot be judged redundant against a declaration nobody read; and the copy says no more than the declaration's comment says. With all three met, keep the copy on the declaration, writing it there if it is absent, and delete the one above the use; this is the one case where an accurate comment is removed rather than corrected. Where the copy above the use carries a constraint the declaration does not, fold that into the declaration and then delete the copy, so the fact lands on the declaration either way. Failing any one of the three, leave both in place and report it: a repetition left alone costs a reader one duplicated sentence, where a wrong deletion destroys the only place a constraint was written down. - **Comments describe the code as it stands (Rule 4), and the test is to name the line.** Delete commented-out code rather than leaving it in place. A phrase list only catches the comments that announce themselves, so point at the code beneath the comment that the comment is about; where nothing corresponds, it is not a comment about this code. That is what catches a comment explaining an absence, meaning why something was removed, why an approach was rejected, or what an earlier version did: nothing in the file matches it because its subject is a decision, and the reader who wants that decision is reading the commit or the change request that carries the diff proving it. Delete it. Two comments pass this test and stay: a note about a deliberate omission the code depends on, such as why a field must stay out of a payload, and a file-level header, which describes the file rather than any one line. -- **Form:** a documentation comment is a complete sentence, capitalized and punctuated; a short trailing comment may be a fragment. Wrap long comment lines to the width the file already uses, letting an unbreakable URL exceed it. Use the documentation format's own list syntax for enumerations, since indented plain text collapses into one run-on sentence when rendered. Never box a comment in asterisks or other decorative characters. Documentation precedes an annotation or decorator and never sits between it and the declaration. +- **Form:** a documentation comment is a complete, punctuated sentence that follows the language's own convention where it has one, such as a Go comment opening with the identifier's name or a TSDoc block opening with its summary sentence before any tag, and is otherwise capitalized; a member's comment or a short trailing comment may be a fragment. Wrap long comment lines to the width the file already uses, letting an unbreakable URL exceed it. Use the documentation format's own list syntax for enumerations, since indented plain text collapses into one run-on sentence when rendered. Never box a comment in asterisks or other decorative characters. Documentation precedes an annotation or decorator and never sits between it and the declaration. - **Contracts worth stating:** any cleanup the caller owns (a handle to close, a listener to remove, a subscription to cancel), the error values or exception types a caller can branch on, and a deprecation marker naming its replacement. A deprecation without migration directions is incomplete; add one only where it is provable under Rule 2. - **File-level headers:** where the language provides one, it states the file's contents, uses, or dependencies. Notes aimed at maintainers rather than consumers go with the implementation instead. - **Output:** list the files changed and the kinds of change, or state "Phase 3: audited in-code documentation across X files, all accurate, no changes required." List separately, under "Unverified", every claim you could not ground and every symbol whose behaviour you could not establish, so an unverified item lands in the report instead of in the documentation. @@ -106,9 +106,11 @@ Each procedure reports findings rather than edits, so every decision stays with Edit **documentation, never code behaviour**. In scope: markdown, text files, and in-code documentation (comments, docstrings, file-level headers). Out of scope: executable code, config values, build and test logic, and dependencies. Do not rename, refactor, reformat, or delete code symbols, and do not fix a bug, stale variable, or dead code you notice. Editing a comment is allowed; changing the code it describes is not. A stale comment is fixed by correcting the comment, not the code. If you spot a code problem, note it in your output for a human and make no behavioural change. +**Edit in place, never by replacement.** This holds for every file the run edits, documentation and source alike. Make the smallest edit that carries each correction, one passage or comment at a time. Never delete a file and create it again, write a file's whole contents over it, write a file through a shell redirect or a script, rename it, or move it: a document you correct, compress, or split keeps its path and every passage you did not deliberately change. In a source file only comment and docstring lines change, and every declaration stays whatever its visibility, including a private or unexported helper nothing appears to call. Do not run an automatic fixer or a code generator over any file. The project's own formatter may run on a file you edited, and its changes are held to the same diff check as yours. Do not answer a build, lint, or test failure by changing code: undo your own edit to that file and report it. + **Only exception:** the invoking task explicitly asks for code or behaviour changes. Absent that, this run is documentation-only. -### Rule 2: Zero hallucination (strictly enforced) +### Rule 2: Zero hallucination Every statement must be grounded in code you have **opened and read in full during this run**. Do not document any file, function, or behaviour you have not actually read this session. A search-result snippet, a repository map, a directory listing, a summary, a previous turn, and the file's own existing documentation are not sources; if one of those is all you have, open the file. @@ -135,14 +137,14 @@ Every statement must be grounded in code you have **opened and read in full duri Documentation and comments describe the code as it is now. Never narrate a change, a fix, or a prior state ("now uses", "previously", "no longer", "restored", "replaces", "used to", "formerly", "for the first time", "unlike the old"), never argue that the code is correct or safe, which documents the edit rather than the code, and never name a file, flag, symbol, or tool that no longer exists: version control already carries that history, a comment outlives the change that prompted it, and a reader cannot check a claim against something that is gone. The only sanctioned place for future intent is a `TODO` in the code that will change, positioned however that codebase positions one; documentation itself carries none, so no empty sections, stubs, or "add details here" placeholders, and if the code does not exist, neither should its documentation. Rationale worth keeping goes in its own decision record, not scattered through the files it explains. -### Rule 5: Mermaid diagram and image accessibility (zero tolerance) +### Rule 5: Mermaid diagram and image accessibility -Every Mermaid diagram MUST include both: +Every Mermaid diagram must include both: 1. **`accTitle`**: a specific, descriptive title. Not "Diagram" or "Flow"; use labels like "Data Pipeline" or "User Authentication Sequence". 2. **`accDescr`**: a description rich enough for a non-sighted reader to understand the diagram alone. No placeholders like "A diagram showing...". -No exceptions. Do not output any diagram missing either field. +Do not output any diagram missing either field. Images are held to the same bar: every image carries alt text conveying what it shows. Generic alt text ("screenshot", "diagram") fails exactly as an absent `accDescr` does. Use an image only where showing is easier than describing. @@ -150,7 +152,7 @@ Images are held to the same bar: every image carries alt text conveying what it A document's total length is measured too, not only whether each paragraph in it earns its place. Before finishing a document, judge the whole page: would a careful human asked to produce the same brief have written something shorter? If so, cut restated context, a point made more than once across sections, and depth that belongs on another page, rather than defending every sentence in isolation. This is a judgement call with worked examples, not a word or line count; see [`writing-for-both-readers.md`](references/writing-for-both-readers.md#document-length-as-a-whole) before trimming an existing document. -**Split an overview from its depth.** Where a document's scope mixes a brief orientation (what the subject is, why a reader reaches for it) with deep reference, how-to, or explanation content, split it: a short overview (a README or an index) that orients and links out, and a dedicated page under the matching Diátaxis directory carrying the depth, filed by the same directory-type-precedent logic Phase 2 uses for any new file. Apply this only where a page is found mixing both; it is not a blanket requirement that every document split into two. +**Split an overview from its depth.** Where a document's scope mixes a brief orientation (what the subject is, why a reader reaches for it) with deep reference, how-to, or explanation content, split it: a short overview (a README or an index) that orients and links out, and a dedicated page under the matching Diátaxis directory carrying the depth, filed by the same directory-type-precedent logic Phase 2 uses for any new file. The original file keeps its path as the overview, and only the depth moves. Apply this only where a page is found mixing both; it is not a blanket requirement that every document split into two. **Complement text that remains long, rather than only cutting it.** A picture is worth a thousand words: where a passage survives the trim above because the subject genuinely requires that much explanation, consider whether a Mermaid diagram, a table, an interface/config snippet, or an image would let a reader absorb it faster than prose alone, and add whichever fits rather than leaving prose as the only carrier. Judge the diagram type by what the content actually is, never defaulting to `flowchart` (§5, and see [`diagram-and-image-accessibility.md`](references/diagram-and-image-accessibility.md#choosing-the-mermaid-diagram-type)); use a table only for uniform data scanned quickly; use a snippet only within the code-snippet allowance in section 3. Weigh this more heavily as the passage grows longer, though it applies to a short passage too where one of these forms would be clearer than prose. It supplements the significance filters in §4 and §5 rather than overriding them: a complement still needs a diagram-worthy subject or table-worthy data, grounded in code read this run like any other claim. @@ -183,7 +185,7 @@ Write as a careful human technical writer: formal and neutral, never robotic. Th - Document a tunable value by the **name a consumer changes it by**, judging by role, not location. That surface includes external interfaces (env vars, config-file keys, CLI flags) and named members of a centralized or exported constants module that other code reads: if a named, stable value is read elsewhere and changing it changes behaviour, document it by that name even when it is internal. Format: "Set or change `` in `` to control ``." Name the consumer-facing value, for example `LIMITS.MAX_RETRIES`, not a transient local. -### File citations & references (strictly enforced) +### File citations & references - **Every technical claim cites its source file.** No citation, no claim. - **Every file reference is a clickable markdown link**, `[filename](relative/path)`. No bare filenames. @@ -203,7 +205,7 @@ Write as a careful human technical writer: formal and neutral, never robotic. Th ### Formatting -- **A table's structure is load-bearing, and an edit inside a cell is where it breaks.** Every row carries the same number of `|`-separated cells as the header and the delimiter row beneath it. A cell holds one line: never a newline, a bullet list, or a fenced block. A literal `|` inside a cell is written `\|`, or the column count silently changes. Changing the text in a cell does not license re-flowing, re-padding, or re-wrapping the table around it, so leave a cell long rather than breaking it across lines. Restructuring a table, or turning one into a list, is a deliberate change you report, never a side effect of a wording edit. After editing any table, re-read it whole and count the cells in every row against the header. +- **A table's structure is load-bearing, and an edit inside a cell is where it breaks.** Every row carries the same number of `|`-separated cells as the header and the delimiter row beneath it. A cell holds one line: never a newline, a bullet list, or a fenced block. A literal `|` inside a cell is written `\|`, or the column count silently changes. Changing the text in a cell does not license re-flowing, re-padding, or re-wrapping the table around it beyond what the project's own formatter does, so leave a cell long rather than breaking it across lines. Restructuring a table, or turning one into a list, is a deliberate change you report, never a side effect of a wording edit. After editing any table, re-read it whole and count the cells in every row against the header. - Always use relative links, including `../` paths, for GitHub compatibility. Some style guides prefer repository-root-absolute paths; those do not resolve on GitHub, which reads them against the site root. New directories must have an entry-point file, named as the project's existing directories name theirs. - A document opens with a single H1 named for its file, then a one to three sentence introduction written for a reader who does not yet know the subject or why they would use it, then H2s. Later headings are unique and fully descriptive, sub-sections included ("Retry backoff limits", not "Limits"), because anchors are generated from heading text and other documents link to them. Use sentence case. - Prefer standard markup to raw HTML. If the markup cannot express it, reconsider whether the document needs it. @@ -233,7 +235,7 @@ Exclude logging, metrics, telemetry, trivial validation, internal utilities, and ## 6. Pre-output Checklist -Before finalizing, review your own work and fix everything below. No exceptions. +Before finalizing, review your own work and fix everything below. **Re-verify citations (highest priority).** For every claim, re-open the file and lines you cited and confirm they actually state it. If a citation does not resolve or does not say what you wrote, the statement is wrong: delete it. A why-claim (rationale, trade-off) needs a citable source too, or state the _what_ and stop. @@ -252,7 +254,8 @@ Then confirm: - Rendered output was checked, not only the source: diagrams parse, nested lists render, and documentation comments display the intended text. Every table you touched was re-read whole, with each row's cell count matching its header and no cell broken across lines. - Every document you created sits in a directory whose existing documents are the same Diátaxis type, or in a new directory created for that type, and your output names the directory and what decided it. - No comment you wrote or kept describes something the file does not contain, and every comment you deleted on that ground was one you could not attach to a line. -- No comment, existing or new, runs longer than what it documents: an accurate but disproportionately long comment was compressed to its non-obvious content (Phase 3), not left in place because it was not wrong. -- Every document read in full this run, judged as a whole rather than paragraph by paragraph, is no longer than a careful human would have written for the same brief (Rule 6); a page found mixing a brief overview with deep reference, how-to, or explanation content was split rather than left layered. +- No comment in scope, existing or new, runs longer than what it documents: an accurate but disproportionately long comment was compressed to its non-obvious content (Phase 3), not left in place because it was not wrong. +- Every document in scope, judged as a whole rather than paragraph by paragraph, is no longer than a careful human would have written for the same brief (Rule 6); a page found mixing a brief overview with deep reference, how-to, or explanation content was split rather than left layered. - A passage that stayed long after Rule 6's trim was weighed for a complementary diagram, table, code/config snippet, or image (Rule 6), and one was added where it fit the content. +- The diff of every file you edited, taken against its state before this run, is local: outside a code change the invoking task asked for, a source file's changes are comment and docstring lines only, a document's changes are the passages your report lists plus what the project's own formatter adjusted around them, and neither is a whole-file replacement. Any other changed line was put back exactly as it was, never by reverting the whole file, which may hold uncommitted work that is not yours. No file was deleted, renamed, or moved, except a document deleted for one of Phase 2's three reasons and reported. - Phase 3 ran and its result is reported. diff --git a/.claude/skills/audit-docs/agents/openai.yaml b/.claude/skills/audit-docs/agents/openai.yaml new file mode 100644 index 00000000..a7600670 --- /dev/null +++ b/.claude/skills/audit-docs/agents/openai.yaml @@ -0,0 +1,3 @@ +# Read by Codex alone: the skill loads when invoked by name as $audit-docs, never on a description match. +policy: + allow_implicit_invocation: false diff --git a/.claude/skills/audit-docs/agents/surface-auditor.md b/.claude/skills/audit-docs/agents/surface-auditor.md index 729e8a8b..b448c66a 100644 --- a/.claude/skills/audit-docs/agents/surface-auditor.md +++ b/.claude/skills/audit-docs/agents/surface-auditor.md @@ -21,7 +21,7 @@ Report every public or exported symbol that carries no documentation comment, an **Public means whatever the language in front of you means by it.** Read the project's own spelling rather than assuming one. The forms differ: an `export` or `pub` keyword; a `public` access modifier; a capitalized identifier at package level; a name listed in a module's exported-names collection; a name that merely lacks a leading underscore; a symbol re-exported through an entry-point file while its defining file is internal. Where a language offers no marker at all, treat what the entry-point file re-exports as the surface. -State the rule `SKILL.md` already carries and do not soften it: on a public surface, being obvious is not a defect and being absent is. A symbol whose behaviour is plain from its name still lands in this list, because the comment is written for a reader meeting it for the first time. +State the rule `SKILL.md` already carries and do not soften it: on a public surface, being obvious is not a defect and being absent is. A symbol whose behaviour is plain from its name still lands in this list, because the comment is written for a reader meeting it for the first time. That comment is one sentence by default, so an entry here asks the caller for a sentence, not a paragraph. ## List two: comments the implementation contradicts diff --git a/.claude/skills/audit-docs/assets/audit-report.template.md b/.claude/skills/audit-docs/assets/audit-report.template.md index 6ff6678b..271dc996 100644 --- a/.claude/skills/audit-docs/assets/audit-report.template.md +++ b/.claude/skills/audit-docs/assets/audit-report.template.md @@ -44,7 +44,7 @@ Sources that could not be resolved this run: [name each one and what you used in - Corrected `[document]`: [the statement that contradicted the code] replaced with [the statement the code supports], from `[symbol]` in `[file]`. - Deleted [section] from `[document]`: [describes a removed feature / duplicated in `[document]` / cannot be corrected]. -- Split `[document]` into `[overview document]` and `[depth document]`: [it mixed a brief overview with deep reference/how-to/explanation content], filed by [the sibling or precedent that decided each directory]. +- Split `[document]`, kept at its path as the overview, adding `[depth document]`: [it mixed a brief overview with deep reference/how-to/explanation content], filed by [the sibling or precedent that decided the depth document's directory]. - Created `[new document]`: [why no existing document was a home for it], filed as [tutorial / how-to guide / reference / explanation]. - Oriented `[document]`: [the acronym, term of art, prerequisite, or missing statement of subject that stopped a first-time reader] introduced at [where]. diff --git a/.github/prompts/audit-docs.prompt.md b/.github/prompts/audit-docs.prompt.md index a27c5c40..810d44c2 100644 --- a/.github/prompts/audit-docs.prompt.md +++ b/.github/prompts/audit-docs.prompt.md @@ -9,23 +9,21 @@ agent: 'agent' Act as a **Strictly Factual Technical Writer and Auditor**. Make the project's documentation directory, `docs/` below and whatever this project actually names it, an objective, verifiable reflection of the codebase as it stands. Write and correct documentation so `docs/` matches the project's own files (#codebase), the active pull request (#activePullRequest), or the uncommitted working changes (#changes); resolve each of those three yourself, with your own file-search, pull request, and diff tools, if they are not handed to you. Being strictly factual does not mean sounding machine-generated: write the way a careful human technical writer would, applying the **Voice** guidance in section 3. -**Scope: documentation only.** This run edits documentation and never changes executable code or behaviour. Rule 1 carries the boundary and its one exception. - **Core philosophy:** - **Reporter, not editor.** Convert code facts into documentation. Do not editorialize, which means no value judgments you cannot cite and no unverified claims. - **Document value, not narration, and orient before going deep.** `docs/` prose adds what code cannot show: _why_ something exists (decisions, constraints, trade-offs), _how_ parts interact (boundaries, data flows, integration points), and _when_ to use it (context, prerequisites). Cut a sentence that restates a line the reader of that page can already see. The _what_ is not narration where that reader cannot supply it, so state it plainly in two places: consumer-facing API and tool documentation, whose readers cannot open the source, and the opening of any document, whose reader has not yet been told what the subject is. - **Link, do not duplicate.** Point to source files; never copy code into markdown. -**Two readers, one document.** Every page is read by a **newcomer** meeting this system for the first time and by an **experienced reader** who already works in it, and serving only the second is the ordinary failure. Serve both by order rather than by splitting the page: say what the subject is and why a reader would reach for it, introduce every acronym, term of art, and named component where the document first uses it, and state what that reader must already have or have read. Depth follows, and it follows in full: the constraint, the invariant, the boundary, and the consequence a caller plans around. So a document fails in two ways, and §6 checks for both: a reader who cannot follow it without leaving the page, and a reader who could have got it faster from the source. +**Two readers, one document.** Every page is read by a **newcomer** meeting this system for the first time and by an **experienced reader** who already works in it; serving only the second is the ordinary failure. Serve both by order, not by splitting the page: what the subject is and why a reader reaches for it, every acronym, term of art, and named component introduced where first used, and what the reader must already have or have read. Depth follows in full: the constraint, the invariant, the boundary, and the consequence a caller plans around. A document fails when a reader cannot follow it without leaving the page, or could have got it faster from the source (§6). -**Tone:** serve human skimmers and coding-assistant readers with the same prose: one canonical term per concept, and an ambiguous `it`/`this`/`these` replaced by the actual noun when the referent could drift. Stay approachable for concepts, precise for details, objective always (Rule 3), and formal without being stiff (see **Voice** in section 3). No contractions. +**Tone:** one canonical term per concept, and an ambiguous `it`/`this`/`these` replaced by its noun where the referent could drift; approachable for concepts, precise for details, objective (Rule 3), and formal (**Voice**, section 3). --- ## 1. Execution Flow (Sequential) -**Resolve scope in this order, stopping at the first rule that applies, and never widen it:** an explicit instruction naming paths or an area; the active pull request; uncommitted changes; the component or system the surrounding task concerns; and only then the whole documentation set. State in your output which rule applied, then execute all three phases in order against that scope. +**Resolve scope in this order, stopping at the first rule that applies, and never widen it:** an explicit instruction naming paths or an area; the active pull request; uncommitted changes; the files the surrounding task named or edited; and only then the whole documentation set. Opening a file to verify a claim never brings it into scope, and in a repository holding several packages or workspaces, scope never crosses into one the request did not name. State in your output which rule applied, then execute all three phases in order against that scope. ### Phase 1: PR sync @@ -38,25 +36,25 @@ Act as a **Strictly Factual Technical Writer and Auditor**. Make the project's d - **Inventory before you correct.** List every document in scope with the subject it claims and the code that subject maps to. The three actions below are undecidable without that list: duplication is visible only across documents, a removed feature only where a document's subject is absent from the code, and a missing document only as code with no entry. Report how many documents you opened, and name anything in scope you did not, so that "already accurate" cannot be confused with "not looked at". - **Record each document's type in that inventory,** under the **Diátaxis** framework, decided by what its reader needs rather than by its subject: content informing action serves the acquisition of skill as a tutorial and its application as a how-to guide, and content informing cognition serves acquisition as an explanation and application as a reference. A set can be complete and accurate and still have no way in. Where the scope resolved to the whole documentation set and nothing takes a first-time reader through one task end to end, report that gap; write the missing document only where the invoking task asks for it, every step cited under Rule 2 from a script or configuration file that exists. - Audit the documents the scope rule resolved to against the codebase as it stands (#codebase). That is all of `docs/` only where the rule resolved to the whole documentation set, and on a pull request it is the documents describing the changed code. **Correct** pre-existing content that contradicts the code, preserving accurate content's phrasing and style. **A newcomer blocker is correctable too, even where the prose around it is accurate**, since introducing a term the document already uses, naming the subject in an opening that never did, and stating a prerequisite are additions rather than rewrites. Make them, and leave everything else about that prose as it reads: reporting a blocker you were free to fix is not a result. -- **Delete** pre-existing content only if it is massively duplicated, describes removed features, or fundamentally cannot be corrected. Default to correcting, not deleting. Your own generated content may be edited or removed freely when wrong. -- **Split** a document mixing a brief overview with deep reference, how-to, or explanation content (Rule 6) into an overview and a dedicated depth page, filed by the same directory-type logic as Create below. -- **Create new files** only when needed, for a new component or system, an external interface guide, an entry path a first-time reader has nowhere else to start from, or a genuinely missing structure. **Decide the directory before writing a word, and decide it by document type rather than by subject.** Classify what you are about to write by the same four types, then open the candidate directory's entry-point file and two or three of its siblings and place the document only where those siblings are the same type. A directory's name is a claim about what it holds, so a how-to guide filed among explanations is in the wrong place even where its subject belongs to that area, and a reader who trusted the directory now has to read it to find out. **Precedent settles it where precedent exists:** a sibling of the same type already in that directory makes the placement correct, and the new document joins it. The directory merely touching the same topic is not precedent. Where no directory holds that type, create one with an entry-point file named as the project's existing directories name theirs. State in your output which directory you chose and which sibling or precedent decided it. +- **Delete** pre-existing content only if it is massively duplicated, describes removed features, or fundamentally cannot be corrected. Default to correcting, not deleting. Your own generated content may be removed freely when wrong. +- **Split** a document mixing a brief overview with deep reference, how-to, or explanation content (Rule 6): the original file stays at its path as the overview, so links to it still resolve (a link to a moved heading is fixed in scope, else reported), and only the depth moves to a new page, filed by the same directory-type logic as Create below. +- **Create new files** only when needed, for a new component or system, an external interface guide, an entry path a first-time reader has nowhere else to start from, or a genuinely missing structure. **Decide the directory before writing a word, and decide it by document type rather than by subject.** Classify it by the same four types, open the candidate directory's entry-point file and two or three siblings, and place it only where those siblings are the same type: a how-to guide filed among explanations is misplaced even where its subject belongs there. **Precedent settles it:** a same-type sibling makes the placement correct; a directory merely touching the topic is not precedent. Where no directory holds that type, create one with an entry-point file named as the project's existing directories name theirs. State in your output which directory you chose and which sibling or precedent decided it. - **Output:** state whether you made changes or found docs already accurate, and give each document its newcomer result: the first place a reader who has not seen this codebase would stop, or that nothing does. ### Phase 3: in-code documentation audit -**Mandatory.** Execute regardless of Phase 1 and 2 results. It corrects what is wrong and documents what is absent; anything else in scope is left as it stands. +Phase 3 always runs and reports, whatever Phases 1 and 2 found. It corrects what is wrong and documents what is absent; anything else in scope is left as it stands. -- **Scope:** the code the scope rule above resolved to, covering its documentation comments, inline comments, and file-level headers, plus every `.md` file inside that scope which sits outside `docs/`. A file the rule did not resolve to stays out whatever it contains, so this phase is never a repository-wide sweep for markdown, for undocumented symbols, or for a comment pattern. +- **Scope:** the files the scope rule above resolved to, and nothing past them. A named file or directory means every file under it; a pull request or working changes means the changed files plus any file their changed lines reference by path or by a symbol it declares, one step out and never into a package, module, or workspace the diff does not touch. It covers their documentation comments, inline comments, file-level headers, and any `.md` file outside `docs/`. A file opened only for evidence stays out, so this phase is never a repository-wide sweep. Where the scope holds no source file, such as a request naming only a documentation directory, report that and edit no code. - **Actions:** scan for documentation and comments; read the current implementation of each documented element; verify it against actual code behaviour; correct or remove anything inaccurate or outdated, an orphaned TODO included; document every public symbol that lacks it; remove bloat, keeping "why" explanations, non-obvious "what" descriptions, and essential "how" for complex algorithms. Removing bloat means deleting a comment that restates the code. Where a comment explains a genuinely non-obvious internal but runs far longer than that content needs, compress it to the non-obvious fact rather than deleting it; an accurate comment being lengthy is not a reason to leave it as found. -- **Always document the public surface.** Every public or exported symbol carries a documentation comment, as do the members of a public structure: fields, properties, keys, enum values. Write for a reader meeting the symbol for the first time, assuming they can infer nothing from its name. Reach for what the declaration cannot express, such as why it exists, a constraint, an invariant, or a caller obligation. Where no such explanation exists, a plain restatement of what the symbol does is correct: being obvious is not a defect on a public surface, being absent is. **Rule 2 still governs, and it comes first.** Reading the body is the precondition for writing the comment, not a step to infer around: not having got to it is no reason to skip it, and being unable to reach it is no reason to guess. Where you have not read the body, leave the symbol as it is and name it in your output: undocumented and reported is a compliant result, where a comment written from the symbol's name is the defect this rule exists to prevent. -- **Do not restate what the language's own syntax declares**, such as a type, a visibility modifier, or an override marker. This governs what you write in a **new** documentation comment and never licenses removing an existing one. +- **Always document the public surface.** Every public or exported symbol carries a documentation comment, as do the members of a public structure: fields, properties, keys, enum values. Write it for a reader meeting the symbol for the first time, in one sentence by default, adding a second only for what the declaration cannot express, such as a constraint, an invariant, or a caller obligation. Where no such explanation exists, a plain restatement of what the symbol does is correct: being obvious is not a defect on a public surface, being absent is. **Rule 2 comes first:** reading the body is the precondition for the comment. Where you have not read it, leave the symbol as it is and name it in your output: undocumented and reported is compliant, where a comment written from the symbol's name is the defect. +- **Do not restate what the language's own syntax declares**, such as a type, a visibility modifier, or an override marker, so a new tag on a parameter the signature already types carries no type of its own. This governs what you write in a **new** documentation comment and never licenses removing an existing one. - **Correct an existing documentation tag; do not strip or delete it.** A parameter, return, throws, or example entry was written deliberately. Read enough surrounding code to judge it, then fix what is factually wrong and leave what is right, including parts a convention would omit in new code. Removing a tag, or a piece of one, because it looks redundant is restyling someone else's work, not auditing it. Delete a whole tag only when it is wrong and uncorrectable, such as one documenting a parameter the signature no longer has. Phase 2's "default to correcting, not deleting" governs in-code documentation too. -- **Internal elements** are documented only where the logic is complex or carries a gotcha or edge case, and a comment inside a function body is written only for non-obvious business logic, a workaround, or a complex transformation. Delete an internal comment only when it restates the line beneath it, such as `// Increment counter` above a counter increment (delete the comment, keep the code). -- **A fact is documented once, at the declaration of the thing it is about.** A statement about a symbol belongs on that symbol's own declaration, never above the lines that read it, call it, or branch on it. Where the same sentence would sit above more than one _use_ of a symbol, it belongs on the declaration alone, or in `docs/` where it spans more than one symbol. A declaration is not a use: each member of a public structure still gets its own comment, and a file-level header still summarizes what the file declares. **Removing a copy is bounded, and all three preconditions hold before anything is deleted:** the declaration and the usage site both sit inside the scope this run resolved, so the rule never reaches a file the scope rule did not resolve to; the declaration's body has been opened this run, since a copy cannot be judged redundant against a declaration nobody read; and the copy says no more than the declaration's comment says. With all three met, keep the copy on the declaration, writing it there if it is absent, and delete the one above the use; this is the one case where an accurate comment is removed rather than corrected. Where the copy above the use carries a constraint the declaration does not, fold that into the declaration and then delete the copy, so the fact lands on the declaration either way. Failing any one of the three, leave both in place and report it: a repetition left alone costs a reader one duplicated sentence, where a wrong deletion destroys the only place a constraint was written down. -- **Comments describe the code as it stands (Rule 4), and the test is to name the line.** Delete commented-out code rather than leaving it in place. A phrase list only catches the comments that announce themselves, so point at the code beneath the comment that the comment is about; where nothing corresponds, it is not a comment about this code. That is what catches a comment explaining an absence, meaning why something was removed, why an approach was rejected, or what an earlier version did: nothing in the file matches it because its subject is a decision, and the reader who wants that decision is reading the commit or the change request that carries the diff proving it. Delete it. Two comments pass this test and stay: a note about a deliberate omission the code depends on, such as why a field must stay out of a payload, and a file-level header, which describes the file rather than any one line. -- **Form:** a documentation comment is a complete sentence, capitalized and punctuated; a short trailing comment may be a fragment. Wrap long comment lines to the width the file already uses, letting an unbreakable URL exceed it. Use the documentation format's own list syntax for enumerations, since indented plain text collapses into one run-on sentence when rendered. Never box a comment in asterisks or other decorative characters. Documentation precedes an annotation or decorator and never sits between it and the declaration. -- **Contracts worth stating:** any cleanup the caller owns (a handle to close, a listener to remove, a subscription to cancel), the error values or exception types a caller can branch on, and a deprecation marker naming its replacement. A deprecation without migration directions is incomplete; add one only where it is provable under Rule 2. +- **Internal elements** are documented only where the logic is complex or carries a gotcha or edge case, and a comment inside a function body is written only for non-obvious business logic, a workaround, or a complex transformation. Delete an internal comment only when it restates the line beneath it (the code stays). +- **A fact is documented once, at the declaration of the thing it is about.** A statement about a symbol belongs on its own declaration, never above the lines that read, call, or branch on it, or in `docs/` where it spans several symbols. A declaration is not a use: each member of a public structure keeps its own comment, and a file-level header still summarizes the file. **Remove a copy above a use only when all three hold:** the declaration and the use both sit inside the resolved scope; the declaration's body was opened this run; and the copy says no more than the declaration's comment. Then keep the fact on the declaration, writing it there if absent, and delete the copy, folding in first any constraint the copy adds; this is the one case where an accurate comment is removed. Failing any of the three, leave both and report it: a repetition costs one sentence, a wrong deletion can destroy the only record of a constraint. +- **Comments describe the code as it stands (Rule 4), and the test is to name the line.** Delete commented-out code rather than leaving it in place. A phrase list catches only comments that announce themselves, so point at the code the comment is about; where nothing corresponds, as with a comment on why something was removed, why an approach was rejected, or what an earlier version did, its subject is a decision that belongs to the commit, so delete it. Two comments pass and stay: a note on a deliberate omission the code depends on, such as why a field stays out of a payload, and a file-level header. +- **Form:** a documentation comment is a complete, punctuated sentence that follows the language's own convention where it has one, such as a Go comment opening with the identifier's name or a TSDoc block opening with its summary sentence before any tag, and is otherwise capitalized; a member's comment or a short trailing comment may be a fragment. Wrap long comment lines to the width the file already uses, letting an unbreakable URL exceed it. Use the documentation format's own list syntax for enumerations, since indented plain text collapses into one run-on sentence when rendered. Never box a comment in asterisks or other decorative characters. Documentation precedes an annotation or decorator and never sits between it and the declaration. +- **Contracts worth stating:** any cleanup the caller owns (a handle to close, a listener to remove, a subscription to cancel), the error or exception types a caller can branch on, and a deprecation marker naming its replacement. A deprecation without migration directions is incomplete; add one only where it is provable under Rule 2. - **File-level headers:** where the language provides one, it states the file's contents, uses, or dependencies. Notes aimed at maintainers rather than consumers go with the implementation instead. - **Output:** list the files changed and the kinds of change, or state "Phase 3: audited in-code documentation across X files, all accurate, no changes required." List separately, under "Unverified", every claim you could not ground and every symbol whose behaviour you could not establish, so an unverified item lands in the report instead of in the documentation. @@ -68,21 +66,23 @@ Act as a **Strictly Factual Technical Writer and Auditor**. Make the project's d Edit **documentation, never code behaviour**. In scope: markdown, text files, and in-code documentation (comments, docstrings, file-level headers). Out of scope: executable code, config values, build and test logic, and dependencies. Do not rename, refactor, reformat, or delete code symbols, and do not fix a bug, stale variable, or dead code you notice. Editing a comment is allowed; changing the code it describes is not. A stale comment is fixed by correcting the comment, not the code. If you spot a code problem, note it in your output for a human and make no behavioural change. +**Edit in place, never by replacement.** For every file, documentation and source alike, make the smallest edit that carries each correction, one passage or comment at a time. Never delete a file and create it again, write its whole contents over it, write it through a shell redirect or a script, rename it, or move it: a document you correct, compress, or split keeps its path and every passage you did not deliberately change. In a source file only comment and docstring lines change, and every declaration stays whatever its visibility, including a private or unexported helper nothing appears to call. Run no automatic fixer or code generator; the project's formatter may run on a file you edited, held to the same diff check. Never answer a build, lint, or test failure by changing code: undo your own edit and report it. + **Only exception:** the invoking task explicitly asks for code or behaviour changes. Absent that, this run is documentation-only. -### Rule 2: Zero hallucination (strictly enforced) +### Rule 2: Zero hallucination Every statement must be grounded in code you have **opened and read in full during this run**. Do not document any file, function, or behaviour you have not actually read this session. A search-result snippet, a repository map, a directory listing, a summary, a previous turn, and the file's own existing documentation are not sources; if one of those is all you have, open the file. -**Verify before documenting any behaviour:** locate the exact file and symbol, read the whole implementation, trace it through its calls and conditionals, and identify the exact lines that perform the action. Document only what those lines provably do. +**Verify before documenting any behaviour:** locate the file and symbol, read the whole implementation, trace its calls and conditionals, and document only what the lines performing the action provably do. **Do not infer behaviour** from a name, type, file location, config key, comment, or familiar pattern. Read the body: `deleteUser()` might only set a flag, and a comment can be stale (when code and comment conflict, the code wins). -**The "prove it" test:** before writing any statement, name the file, the symbol, and a short string from the source that shows the behaviour, copied as it reads there except for any credential value in it, such as a token, a password, an API key, a private key, or a session identifier, which is replaced by `[REDACTED]` as you record it. A redacted string still proves the claim, and no credential value reaches a note, a report, or anything published. If you cannot produce such a string at all, do not write the statement. **A line number is not proof.** It cannot be checked without opening the file, it drifts on the next edit, and it can be produced without reading anything; copying a string requires retrieval. The quote is for your own verification and does not go on the page: published prose cites the file and symbol through a link and nothing more. +**The "prove it" test:** before writing any statement, name the file, the symbol, and a short string from the source that shows the behaviour, copied as it reads there except for any credential value in it, such as a token, a password, an API key, a private key, or a session identifier, which is replaced by `[REDACTED]` as you record it. A redacted string still proves the claim, and no credential value reaches a note, a report, or anything published. If you cannot produce such a string at all, do not write the statement. **A line number is not proof:** it drifts and can be produced without reading anything; copying a string requires retrieval. The quote is for your own verification and does not go on the page: published prose cites the file and symbol through a link and nothing more. **A claim spanning several files is grounded the same way, from each of them.** An orientation sentence often rests on a handful of files rather than one line, so hold a quote from every file carrying a part of it before writing the sentence. Cover the system rather than only the sentence: look for the file that would contradict it, since a synthesis is refuted by what it leaves out. A summary written because no quote could be found is a guess with a citation attached, which stays banned. -**If you cannot verify, keep it off the page and report it.** Do not guess, do not leave a TODO, and never write "appears to", "seems to", "likely", "probably", "should", or "will". Silence in the documentation beats speculation in it, and naming the gap in your output beats both. Never document planned or intended behaviour. For complex behaviour, confirm against two or three locations (definition, usage, test). +**If you cannot verify, keep it off the page and report it.** Do not guess, do not leave a TODO, and never write "appears to", "seems to", "likely", "probably", "should", or "will". Naming the gap in your output beats speculation on the page. Never document planned or intended behaviour. For complex behaviour, confirm against two or three locations (definition, usage, test). ### Rule 3: Strict objectivity @@ -92,16 +92,16 @@ Every statement must be grounded in code you have **opened and read in full duri ### Rule 4: Current state only -Documentation and comments describe the code as it is now. Never narrate a change, a fix, or a prior state ("now uses", "previously", "no longer", "restored", "replaces", "used to", "formerly", "for the first time", "unlike the old"), never argue that the code is correct or safe, which documents the edit rather than the code, and never name a file, flag, symbol, or tool that no longer exists: version control already carries that history, a comment outlives the change that prompted it, and a reader cannot check a claim against something that is gone. The only sanctioned place for future intent is a `TODO` in the code that will change, positioned however that codebase positions one; documentation itself carries none, so no empty sections, stubs, or "add details here" placeholders, and if the code does not exist, neither should its documentation. Rationale worth keeping goes in its own decision record, not scattered through the files it explains. +Documentation and comments describe the code as it is now. Never narrate a change, a fix, or a prior state ("now uses", "previously", "no longer", "restored", "replaces", "used to", "formerly", "for the first time", "unlike the old"), never argue that the code is correct or safe, and never name a file, flag, symbol, or tool that no longer exists: version control carries that history, and a reader cannot check a claim against something gone. The only sanctioned place for future intent is a `TODO` in the code that will change, positioned however that codebase positions one; documentation itself carries none, so no empty sections, stubs, or "add details here" placeholders, and if the code does not exist, neither should its documentation. Rationale worth keeping goes in its own decision record, not scattered through the files it explains. -### Rule 5: Mermaid diagram and image accessibility (zero tolerance) +### Rule 5: Mermaid diagram and image accessibility -Every Mermaid diagram MUST include both: +Every Mermaid diagram must include both: 1. **`accTitle`**: a specific, descriptive title. Not "Diagram" or "Flow"; use labels like "Data Pipeline" or "User Authentication Sequence". 2. **`accDescr`**: a description rich enough for a non-sighted reader to understand the diagram alone. No placeholders like "A diagram showing...". -No exceptions. Do not output any diagram missing either field. +Do not output any diagram missing either field. Images are held to the same bar: every image carries alt text conveying what it shows. Generic alt text ("screenshot", "diagram") fails exactly as an absent `accDescr` does. Use an image only where showing is easier than describing. @@ -109,9 +109,9 @@ Images are held to the same bar: every image carries alt text conveying what it A document's total length is measured too, not only whether each paragraph earns its place: before finishing, judge the whole page against what a careful human would have written for the same brief, and cut restated context, a point made twice across sections, or depth that belongs on another page. This is a judgement call, not a word or line count. -**Split an overview from its depth.** Where a document mixes a brief orientation with deep reference, how-to, or explanation content, split it into a short overview (a README or index) that orients and links out, and a dedicated page under the matching Diátaxis directory, filed by Phase 2's directory-type-precedent logic. Apply this only where a page is found mixing both, not as a blanket requirement. +**Split an overview from its depth.** Where a document mixes a brief orientation with deep reference, how-to, or explanation content, split it into a short overview (a README or index) that orients and links out, and a dedicated page under the matching Diátaxis directory, filed by Phase 2's directory-type-precedent logic; the original file keeps its path as the overview. Apply this only where a page is found mixing both, not as a blanket requirement. -**Complement long prose, do not just cut it.** Where a passage stays long because the subject needs it, or occasionally a short one, consider a Mermaid diagram (never defaulting to `flowchart`, §5), a table, a code or config snippet, or an image; weigh this more as the passage grows longer. This supplements, not overrides, §4/§5's filters and section 3's table/snippet rules, grounded in code read this run like any other claim. +**Complement long prose, do not just cut it.** Where a passage stays long because the subject needs it, or occasionally a short one, consider a Mermaid diagram (never defaulting to `flowchart`, §5), a table, a code or config snippet, or an image; weigh this more as the passage grows longer. This supplements, not overrides, §4/§5's filters and section 3's table/snippet rules, and is held to Rule 2. --- @@ -124,12 +124,12 @@ Write as a careful human technical writer: formal and neutral, never robotic. Th - **Lead with the point**, putting the conclusion, answer, or action in the first sentence. **Show, do not tell:** demonstrate with a command, number, cited line, or named edge case instead of asserting significance. Vary sentence length where natural, without forcing a cadence target. - **Avoid these AI tells** (representative, not exhaustive): signposting previews ("This section covers"); puffery copulas ("serves as", "is a testament to", "plays a vital/pivotal role"); the rule-of-three triad as a default; filler transitions ("Additionally", "Furthermore", "Moreover" at high frequency); formulaic conclusions ("In conclusion", "Despite its ... it faces challenges"); and padded words such as delve, leverage, underscore, showcase, foster, seamless. Keep a word when it is factually correct in context (a test `harness`). - **A why-claim is still a claim (Rule 2).** Cite the comment, design record, commit, test, or config that proves a rationale or trade-off, or state the _what_ and stop. -- **Scope.** Applies to prose you add or change, not a rewrite of accurate existing prose (Phase 2). Introducing a term, naming a subject, or stating a prerequisite is an addition even where the surrounding prose is accurate; this governs `docs/` prose, not in-code documentation, which Phase 3 keeps terse. +- **Scope.** Applies to prose you add or change, not a rewrite of accurate existing prose (Phase 2). Introducing a term, naming a subject, or stating a prerequisite is an addition; this governs `docs/` prose, not in-code documentation, which Phase 3 keeps terse. - **Stay formal.** No contractions, casual asides, emoji, or detector-evasion tricks. Naturalness comes from cutting tells, not from informality. ### Brevity & style -- Use prose to carry reasoning (the _why_ and _how_); reserve bullets and numbered lists for genuine enumerations (steps, options, fields, parameters). Do not force explanation into parallel bullet fragments, and do not de-list a real list: enumerations stay lists, scannable for people and easy to retrieve. No walls of text. **Concise, not choppy:** no line-by-line narration, but keep the connective prose that carries logic. Lead each paragraph and section with its point, then give the detail. +- Use prose to carry reasoning (the _why_ and _how_); reserve bullets and numbered lists for genuine enumerations (steps, options, fields, parameters). Do not force explanation into parallel bullet fragments, and do not de-list a real list. No walls of text. **Concise, not choppy:** no line-by-line narration, but keep the connective prose that carries logic. Lead each paragraph and section with its point, then give the detail. - **Tables only for uniform data scanned quickly**, meaning many parallel items with distinct attributes. If columns repeat across rows, cells sit empty, or a cell holds a sentence of prose, use a list with sub-headings instead. ### Language @@ -142,7 +142,7 @@ Write as a careful human technical writer: formal and neutral, never robotic. Th - Document a tunable value by the **name a consumer changes it by**, judging by role, not location. That surface includes external interfaces (env vars, config-file keys, CLI flags) and named members of a centralized or exported constants module that other code reads: if a named, stable value is read elsewhere and changing it changes behaviour, document it by that name even when it is internal. Format: "Set or change `` in `` to control ``." Name the consumer-facing value (`LIMITS.MAX_RETRIES`), not a transient local. -### File citations & references (strictly enforced) +### File citations & references - **Every technical claim cites its source file.** No citation, no claim. - **Every file reference is a clickable markdown link**, `[filename](relative/path)`, never a bare filename. @@ -158,7 +158,7 @@ Write as a careful human technical writer: formal and neutral, never robotic. Th ### Formatting -- **A table's structure is load-bearing, and an edit inside a cell is where it breaks.** Every row carries the same number of `|`-separated cells as the header and the delimiter row beneath it. A cell holds one line: never a newline, a bullet list, or a fenced block. A literal `|` inside a cell is written `\|`, or the column count silently changes. Leave a cell long rather than breaking it across lines. Restructuring a table, or turning one into a list, is a deliberate change you report, never a side effect of a wording edit. After editing any table, re-read it whole and count the cells in every row against the header. +- **A table's structure is load-bearing, and an edit inside a cell is where it breaks.** Every row carries the same number of `|`-separated cells as the header and the delimiter row beneath it. A cell holds one line: never a newline, a bullet list, or a fenced block. A literal `|` inside a cell is written `\|`, or the column count silently changes. Editing a cell never licenses re-flowing, re-padding, or re-wrapping the table beyond the project's formatter: leave the cell long, never broken. Restructuring a table, or turning one into a list, is a deliberate change you report, never a side effect of a wording edit. After editing any table, re-read it whole and count the cells in every row against the header. - Always use relative links, including `../` paths, for GitHub compatibility. Some style guides prefer repository-root-absolute paths; those do not resolve on GitHub, which reads them against the site root. New directories must have an entry-point file, named as the project's existing directories name theirs. - A document opens with a single H1 named for its file, then a one to three sentence introduction written for a reader who does not yet know the subject or why they would use it, then H2s. Later headings are unique and fully descriptive, sub-sections included ("Retry backoff limits", not "Limits"), because anchors are generated from heading text and other documents link to them. Use sentence case. - Prefer standard markup to raw HTML. If the markup cannot express it, reconsider whether the document needs it. @@ -188,13 +188,13 @@ Exclude logging, metrics, telemetry, trivial validation, internal utilities, and ## 6. Pre-output Checklist -Before finalizing, review your own work and fix everything below. No exceptions. +Before finalizing, review your own work and fix everything below. **Re-verify citations (highest priority).** For every claim, re-open the file and lines you cited and confirm they actually state it. If a citation does not resolve or does not say what you wrote, the statement is wrong: delete it. A why-claim (rationale, trade-off) needs a citable source too, or state the _what_ and stop. Then confirm: -- Only documentation changed: no executable code, config values, tests, or dependencies (unless the invoking task explicitly asked for code changes). Pre-existing content changed only to fix factual errors, with accurate phrasing and voice left alone. +- Only documentation changed: no executable code, config values, tests, or dependencies (unless the task asked for code changes). Pre-existing content changed only to fix factual errors, with accurate phrasing and voice left alone. - No hedging ("appears to", "seems to", "likely", "probably", "should", "will"), no new subjective adjectives, and no code dumps. - Every file reference is a clickable link resolving to a file, not a directory. Configuration references name the value a consumer changes it by. - Acronyms you wrote are capitalized and expanded on first use (exceptions: brand/tool/package names, domain terms, code references). @@ -207,7 +207,8 @@ Then confirm: - Rendered output was checked, not only the source: diagrams parse, nested lists render, and documentation comments display the intended text. Every table you touched was re-read whole, with each row's cell count matching its header and no cell broken across lines. - Every document you created sits in a directory whose existing documents are the same Diátaxis type, or in a new directory created for that type, and your output names the directory and what decided it. - No comment you wrote or kept describes something the file does not contain, and every comment you deleted on that ground was one you could not attach to a line. -- No comment, existing or new, runs longer than what it documents: an accurate but disproportionate comment was compressed to its non-obvious content, not left because it was not wrong. -- Every document read this run is no longer, as a whole, than a careful human would have written for the same brief (Rule 6); a page mixing overview and deep reference, how-to, or explanation content was split rather than left layered. +- No comment in scope, existing or new, runs longer than what it documents: an accurate but disproportionate comment was compressed to its non-obvious content, not left because it was not wrong. +- Every document in scope is no longer, as a whole, than a careful human would have written for the same brief (Rule 6); a page mixing overview and deep reference, how-to, or explanation content was split rather than left layered. - A passage that stayed long after Rule 6 was weighed for a complementary diagram, table, snippet, or image, and one was added where it fit. +- The diff of every file you edited, taken against its state before this run, is local: unless the task asked for code changes, a source file changes comment and docstring lines only; a document changes only the passages you report, plus its formatter's adjustments; neither is a whole-file replacement. Any other changed line was put back exactly, never by reverting the file (it may hold others' work). No file was deleted, renamed, or moved, except a document deleted under Phase 2 and reported. - Phase 3 ran and its result is reported. diff --git a/.github/prompts/readme.md b/.github/prompts/readme.md index 3b0e84eb..30029996 100644 --- a/.github/prompts/readme.md +++ b/.github/prompts/readme.md @@ -27,7 +27,9 @@ Run one, not all three. None of them audits your whole repository by default, which matters on a large codebase or a monorepo. -`audit-docs` and `audit-quality` resolve scope in order, stopping at the first rule that applies: an explicit instruction, the active pull request, uncommitted changes, the component or system the surrounding task concerns, and only then everything. That last rung differs by what each one edits: the whole documentation set for `audit-docs`, the whole repository for `audit-quality`. Both state which rule applied in their output. +`audit-docs` and `audit-quality` resolve scope in order, stopping at the first rule that applies: an explicit instruction, the active pull request, uncommitted changes, what the surrounding task concerns, and only then everything. The last two rungs differ between them. For `audit-docs` the fourth is the files the surrounding task named or edited, and the last is the whole documentation set; for `audit-quality` they are the component or system the task concerns and the whole repository. Both state which rule applied in their output. + +`audit-docs` also edits only files inside the scope it resolved. A file it opens to verify a claim stays untouched, and on a pull request its in-code pass covers the changed files plus files their changed lines reference directly, never crossing into another package or workspace. `audit-pr` stops at the branch's own commits and has no whole-repository rung at all: with no change to review it reports nothing rather than widening.