diff --git a/.claude/skills/audit-docs/SKILL.md b/.claude/skills/audit-docs/SKILL.md index f44bd48..74d36f8 100644 --- a/.claude/skills/audit-docs/SKILL.md +++ b/.claude/skills/audit-docs/SKILL.md @@ -44,13 +44,13 @@ Open one of these when the run needs its detail. Nothing here is loaded until yo Five subagents ship with this skill. **The default is to dispatch none of them.** Each one 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 see the changed set, not the tree. A small pull request should reach for nothing here. -| Subagent | Dispatch when | Skip when | -| -------------------------------------------------- | ------------------------------------------------------------------------------- | ---------------------------------- | -| [`coverage-mapper`](agents/coverage-mapper.md) | scope is the whole documentation set, or the change adds or removes a component | scope is a single named document | -| [`curation-reviewer`](agents/curation-reviewer.md) | a document was substantially rewritten, or is consumer-facing | only a factual correction was made | -| [`diagram-reviewer`](agents/diagram-reviewer.md) | a document contains a diagram, or describes a flow of five or more steps | neither is true | -| [`surface-auditor`](agents/surface-auditor.md) | the change touches a public surface | the change is documentation-only | -| [`claim-verifier`](agents/claim-verifier.md) | a claim you cannot ground from a file already open | the proof is already in hand | +| Subagent | Dispatch when | Skip when | +| -------------------------------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------- | +| [`coverage-mapper`](agents/coverage-mapper.md) | scope is the whole documentation set, or the change adds or removes a component | scope is a single named document | +| [`curation-reviewer`](agents/curation-reviewer.md) | a document was substantially rewritten, or is consumer-facing | only a factual correction was made | +| [`diagram-reviewer`](agents/diagram-reviewer.md) | a document contains a diagram, or describes a flow of five or more steps | neither is true | +| [`surface-auditor`](agents/surface-auditor.md) | the change touches a public surface, or code in scope carries comments this run did not write | the change is documentation-only | +| [`claim-verifier`](agents/claim-verifier.md) | a claim you cannot ground from a file already open | the proof is already in hand | Each returns findings rather than edits, so the decision to act on any of them stays with this run. @@ -76,19 +76,19 @@ Each returns findings rather than edits, so the decision to act on any of them s ### Phase 3: in-code documentation audit -**Mandatory.** Execute regardless of Phase 1 and 2 results. +**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. - **Scope:** every `.md` file outside `docs/`, plus documentation comments, inline comments, and file-level headers across the code the scope rule above resolved to. -- **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; 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 comments that restate the code, never comments that explain a non-obvious internal. +- **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 comments that restate the code, never comments that explain a non-obvious internal. - **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. A public symbol left undocumented and reported is a compliant result; a comment written from the symbol's name is a defect, and it 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. - **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 where the logic is complex or carries a gotcha or edge case. 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). +- **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. Where a comment naming a symbol already sits above a line that uses that symbol, and it says what the declaration's comment says, 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; leave both in place and report it only where evidence in the opened implementation does not establish whether the usage-site copy adds a distinct constraint. _Bad:_ `isBetaEnabled mirrors the beta-features flag` above every read of `isBetaEnabled`. _Good:_ that sentence once, on the declaration of `isBetaEnabled`, and nothing at the read sites. - **Comments describe the code as it stands.** 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"), and never name a file, flag, or tool that no longer exists: version control carries that, and the comment outlives the change that prompted it. Never argue that the code is correct or safe, which documents the edit rather than the code. Delete commented-out code rather than leaving it in place. - **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. - **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. -- **Also remove:** outdated comments and orphaned TODO comments. - **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. --- @@ -229,6 +229,6 @@ Then confirm: - New or changed prose reads as a careful human wrote it: leads with the point, no signposting or banned AI tells, one canonical term per concept, no ambiguous `it`/`this`/`these`. - Architecture flows include only significant steps (§4); every diagram has `accTitle` and `accDescr`, and every image has real alt text. - No em-dashes (`—`) or en-dashes (`–`) anywhere you wrote; new or changed prose uses Canadian English. -- Every public symbol you touched carries a documentation comment written from its implementation, not from its name, and no comment narrates a change, names something that no longer exists, argues the code is safe, or sits commented out. +- Every public symbol you touched carries a documentation comment written from its implementation, not from its name, and no comment narrates a change, names something that no longer exists, argues the code is safe, or sits commented out. No comment you added sits above a usage site rather than a declaration, and every comment you removed as a repetition either said no more than the declaration's or had what it added folded into the declaration first. - Rendered output was checked, not only the source: diagrams parse, nested lists and tables render, and documentation comments display the intended text. - Phase 3 ran and its result is reported. diff --git a/.claude/skills/audit-docs/agents/surface-auditor.md b/.claude/skills/audit-docs/agents/surface-auditor.md index e1020d9..9bb930d 100644 --- a/.claude/skills/audit-docs/agents/surface-auditor.md +++ b/.claude/skills/audit-docs/agents/surface-auditor.md @@ -1,11 +1,11 @@ --- name: surface-auditor -description: Walks the code area in scope once and returns the public symbols carrying no documentation comment together with the comments their own implementation contradicts, so invoke it at the start of the in-code documentation phase. +description: Walks the code area in scope once and returns the public symbols carrying no documentation comment, the comments their own implementation contradicts, and the comments repeated above a usage site rather than a declaration, so invoke it at the start of the in-code documentation phase. --- # Surface auditor -This agent walks the code the caller's scope resolved to one time and returns two lists: public symbols carrying no documentation comment, and comments the implementation beneath them contradicts. It is the discovery pass for the in-code documentation phase, which is the largest read of the audit, and it exists so that the reading happens in this context and the caller receives a short list instead of a context filled with source it will not open again. The agent reports. It does not write a comment, correct one, or delete one, and the caller decides every repair. +This agent walks the code the caller's scope resolved to one time and returns three lists: public symbols carrying no documentation comment, comments the implementation beneath them contradicts, and comments repeated above a usage site rather than sitting on a declaration. It is the discovery pass for the in-code documentation phase, which is the largest read of the audit, and it exists so that the reading happens in this context and the caller receives a short list instead of a context filled with source it will not open again. The agent reports. It does not write a comment, correct one, or delete one, and the caller decides every repair. ## Input the agent receives @@ -52,19 +52,37 @@ fn fetch(&self, id: u64) -> Row { COMMENT: `Now uses the shared pool instead of opening a connection per call.` CODE: `self.pool.acquire().query(id)`. The sentence describes an edit rather than the code, and a reader cannot check "instead of" against anything still present. +## List three: comments repeated above a usage site + +A comment can be accurate and still be in the wrong place. Report every comment that names a symbol, sits above a line that uses that symbol, and states what the symbol's own declaration states or would state. One fact belongs on one declaration, so each copy above a read, a call, or a branch is an entry here. + +Both halves of the test are mechanical, and both are required. The comment names a symbol, and the line beneath it uses that same symbol. A comment above a line that does not reference the symbol it discusses is a different comment and is never reported. + +```javascript +// isBetaEnabled mirrors the beta-features flag. +if (isBetaEnabled === undefined) { + return fallback; +} +``` + +SYMBOL: `isBetaEnabled`. COMMENT: `isBetaEnabled mirrors the beta-features flag.` The line beneath reads `isBetaEnabled` rather than declaring it, so the sentence belongs on the declaration and this copy is an entry. + +**A copy that says more than the declaration is reported separately, not merged into the first list.** Where two comments about one symbol differ, and one carries a constraint, a hazard, or a caller obligation the declaration does not, report it under `DIFFERS`, quoting both and naming what the copy adds. The caller folds that addition into the declaration and then removes the copy, so naming the addition precisely is what the entry is for. Uncertainty about whether two comments say the same thing resolves to `DIFFERS`, never to `REPEATED`: an entry the caller settles by hand costs one judgement, where a wrong `REPEATED` points the caller at a comment carrying something real. + ## What the agent does not report -Each of these produces noise rather than a finding, so leave all of them out of both lists: +Each of these produces noise rather than a finding, so leave all of them out of every list: - a comment that is merely terse, or plain, or worded differently from how a convention would word it; - an internal helper whose name and signature already carry what it does; - a missing comment on a binding inside a function body; +- a comment sitting on a declaration, since a declaration is never a use: each member of a public structure carries its own comment, and a file-level header summarizes what the file declares; - a type annotation restated in prose, which is a style question and not a contradiction; - anything the agent could not open, which is reported as unread in the counts and never as a finding. ## The evidence bar -A contradiction is reported only with a verbatim string copied out of the body. Where that string holds a credential value, such as a token, a password, an API key, a private key, or a session identifier, replace the value with `[REDACTED]` when the entry is written; a redacted string still carries the contradiction, so the entry is reported rather than withheld. Three limits follow, matching the standard the rest of the audit holds: +A contradiction is reported only with a verbatim string copied out of the body. **Every verbatim string this agent returns, in any of the lists, follows one rule:** where it holds a credential value, such as a token, a password, an API key, a private key, or a session identifier, replace the value with `[REDACTED]` when the entry is written; a redacted string still carries the finding, so the entry is reported rather than withheld. Three limits follow, matching the standard the rest of the audit holds: - **A signature, a type, or a declaration proves what is declared and never what runs.** A function named `delete_user` returning a success type settles nothing about whether a row is removed. - **A comment cannot be evidence about another comment.** Where a file-level header and a symbol's own comment disagree, quote the body or report neither. @@ -83,15 +101,29 @@ COMMENT: CODE: BEHAVIOUR: +REPEATED + :: +COMMENT: +DECLARATION: +USES: , + +DIFFERS + :: +COMMENT: +DECLARATION COMMENT: +ADDS: + COUNTS Files in scope: Files read: Files unread: :: , Undocumented symbols: Contradicted comments: +Repeated comments: +Differing copies: ``` -Both lists may be empty. An empty pair reported with the counts beside it is a result; the same pair reported without them is indistinguishable from a run that opened nothing. +Every list may be empty. An empty set reported with the counts beside it is a result; the same set reported without them is indistinguishable from a run that opened nothing. ## Closing rule diff --git a/.claude/skills/audit-docs/assets/audit-report.template.md b/.claude/skills/audit-docs/assets/audit-report.template.md index dbb75d2..7ee9a1b 100644 --- a/.claude/skills/audit-docs/assets/audit-report.template.md +++ b/.claude/skills/audit-docs/assets/audit-report.template.md @@ -52,7 +52,7 @@ Sources that could not be resolved this run: [name each one and what you used in **Status:** [changed / audited in-code documentation across [count] files, all accurate, no changes required] -- `[file]`: [kind of change, such as documented a public symbol, corrected a parameter entry that named a removed argument, removed a comment that restated its line, removed an orphaned TODO]. +- `[file]`: [kind of change, such as documented a public symbol, corrected a parameter entry that named a removed argument, removed a comment that restated its line, removed an orphaned TODO, kept one copy of a repeated comment on the declaration of `[symbol]` and removed [count] copies above usage sites]. - `[file]`: [kind of change]. Public symbols left as they stand because their implementation was not read: [`Cache::evict` in `[file]`, `settle_invoice` in `[file]`, or write "none"]. @@ -67,7 +67,7 @@ Public symbols left as they stand because their implementation was not read: [`C | `[path]` | [documented [count] previously undocumented public symbols] | | `[path]` | [created, [tutorial / how-to guide / reference / explanation]] | -Kinds to choose from: corrected a factual statement, documented a public symbol, corrected an existing documentation tag, removed an outdated or restating comment, removed a duplicated section, created, deleted. +Kinds to choose from: corrected a factual statement, documented a public symbol, corrected an existing documentation tag, removed an outdated or restating comment, removed a comment repeated above a usage site, removed a duplicated section, created, deleted. (One row per file, not one per edit. If no file changed, replace the table with "No files changed.") diff --git a/.claude/skills/audit-docs/references/evidence-and-citation.md b/.claude/skills/audit-docs/references/evidence-and-citation.md index ed92672..50828d6 100644 --- a/.claude/skills/audit-docs/references/evidence-and-citation.md +++ b/.claude/skills/audit-docs/references/evidence-and-citation.md @@ -7,6 +7,7 @@ A claim is publishable when you can name the file, the symbol, and a string copi - [Why the quote stays out of the published sentence](#why-the-quote-stays-out-of-the-published-sentence) - [Reading a symbol through to the claim](#reading-a-symbol-through-to-the-claim) - [Proving that something does not happen](#proving-that-something-does-not-happen) +- [Provable is not the same as worth writing](#provable-is-not-the-same-as-worth-writing) - [Sources that do not count as evidence](#sources-that-do-not-count-as-evidence) - [Hallucination patterns and the check that catches each](#hallucination-patterns-and-the-check-that-catches-each) - [Worked examples in three languages](#worked-examples-in-three-languages) @@ -53,6 +54,16 @@ Delegation needs care. When the symbol forwards to another, the proof lives in t An absence claim ("does not validate the payload", "no retry on a 4xx") cannot be proved by copying one string, because the evidence is a branch that is not there. Ground it by enumerating the full set of branches and quoting the boundary that closes the set: the final `else`, the `default` case, the end of the match, or the last statement of the body. Then search for anything else that writes the same path (a subclass, an override, middleware, a decorator, a registered hook, generated code) and confirm none of them supplies the behaviour you are calling absent. Record the search you ran next to the quote. If the set cannot be closed, because dispatch is dynamic or the handler list is assembled at run time, the claim goes under "Unverified" instead of on the page. +## Provable is not the same as worth writing + +Grounding decides whether a statement **may** be written. It never decides that it **should** be, and it never decides how many times. Holding proof for one fact is proof about one fact, not a licence to state it at every site where it happens to be true. + +A verified fact about a symbol has one home, and that home is the symbol's own declaration: the sentence goes there and not above the lines that read it, call it, or branch on it. Proof accumulated while tracing a symbol through its callers is what settles the claim; the trace is not a list of places to write it down. + +**A declaration is never a usage site, so this bounds repetition across uses and nothing else.** Each member of a public structure is its own declaration and carries its own comment, however much that echoes the container's. A file-level header states what the file holds, which restates its declarations by design. Both are correct, and reading this section as "no fact twice anywhere" would forbid them. + +The failure this catches passes every other check in this file. The claim is grounded, the quote is real, the wording is accurate, and the run still leaves a comment above twenty branches where one comment on a declaration was the whole of what was needed. + ## Sources that do not count as evidence Each of these can start an investigation. None of them ends one. diff --git a/.claude/skills/typescript-code-and-test-standards/SKILL.md b/.claude/skills/typescript-code-and-test-standards/SKILL.md index f2b7221..af91fc0 100644 --- a/.claude/skills/typescript-code-and-test-standards/SKILL.md +++ b/.claude/skills/typescript-code-and-test-standards/SKILL.md @@ -96,6 +96,7 @@ Writing new code, reviewing a diff, and fixing a failing test are different jobs - A comment that contradicts the code is **corrected, not deleted**. When the two disagree, the code is the truth. - Delete commented-out code rather than leaving it in place. - Inside a function body, a comment restating the line beneath it is noise. Delete those, and keep anything carrying a constraint, hazard, or non-obvious behaviour. On a public surface, redundancy is not a defect. +- **A fact about a symbol is documented once, on its declaration.** Never repeat it above the lines that read, call, or branch on that symbol: `// isBetaEnabled mirrors the beta-features flag` belongs on the declaration of `isBetaEnabled`, not above each `if (isBetaEnabled)`. Each member of an exported structure is its own declaration and keeps its own block; a usage site is not one. Where a copy above a use carries a constraint the declaration does not, fold that into the declaration rather than leaving both. - **Never delete a tooling directive.** `//@ts-check`, `/// `, `// @ts-expect-error`, `eslint-disable`, `biome-ignore`, `istanbul ignore`, and `prettier-ignore` are instructions to a tool, not commentary. - Use `//` for implementation notes, and consecutive `//` lines for a multi-line note. No `/* */` block inside a function body, with one exception: naming an argument at a call site, `someFunction(/* shouldRender= */ true)`. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index be610c7..222f6b2 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -163,7 +163,7 @@ Not adopted: the ban on default exports (this repository uses them for the modul - **Comments describe the code as it stands.** Never narrate a change, fix, or prior state ("now uses", "previously", "no longer", "restored", "replaces", "used to", "formerly", "for the first time"), and never name a file, flag, or tool that no longer exists; git history carries that. Never argue that the code is correct or safe, which documents the edit rather than the code. Delete commented-out code. A comment contradicting the code is corrected, not deleted - **Every exported symbol carries a `/** */` block, without exception**, as do the members of an exported structure (interface properties, object keys, enum values). Write for a reader meeting it for the first time; where nothing beyond a restatement is true, restate. Being obvious is not a defect on a public surface, being absent is -- A private helper gets a block when its name and signature do not carry it; a binding inside a function body does not, and a comment there that restates the next line is noise +- A private helper gets a block when its name and signature do not carry it; a binding inside a function body does not, and a comment there that restates the next line is noise. **A fact about a symbol is stated once, on its declaration**, never repeated above the lines that read, call, or branch on it: `isBetaEnabled mirrors the beta-features flag` belongs on the declaration, not above each `if (isBetaEnabled)`. A member of an exported structure is its own declaration; a usage site is not - **In a block you write, do not put types in JSDoc.** TypeScript ignores `@param {string}`, `@returns {number}`, `@type`, and `@typedef` in `.ts`/`.tsx`, so they drift from the signature. Skip `@implements`, `@enum`, `@private`, and `@override` beside the keyword, and add `@param`/`@returns` where they say more than the name and type do - **Leave existing tags alone unless wrong.** A `@param`/`@returns` already in the tree was added deliberately, annotation and all. Read the surrounding code, fix what is factually wrong, change nothing else: do not strip a `{type}`, reword accurate prose, or delete a tag for looking redundant. Delete only when wrong and uncorrectable, such as documenting a parameter the signature no longer has - `@throws`, `@example`, `@deprecated` (naming its replacement), and `@see` are encouraged: none are expressible in the type system. Open a block with a third-person verb phrase; one tag per line; bodies are Markdown diff --git a/.github/prompts/audit-docs.prompt.md b/.github/prompts/audit-docs.prompt.md index ec14740..0c44e5a 100644 --- a/.github/prompts/audit-docs.prompt.md +++ b/.github/prompts/audit-docs.prompt.md @@ -41,19 +41,19 @@ Act as a **Strictly Factual Technical Writer and Auditor**. Make the project's d ### Phase 3: in-code documentation audit -**Mandatory.** Execute regardless of Phase 1 and 2 results. +**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. - **Scope:** every `.md` file outside `docs/`, plus documentation comments, inline comments, and file-level headers across the code the scope rule above resolved to. -- **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; 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 comments that restate the code, never comments that explain a non-obvious internal. +- **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 comments that restate the code, never comments that explain a non-obvious internal. - **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. A public symbol left undocumented and reported is a compliant result; a comment written from the symbol's name is a defect, and it 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. - **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 where the logic is complex or carries a gotcha or edge case. 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). +- **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. Where a comment naming a symbol already sits above a line that uses that symbol, and it says what the declaration's comment says, 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; leave both in place and report it only where evidence in the opened implementation does not establish whether the usage-site copy adds a distinct constraint. _Bad:_ `isBetaEnabled mirrors the beta-features flag` above every read of `isBetaEnabled`. _Good:_ that sentence once, on the declaration of `isBetaEnabled`, and nothing at the read sites. - **Comments describe the code as it stands.** 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"), and never name a file, flag, or tool that no longer exists: version control carries that, and the comment outlives the change that prompted it. Never argue that the code is correct or safe, which documents the edit rather than the code. Delete commented-out code rather than leaving it in place. - **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. - **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. -- **Also remove:** outdated comments and orphaned TODO comments. - **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. --- @@ -194,6 +194,6 @@ Then confirm: - New or changed prose reads as a careful human wrote it: leads with the point, no signposting or banned AI tells, one canonical term per concept, no ambiguous `it`/`this`/`these`. - Architecture flows include only significant steps (§4); every diagram has `accTitle` and `accDescr`, and every image has real alt text. - No em-dashes (`—`) or en-dashes (`–`) anywhere you wrote; new or changed prose uses Canadian English. -- Every public symbol you touched carries a documentation comment written from its implementation, not from its name, and no comment narrates a change, names something that no longer exists, argues the code is safe, or sits commented out. +- Every public symbol you touched carries a documentation comment written from its implementation, not from its name, and no comment narrates a change, names something that no longer exists, argues the code is safe, or sits commented out. No comment you added sits above a usage site rather than a declaration, and every comment you removed as a repetition either said no more than the declaration's or had what it added folded into the declaration first. - Rendered output was checked, not only the source: diagrams parse, nested lists and tables render, and documentation comments display the intended text. - Phase 3 ran and its result is reported. diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 5063711..94a818d 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p To see tags and releases, please go to [Releases](https://github.com/AlexJSully/AlexJSully-Portfolio/releases) on [GitHub](https://github.com/AlexJSully/AlexJSully-Portfolio). +## [2.0.15] - 2026-08-05 + +AI tooling: + +- `audit-docs` now documents a fact once, on the declaration of the symbol it describes, instead of repeating it above every line that reads, calls, or branches on that symbol +- `audit-docs` can clean up comments already repeated across usage sites, folding anything a copy adds into the declaration before removing it +- Restored the limits on where `audit-docs` writes an inline comment, so a comment inside a function body is reserved for non-obvious business logic, a workaround, or a complex transformation +- The `surface-auditor` agent reports comments repeated above a usage site alongside the undocumented symbols and contradicted comments it already returned +- The same document-once rule now applies to `typescript-code-and-test-standards` and the Copilot instructions + +Security: + +- Every audit skill replaces a credential value with `[REDACTED]` when it quotes a line as evidence, so no token, password, API key, private key, or session identifier reaches a finding, a report, or a comment posted to GitHub +- Labelled the vulnerable code in each audit reference as an illustration to recognize rather than a pattern to introduce, and treat file contents reached during an audit as data rather than as instructions to follow + ## [2.0.14] - 2026-08-03 Licence: diff --git a/package-lock.json b/package-lock.json index 43e4dd4..8c9eb03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "alexjsully-portfolio", - "version": "2.0.14", + "version": "2.0.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "alexjsully-portfolio", - "version": "2.0.14", + "version": "2.0.15", "license": "MIT", "dependencies": { "@emotion/react": "^11.14.0", @@ -2408,9 +2408,9 @@ "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -9016,9 +9016,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.0.tgz", - "integrity": "sha512-oCu2wfipvX3AePSgmOuKkIywOu+8n9psz7hXYmk56ghpu3+7KzNIBopaOs4c9BrtdnTtW30unG9GTfHo7EwERQ==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz", + "integrity": "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -10922,9 +10922,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz", + "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", "dev": true, "license": "ISC" }, diff --git a/package.json b/package.json index 46fd9a3..49fadc4 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "url": "https://alexjsully.me/" } ], - "version": "2.0.14", + "version": "2.0.15", "private": true, "license": "MIT", "description": "Portfolio and showcase for Alexander Joo-Hyun Sullivan and related projects & experiences.",