technical-documentation
Audit, write, and improve developer documentation using Google's Developer Documentation Style Guide and Technical Writing courses. Use this skill for any documentation work, even when the user names no style guide: "audit our docs", "review this README", "write a README", "getti
Install
npx skills add https://github.com/wondelai/skills/tree/main/technical-documentation
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install wondelai-skills@llmmart
git clone https://github.com/wondelai/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole wondelai/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Technical Documentation
Audit, write, and improve developer documentation the way Google's technical writers do: start from the reader's task, verify every fact against the code, then apply the style guide in severity order — structure before voice, voice before word choice.
Core Principle
Write for the reader's task, not the product's feature list. Google's guide asks for prose that is conversational but not frivolous, precise, and consistent, because a developer reading docs is trying to get something done, not to admire the product. Two framing rules from the guide shape everything below:
- Guidelines, not rules. Depart from the guide when doing so improves the content — established domain terminology wins — but stay consistent within the document.
- Precedence. A project's own style guide comes first, then Google's guide, then Merriam-Webster (spelling), the Chicago Manual of Style (general style), and the Microsoft Writing Style Guide (technical style).
Rules come in two layers. Structural and content rules (headings, procedures, code samples, second person, active voice, timeless docs, accessibility) apply to documentation in any language. Rules tagged [EN] (spelling, serial comma, contractions, the word list) apply only to English text — skip them for other languages, and never translate a document unless asked.
Scoring
Goal: 10/10. Score = number of Quick Diagnostic rows passed (10 rows, 1 point each; the [EN] row auto-passes for non-English docs). Bands: 9-10 = ships as is; 7-8 = word- and voice-level edits only; 5-6 = restructure sections, then re-edit; ≤4 = rewrite from the doc-type skeleton. Blocking findings — wrong or unverifiable facts, a procedure that can't be completed, information that exists only in an image or in an image without alt text — are a separate gate: the doc is not shippable at any score until they're fixed. Report the score, the failed rows, and the exact edits that reach 10/10.
Framework
1. Know the Reader and the Document's Job
Core concept: Every page serves one reader with one task. Name both before writing a word — audience and level, what they'll be able to do afterwards — and pick the document type that fits: tutorial (learn by doing), how-to (accomplish a task), concept (understand), reference (look up), README (orient and start).
Why it works: Readers scan for their task; a page that mixes concept, procedure, and reference forces them to read everything to find anything.
Key insights:
- Google's Technical Writing course opens a doc with an audience statement and a scope plus non-scope statement — the non-scope rescues readers who are on the wrong page
- "Before you begin" lists prerequisites before step 1, not inside step 4 (convention)
- Key points first: the intro states what the reader gets, not the product's history
- Every procedural page ends with verification ("Confirm that…") and "What's next" (convention)
Applications:
| Context | Application | Example |
|---|---|---|
| README | Orient: what it is, who it's for, three-step start, links out | Purpose → install → first run → docs map |
| Mixed page | Split concept from procedure into linked pages | "How OAuth works" + "Configure OAuth" |
| Tutorial vs how-to | Tutorial teaches one path end to end; how-to assumes context | "Build your first plugin" vs "Add a hook" |
See references/document-types.md when choosing or restructuring a doc type — skeletons for README, getting started, tutorial, how-to, and concept pages, the audience and scope statements, and the self-editing pass for large doc sets.
2. Voice: You, Active, Present, Timeless
Core concept: Address the reader as "you", make the actor of every sentence explicit, describe behavior in the present tense, and write as if the page will be read in five years.
Key insights:
- "We" hides who acts; "the user" turns the reader into a third party — both weaken an instruction
- Passive voice is allowed only when the actor is unknown or irrelevant ("The file is encrypted at rest")
- "Will" belongs only to genuinely later effects: "The server sends an ack", not "will send"
- Contractions are fine — Google prefers "isn't" over "is not" for negations
[EN] - Software doesn't want, see, or think: "The API detects", not "the API sees"
- No "please" (reserve it for asking permission), no "simply / easily / just", no superlatives — if a step is easy, the reader will notice
- Timeless: cut "currently", "new", and "soon"; never pre-announce unreleased features
Before → after:
- "Please note that the new dashboard will simply be shown once the user has logged in." → "After you sign in, the dashboard appears."
- "We recommend that the token is refreshed by the client." → "Refresh the token from the client."
See references/voice-and-words.md when a doc's tone is off or inconsistent — the voice rules with the guide's exact exceptions, inclusive and global-audience language, and the full word list.
3. Sentences and Words
Core concept: Put the condition before the instruction, keep one idea per sentence, and choose the plain word the guide's word list prefers.
Key insights:
- "To delete the document, click Delete" — readers decide whether a step applies before they act, not after
- Spell out an abbreviation on first use with the short form in parentheses; skip only universally known ones (URL, HTML)
- Latin abbreviations translate and scan poorly: "for example", not "e.g."; "that is", not "i.e."; omit "etc." or finish the list
[EN] - "can" = ability, "may" = permission, "might" = possibility
[EN] - Word list samples
[EN]: sign in (not log in) · set up as a verb · lets you (not allows you to) · through or by using (not via) · after (not once) · use (not leverage or utilize) · checkbox · email - Jargon is fine for the stated reader and a defect for anyone else — define it or link it
Before → after:
- "Click Save in order to persist the settings once you are done, i.e. when all fields are filled." → "After you fill in all fields, click Save."
- "The CLI utilizes the GCP SDK (e.g. for auth)." → "The CLI uses the Google Cloud SDK, for example for authentication."
See references/voice-and-words.md when auditing word choice — the word list table (avoid → use → why), abbreviation rules, and modal verbs.
4. Structure: Headings, Lists, Tables, Notices
Core concept: Structure is the reader's map. Headings in sentence case read as a table of contents; lists carry parallel items introduced by a full sentence; tables have header rows; notices are rare and mean something.
Key insights:
- Task headings are bare imperatives ("Create an instance"); concept headings are noun phrases ("Instance lifecycle"); no "-ing" headings
- A list needs an introductory sentence ending in a colon, and every item in the same grammatical form; numbered only when order matters
- Description lists (term → definition) beat two-column tables for paired data
- Tables: header row, an intro sentence, no merged or empty cells — screen readers depend on it
- Note = useful but optional; Caution = proceed carefully; Warning = harm or irreversible loss. Don't stack them; one per section is a practical ceiling (inferred)
- Cross-references say "see", never "above" or "below" — pages reflow and get translated
- Link text names the target ("see Configure a custom domain"), never "click here"
- Alt text states the image's purpose; information must never live only in a picture
Applications:
| Context | Application | Example |
|---|---|---|
| Wall-of-text page | Insert a task heading wherever the task changes | "Install", "Configure", "Verify" |
| Three stacked notes | Fold two into body text; keep the one that changes behavior | One Caution about data loss |
| Options table | Header row + intro sentence + parallel cell phrasing | "The following flags control output:" |
See references/structure-and-formatting.md when fixing page structure — heading, list, table, notice, cross-reference, link-text, image, number, and date rules with before/after pairs.
5. Procedures and Code
Core concept: A procedure is a numbered list of single imperative actions, each stating where to act and what to expect. Code is set in code font, introduced by a sentence ending in a colon, and uses placeholders the reader can't mistake for literals.
Key insights:
- One action per step; "Optional:" prefix for optional steps; a single step is a bullet, not "1."
- Sub-steps run a, b, c; document the shortest path, not every alternative
- UI element names in bold, matching on-screen casing; click for a mouse, tap for touch, select when device-agnostic
- Code font for filenames, paths, commands, flags, parameters, and values — not for product names
- Placeholders are
ALL_CAPS_WITH_UNDERSCORES, never<your-key>orYOUR_API_KEY, and are explained right after the sample ("ReplacePROJECT_IDwith…") - Command syntax:
[optional],{a|b}for exclusive choices,...for repeatable arguments - Samples are runnable, minimal, wrapped at 80 characters, and show the expected output
Before → after:
- "Run the command below with your key:
shipit deploy --key=<your-key>" → "To deploy, run the following command:" → fencedshipit deploy --key=API_KEY→ "ReplaceAPI_KEYwith the key from the Settings page." - "1. You should now click on the Deploy button to deploy." → "1. Click Deploy. The status changes to Deploying."
See references/procedures-and-code.md when writing steps or samples — the full procedure rules, UI-element and device verbs, code-in-text, placeholder, command-line syntax, and the sample-code quality checklist.
6. Reference Docs: API, Docstrings, CLI Help
Core concept: Reference text is descriptive, complete, and formulaic on purpose — readers look things up, so every entry must exist and read the same way.
Key insights:
- Document every public class, method, field, constant, and enum value; a missing entry reads as "unsupported"
- Open method descriptions with the category verb: "Gets the…", "Sets the…", "Checks whether…", "Creates a…", "Returns…" — never "This method…"
- Non-boolean parameters start "The…" or "A…"; booleans read "If true, … If false, …" (action) or "True if …; false otherwise" (state)
- Document return values and exceptions ("Thrown when…") for every method that has them
- A deprecated element names its replacement in the first sentence
- CLI
--help(convention — Google has no--helppage): usage line in[optional]syntax, one-line synopsis, every flag described with the same placeholder style
Before → after:
- "This method is used for getting the customer." → "Gets the customer for the given
customerId. ThrowsNotFoundErrorwhen no customer exists." - "@param force - force flag" → "@param force If true, deletes the bucket even if it contains objects. If false, fails when the bucket isn't empty."
See references/api-reference.md when writing or auditing reference material — the verb-by-category table, parameter, return, and exception patterns, one complete JSDoc example, and CLI help conventions.
7. Release Notes, Changelogs, Migration Guides
Core concept: A changelog is documentation for the reader who is about to upgrade. Each entry states what changed, what it means for them, and what to do — in the structure of Keep a Changelog, in the voice of the rest of the docs.
Key insights:
- Newest version first, an
Unreleasedsection on top, ISO dates in version headings, version headings linked to diffs (Keep a Changelog) - Group entries under Added / Changed / Deprecated / Removed / Fixed / Security; never paste commit messages
- Breaking changes go first in the version, with a link to migration steps (convention)
- A deprecation entry names the replacement and the removal version or date
- A migration guide is a procedure: "Before you begin" (versions, backups), numbered steps with before/after snippets, "Verify the migration", rollback
- Apply the Google layer to every entry: second person for actions, no "currently/new", code font for flags and APIs, one tense used consistently
Before → after:
- "Various improvements to the auth module (#412)" → "Changed:
login()now returns aSessioninstead of a token string. Update callers that read.token— see Migrate to sessions."
See references/release-notes.md when writing release notes or a migration guide — the Keep a Changelog skeleton, entry patterns per category, deprecation wording, and the migration-guide procedure.
8. Running the Audit, Rewrite, or Write
Core concept: Three modes, one discipline: intake → local conventions → read as the reader → verify facts → apply rules by severity → output in a fixed shape.
Protocol:
- Intake. Confirm the mode (audit, improve, or write), document type, reader and level, and language. For write, the reader's task and the fact sources (code paths, existing docs) are required — don't start without them.
- Local style guide. Look for
CONTRIBUTING.md,STYLE.md,docs/style-guide.md,.vale.ini, and the conventions existing docs already follow (for example, "log in" everywhere). They win over Google. Vale with theGooglepackage automates the[EN]word and punctuation layer if the project wants a linter. - Read
references/audit-checklist.mdbefore any audit or improve pass — the rule IDs cited in findings live there; never cite an ID you haven't read. - Read the doc cold as the target reader, then check every command, flag, parameter, and behavior against the code before judging style. A stylish wrong doc is worse than an ugly right one.
- Apply rules in severity order: Blocking → High (structure, accessibility, missing reference entries) → Medium (voice, notices, intro sentences) → Low (word list, punctuation
[EN]). - Output. A finding's location is one the reader can find: the heading path, plus the line number when auditing a file. Improve = a one-line
Score before → after, the full rewritten document, then a## Change logtable (Change | Rule ID + name | Why). Facts stay untouched — a fact stated in the source document counts as received from the user, so keep it (withTODO(verify): …when no code confirms it) rather than deleting it. Write = the document, withTODO(verify)for every gap. Never include a command, flag, or parameter you didn't see in code or receive from the user.
ALWAYS output audits in this format:
# Documentation Audit: [path or title]
**Score:** X/10 — [band] **Shippable:** yes | no (blocking findings below)
**Diagnostic:** N/10 — failed rows: [row numbers + one-line reason each]
**Doc type / reader:** [type] for [audience, level] **Language:** [en | xx — [EN] rules skipped]
**Local style guide:** [file found and honored | none — Google applies]
**Blocking:** [wrong/unverifiable facts, unfollowable steps, image-only information — or "none"]
**Findings:**
| # | Location | Rule (ID + name) | Before | After | Severity |
**Rewrite plan:** [ordered: structure → voice → words; what to do first to reach 10/10]
See references/audit-checklist.md when running any audit or rewrite — the full rule table with IDs and severities, the severity rubric, non-English handling, a Vale configuration, and a worked mini-audit.
Common Mistakes
| Mistake | Why It Fails | Fix |
|---|---|---|
| Organizing by feature instead of reader task | Readers hunt across sections for one workflow | Name the reader's task; pick the doc type; one task per page |
| Fixing style before verifying facts | Polished wrong instructions are trusted longer | Check every command and parameter against code first |
| "Click here" and "see below" | Meaningless out of context, to screen readers, and after reflow | Link text names the target; cross-refs say "see" |
| Steps buried in paragraphs, passive and future tense | Reader can't tell who does what, or in what order | Numbered imperative steps, condition first, present tense |
| Stacked Note/Warning boxes | Everything shouted, nothing heard | One notice per section; the rest becomes body text |
<your-key> or YOUR_API_KEY placeholders |
Reader types the brackets or reads the prefix as a literal | API_KEY in caps, explained after the sample |
| Rewriting the meaning while "fixing style" | Reviewer approves prose, ships wrong behavior | Facts unchanged; unknowns become TODO(verify) |
Quick Diagnostic
| Question | If No | Action |
|---|---|---|
| Does the first paragraph say who the doc is for and what they'll be able to do? | Readers can't tell if they're on the right page | Add audience, outcome, and non-scope statements |
| Does the doc type match the reader's task (tutorial · how-to · concept · reference · README)? | Concept and steps interleave; nothing is findable | Split by type; link between pages |
| Is every command, flag, parameter, and behavior verified against code or the user? | The doc teaches something false | Verify or mark TODO(verify); not shippable until fixed |
| Do headings read as a sentence-case table of contents (tasks imperative, concepts noun phrases)? | Scanning fails; "-ing" headings hide the action | Rewrite headings; add one where each new task starts |
| Are all sequences numbered steps, one imperative action each, condition first? | Readers miss steps or act before checking | Convert paragraphs to steps; move conditions forward |
Is every code sample introduced by a colon sentence, with ALL_CAPS placeholders explained? |
Readers paste literals or don't know what the sample does | Add intro sentences; fix and explain placeholders |
| Is the text in second person, active voice, present tense, with no please/simply/just and no anthropomorphism? | Instructions read as narration | Rewrite sentence by sentence; cut filler |
| Are links descriptive, cross-refs "see"-based, images alt-texted, tables headed? | Screen readers and reflow break the page | Fix each; move image-only information into text |
| Is it timeless — no "currently/new/soon", no pre-announced features? | The doc rots the day it ships | Remove time words; describe only shipped behavior |
[EN] Does it follow the word list, serial comma, contractions, and American spelling — or the local guide? |
Small inconsistencies erode trust | Apply the word list; run Vale if configured |
About the Source
Google's Developer Documentation Style Guide is the public house style that Google's technical writers maintain for developers.google.com, Android, and Google Cloud documentation; the companion Technical Writing One and Two courses are Google's internal engineer training, released publicly. This skill adapts both under CC BY 4.0 (per Google's site policies) and adds Keep a Changelog for release notes; it is an independent adaptation, not endorsed by Google.
Further Reading
- Google Developer Documentation Style Guide — start with Highlights and the Word list
- Technical Writing One and Technical Writing Two — Google's courses on words, sentences, documents, self-editing, and sample code
- Keep a Changelog — the changelog structure this skill uses for release notes
- "Docs for Developers: An Engineer's Field Guide to Technical Writing" by Jared Bhatti, Zachary Sarah Corleissen, Jen Lambourne, David Nunez, and Heidi Waterhouse
- "Every Page Is Page One: Topic-Based Writing for Technical Communication and the Web" by Mark Baker
Files (skills)
-
references
-
api-reference.md 19 KB
# API Reference, Docstrings, and CLI Help Reference material is descriptive, complete, and formulaic on purpose. Readers arrive at a reference entry mid-task, read one entry, and leave. This file owns rules **A1–A7**. **Contents** 1. [What to document (A1)](#what-to-document-a1) · every public element, and what "public" means per language 2. [Method descriptions: verb by category (A2)](#method-descriptions-verb-by-category-a2) 3. [Parameters (A3)](#parameters-a3) · non-boolean, boolean, optional, units 4. [Return values and exceptions (A4)](#return-values-and-exceptions-a4) 5. [Deprecations (A5)](#deprecations-a5) 6. [Complete example: `StorageClient`](#complete-example-storageclient) · TypeScript, plus a Python mirror 7. [Reference voice (A7)](#reference-voice-a7) 8. [CLI help text (A6)](#cli-help-text-a6-convention) and [REST reference pages](#resthttp-reference-pages-convention) — both convention --- ## What to document (A1) Document every public class, interface, struct, constant, field, enum value, and method. An undocumented public element reads as unsupported: developers skip it, file bugs against it, or reimplement it. A reader can't tell "not documented" from "not there". "Public" is defined by the language, not by intent (convention — per-language norms, not the style guide): | Language | Public surface | |----------|----------------| | TypeScript / JavaScript | Exported from the package entry point, including types and enum members | | Python | Names without a leading underscore, or listed in `__all__` | | Rust | `pub` items reachable from the crate root, including fields and variants | | Go | Identifiers starting with a capital letter, including package-level errors | | Java / C# | `public` and `protected` members of public types | Write the summary as one sentence, first, in the entry's own paragraph. The summary answers "what does this do"; it never restates the name. A second paragraph, when one is needed, adds what the reader can't infer from the signature: side effects, cost, lifecycle, concurrency safety, ordering guarantees, or a link to the task page. **Before → after (class summary):** - Before: `/** StorageClient class. Used for storage. */` - After: `/** Reads and writes objects in a single storage bucket. */` followed by a second paragraph: `A client opens one connection per instance and reuses it. Create one client per bucket and share it across requests; the client is safe for concurrent use.` **Before → after (enum value):** - Before: `ARCHIVE, // archive` - After: `ARCHIVE — Lowest storage price, highest retrieval price. Intended for objects read less than once a year.` Source: api-reference-comments --- ## Method descriptions: verb by category (A2) Open a method description with a third-person present-tense verb chosen by the method's category. The verb tells the reader the shape of the call before they read the parameters. `[EN]` The exact verb wordings below are English; the category-to-verb discipline applies in any language. | Category | Opening verb | Example first sentence | |----------|--------------|------------------------| | Boolean getter | Checks whether… | `Checks whether the bucket has an active retention policy.` | | Other getter | Gets the… | `Gets the storage class of the bucket.` | | Setter | Sets the… | `Sets the retention period, in days, for objects in the bucket.` | | Creator or factory | Creates a… | `Creates a signed URL that grants temporary read access to an object.` | | Everything else | Returns / Registers / Sends / Deletes / Validates / Uploads… | `Deletes the object and every one of its versions.` | Drop the "This method…" and "This function…" openers, along with "A function that…" and "Method to…". The entry already appears under the member's name and signature, so the phrase spends the reader's first four words on the heading. **Before → after:** - Before: `/** This method is used for getting the customer associated with a subscription. */` - After: `/** Gets the customer that owns the subscription. */` - Before: `/** Function that checks if a bucket is public or not. */` - After: `/** Checks whether anyone with the URL can read objects in the bucket. */` - Before: `/** Will create a new signed URL for the object and return it to the caller. */` - After: `/** Creates a signed URL that grants temporary read access to the object. */` Source: api-reference-comments --- ## Parameters (A3) A parameter description is a noun phrase describing the value, not a sentence about the parameter. Non-boolean parameters start with "The" or "A". **Booleans take one of two patterns**, chosen by what the flag does: | Flag means | Pattern | Example | |------------|---------|---------| | An action the call performs | `If true, … If false, …` | `If true, deletes the bucket and every object in it. If false, fails when the bucket still contains objects.` | | A state the value carries | `True if …; false otherwise` | `True if the object is publicly readable; false otherwise.` | **Optional parameters** say so and name the default at the end of the description: "Optional. Connection options such as the region and the request timeout. Defaults to the `us-east-1` region and a 30-second timeout." Where a generator already prints defaults from the signature, repeat them only where the reader can't see the signature — REST body tables and CLI help (convention). **Units and ranges** belong in the description, not in the reader's head. Give the unit for every duration, size, rate, and price, and give the accepted range where one exists. **Before → after:** - Before: `@param timeout timeout` - After: `@param timeout The time to wait for a response, in milliseconds. An integer from 1000 to 600000.` - Before: `@param force force flag` - After: `@param force If true, deletes the bucket even when it contains objects. If false, fails when the bucket isn't empty.` - Before: `@param amount the amount @param currency currency (optional, default USD)` - After: `@param amount The amount to charge, in the currency's smallest unit — 1099 means $10.99 for USD.` and `@param currency Optional. The three-letter ISO 4217 currency code. Defaults to usd.` Source: api-reference-comments --- ## Return values and exceptions (A4) Describe the return value with the same noun-phrase pattern as parameters: "The…" or "A…", brief, one sentence where possible. A boolean return uses `True if …; false otherwise`. A method returning nothing needs no return entry. Exception wording depends on whether the tool prints the word "Throws" for you: | Situation | Write | Renders as | |-----------|-------|------------| | Tool inserts "Throws" — JSDoc `@throws {E}`, Python `Raises: E:` | `If the bucket doesn't exist.` | Throws `NotFoundError` if the bucket doesn't exist | | Nothing inserted — prose tables, hand-written reference pages | `Thrown when the bucket doesn't exist.` | as written | Document every error type the method throws, one entry each — a caller writing a `catch` needs the full list, including errors raised by validation before any I/O happens. Say what comes back when there's nothing to return. `null`, `undefined`, an empty array, and a thrown error are four different contracts, and a reader who guesses wrong ships a crash. **Before → after:** - Before: `@returns the objects @throws error if something goes wrong` - After: `@returns The objects whose keys start with the prefix, sorted by key. Returns an empty array when no object matches.` plus `@throws {NotFoundError} If the bucket doesn't exist.` and `@throws {PermissionDeniedError} If the credentials can't list the bucket.` - Before: `@returns boolean` - After: `@returns True if the connection is open; false otherwise.` Source: api-reference-comments --- ## Deprecations (A5) A deprecated element names its replacement in the first sentence, because the reader is looking for what to call instead. Add the removal version or date, and one line of migration guidance concrete enough to apply without opening another page. **Before → after:** - Before: `/** @deprecated Deprecated. Do not use. */` - After: ``` /** * Deprecated: use `createSignedUrl` instead, which returns a URL that expires. * Removed in v4.0.0 (2027-01-15). Replace `getObjectUrl(key)` with * `createSignedUrl(key, { expiresInSec: 3600 })`. * * Gets a permanent public URL for an object. */ ``` Keep the original description below the deprecation note — readers still maintaining old code need it. Where a replacement doesn't exist, say what the reader does instead ("Store the object in a public bucket and read `object.publicUrl`.") rather than leaving them to guess. Source: api-reference-comments --- ## Complete example: `StorageClient` **Before** — typical weak comments: names restated, a flag undocumented, a thrown error invisible, a deprecation with no replacement. ```ts /** Storage client */ export class StorageClient { /** @param bucket bucket @param options options */ constructor(bucket: string, options?: ClientOptions) {} /** connected? */ get isConnected(): boolean {} /** region */ get region(): string {} /** This method deletes a bucket. @param force force flag */ async deleteBucket(force: boolean): Promise<boolean> {} /** Uploads an object. */ async upload(key: string, body: Buffer): Promise<ObjectMetadata> {} /** Deprecated, don't use. */ getObjectUrl(key: string): string {} } ``` **After** — every rule in this file applied at once. ```ts /** * Reads and writes objects in a single storage bucket. * * A client opens one connection per instance and reuses it for every request. * Create one client per bucket and share it across requests; the client is * safe for concurrent use. */ export class StorageClient { /** * Creates a client for the given bucket. * * @param bucket The name of an existing bucket in the caller's project. * @param options Optional. Connection options such as the region and the * request timeout. Defaults to the `us-east-1` region and a 30-second * timeout. */ constructor(bucket: string, options: ClientOptions = DEFAULT_OPTIONS) {} /** * Checks whether the client holds an open connection to the bucket. * * @returns True if the connection is open; false otherwise. */ get isConnected(): boolean {} /** * Gets the region that stores the bucket. * * @returns The region code, for example `us-east-1`. * @throws {NotConnectedError} If the client hasn't connected yet. Call * `connect` first. */ get region(): string {} /** * Deletes the bucket. * * @param force If true, deletes the bucket and every object in it. If false, * fails when the bucket still contains objects. * @returns True if the bucket was deleted; false if it didn't exist. */ async deleteBucket(force: boolean): Promise<boolean> {} /** * Uploads an object and returns its stored metadata. * * The upload replaces any object with the same key. To make the write * conditional, pass a generation to `uploadIfGenerationMatch`. * * @param key The object key, up to 1024 bytes of UTF-8. * @param body The object contents. * @returns The metadata of the stored object, including its generation * number and ETag. * @throws {QuotaExceededError} If the upload would exceed the project's * storage quota. * @throws {NotConnectedError} If the client hasn't connected yet. */ async upload(key: string, body: Buffer): Promise<ObjectMetadata> {} /** * Deprecated: use `createSignedUrl` instead, which returns a URL that * expires. Removed in v4.0.0 (2027-01-15). Replace `getObjectUrl(key)` with * `createSignedUrl(key, { expiresInSec: 3600 })`. * * Gets a permanent public URL for an object. * * @param key The object key. * @returns The public URL of the object. * @deprecated Use `createSignedUrl` instead. */ getObjectUrl(key: string): string {} } ``` The same `upload` entry as a Python docstring, in **Google Python style guide** form (`Args:` / `Returns:` / `Raises:`): ```python def upload(self, key: str, body: bytes) -> ObjectMetadata: """Uploads an object and returns its stored metadata. Args: key: The object key, up to 1024 bytes of UTF-8. body: The object contents. Returns: The metadata of the stored object, including its generation number and ETag. Raises: QuotaExceededError: If the upload would exceed the project's storage quota. NotConnectedError: If the client hasn't connected yet. """ ``` Source: api-reference-comments; Google Python style guide (docstring sections) --- ## Reference voice (A7) Reference entries describe; guides instruct. The difference is grammatical, and mixing the two inside one reference set makes entries read as inconsistent even when every fact is right. | Surface | Voice | Example | |---------|-------|---------| | Reference entry | Third-person present, descriptive | `Creates a signed URL that expires after the given interval.` | | Guide, tutorial, procedure step | Second person, imperative | `Create a signed URL, then send it to the browser.` | | Usage note inside an entry | Second person, addressed to the caller | Call `connect` before you read `region`. | Second person still earns its place in an entry's usage notes and constraints — the lines telling a caller what to do about the behavior just described. Keep it out of the summary line and the parameter, return, and exception descriptions. Hold one tense across every entry in the set; a reference where some methods "return" and others "will return" reads as two documents merged. Behavior described in the present is true whenever the reader arrives. **Before → after:** - Before: `Use this method to fetch the invoice. It will return the invoice object.` - After: `Gets the invoice for the given ID. Returns the invoice, including its line items.` Voice rules for the surrounding prose — second person, active voice, present tense, no filler — are V1–V10 in [voice-and-words.md](voice-and-words.md). Source: reference-verbs --- ## CLI help text (A6, convention) Google has no page on `--help` output, so this section is **(convention)**, built on the guide's command-line syntax notation (P9, [procedures-and-code.md](procedures-and-code.md)) — `[optional]`, `{a|b}`, `...` — and its placeholder rules (P8, same file). A complete `--help` screen carries seven parts in this order: usage line, one-line synopsis, description paragraph, positional arguments, flags with placeholder-style values and defaults, examples, and exit codes. **Before:** ``` $ shipit deploy --help Usage: shipit deploy Deploys stuff. Simply pass your key and it will deploy your app really fast. Options: --key your api key (e.g. YOUR_API_KEY) --env environment --dry-run dry run --help help ``` **After:** ``` $ shipit deploy --help Usage: shipit deploy [OPTIONS] SERVICE_NAME Deploys a service to an environment and waits for it to become healthy. The command builds the image, uploads it, and replaces one replica at a time. The rollout stops at the first replica that fails its health check. Arguments: SERVICE_NAME The name of the service to deploy, as listed by `shipit services list`. Options: --key API_KEY The API key used to authenticate. Defaults to the value of SHIPIT_API_KEY. --env {staging|prod} The target environment. Default: staging. --replicas COUNT The number of replicas to run. An integer from 1 to 50. Default: 3. --dry-run If set, prints the deployment plan and exits without changing anything. -h, --help Prints this help text and exits. Examples: Deploy the checkout service to staging: shipit deploy checkout Preview a production rollout with five replicas: shipit deploy --env prod --replicas 5 --dry-run checkout Exit codes: 0 The deployment succeeded. 1 The deployment failed and was rolled back. 2 An argument or flag was invalid. 3 The API key was missing or rejected. ``` What the rewrite fixed: the usage line shows the optional and positional parts; the synopsis states the outcome instead of selling it; every flag names its value in placeholder caps and its default; the boolean flag uses the `If set, …` action pattern from A3; exit codes let a script branch on the result. Source: convention (no Google `--help` page); code-syntax for the usage-line notation --- ## REST/HTTP reference pages (convention) Google has no REST reference page type either, so the section list below is **(convention)**. The wording inside each cell is not: parameter descriptions follow A3, error descriptions follow A4. A minimum endpoint page carries six parts: method and path as the heading, a one-sentence purpose, parameter tables split by location, the response with an example body, and an error table. ### POST /v1/buckets/{bucketId}/objects Uploads an object to a bucket and returns its stored metadata. The following table lists the path parameters: | Name | Type | Required | Description | |------|------|----------|-------------| | `bucketId` | string | Yes | The ID of the bucket that receives the object. | The following table lists the query parameters: | Name | Type | Required | Description | |------|------|----------|-------------| | `ifGenerationMatch` | integer | No | The generation the object must currently have for the write to succeed. Omit to overwrite any generation. | The following table lists the body parameters: | Name | Type | Required | Description | |------|------|----------|-------------| | `key` | string | Yes | The object key, up to 1024 bytes of UTF-8. | | `contentType` | string | No | The MIME type stored with the object. Default: `application/octet-stream`. | | `public` | boolean | No | If true, grants read access to anyone with the URL. If false, restricts access to the bucket's ACL. Default: false. | A successful request returns `201 Created` and the object's metadata: ```json { "key": "invoices/2026-08.pdf", "generation": 1724947200000001, "sizeBytes": 48213, "etag": "d41d8cd98f00b204e9800998ecf8427e", "createdAt": "2026-08-29T10:00:00Z" } ``` The following table lists the errors this endpoint returns: | Status | Code | Description | |--------|------|-------------| | 404 | `bucket_not_found` | Returned when no bucket has the given ID, or the credentials can't see it. | | 409 | `generation_mismatch` | Returned when `ifGenerationMatch` doesn't equal the object's current generation. | | 413 | `object_too_large` | Returned when the body exceeds the 5 TiB per-object limit. | | 429 | `quota_exceeded` | Returned when the project exceeds its write rate. Retry after the interval in the `Retry-After` header. | Give every endpoint the same section order and the same tables, including single-row ones. A reader scanning six endpoints reads position, not prose: a page that drops its query-parameter table reads as an endpoint that takes none. Source: convention (no Google REST reference page); api-reference-comments for the parameter and error wording -
audit-checklist.md 19.1 KB
# Audit Checklist: Rule IDs, Severity, and Scoring The frozen rule table this skill cites, plus the mechanics that turn it into a score and a ship/no-ship call. 1. [How to use this checklist](#how-to-use-this-checklist) 2. [The rule table](#the-rule-table) 3. [Severity rubric and the shippability gate](#severity-rubric-and-the-shippability-gate) 4. [Scoring mechanics](#scoring-mechanics) 5. [Finding a local style guide first](#finding-a-local-style-guide-first) 6. [Non-English documents](#non-english-documents) 7. [Automating the `[EN]` layer with Vale](#automating-the-en-layer-with-vale) 8. [Worked mini-audit](#worked-mini-audit) --- ## How to use this checklist Read this file before any audit or improve pass. Rule IDs are stable identifiers, not shorthand you can invent: cite an ID only after reading its row. They fill the Findings column "Rule (ID + name)" and, in improve mode, the change-log column "Rule ID + name". - One ID per finding; pair them (`P7 / P8`) only when a single edit fixes both — the pair takes the higher severity. - Severity comes from the table below, not from how annoying the defect felt. - Explanations and before/after pairs live in the owner file named in the last column; here each rule is one row. Use the audit output block defined in SKILL.md, section 8, unchanged. Source: skill rule (SKILL.md, section 8). --- ## The rule table Sixty rules in seven groups. The `(inferred)` and `(convention)` labels mark rows that are not verbatim Google guidance; keep the label when you cite them. Source slugs are pages under `developers.google.com/style` unless another origin is named. **R — reader and document type** | ID | Rule | Severity | [EN] | Source | Owner file | |----|------|----------|------|--------|-----------| | R1 | First paragraph states who the doc is for and what they'll accomplish | High | — | tech-writing/one "Documents" | document-types.md | | R2 | Doc type matches the reader's task: tutorial / how-to / concept / reference / README | High | — | (inferred taxonomy; Google covers procedures + concept-heading style) | document-types.md | | R3 | Prerequisites listed before the first step ("Before you begin") | High | — | procedures + Google docs practice (convention) | document-types.md | | R4 | Scope and non-scope stated | Medium | — | tech-writing/one "Documents" | document-types.md | | R5 | Ends with verification and/or "What's next" | Medium | — | (convention) | document-types.md | | R6 | Key points first; one idea per paragraph, lead sentence carries it | Medium | — | paragraphs, tech-writing/one "Paragraphs" | document-types.md | **V — voice** | ID | Rule | Severity | [EN] | Source | Owner file | |----|------|----------|------|--------|-----------| | V1 | Second person "you"; never "we"/"the user" for the reader | Medium | — | person | voice-and-words.md | | V2 | Active voice unless the actor is unknown or irrelevant | Medium | — | voice | voice-and-words.md | | V3 | Present tense; "will" only for genuinely later effects | Medium | — | tense | voice-and-words.md | | V4 | No "please" outside permission/forgiveness | Low | — | tone | voice-and-words.md | | V5 | No "simply/easily/just/quickly", superlatives, or absolutes | Low | — | excessive-claims | voice-and-words.md | | V6 | No anthropomorphism (software doesn't want/see/think) | Medium | — | anthropomorphism | voice-and-words.md | | V7 | Timeless: no "currently/now/new/soon"; no pre-announcing | Medium | — | future, timeless-documentation | voice-and-words.md | | V8 | Contractions allowed; negations prefer "isn't/don't" | Low | `[EN]` | contractions | voice-and-words.md | | V9 | Inclusive language (no master/slave, blacklist/whitelist, sanity check, gendered generic pronouns) | Medium | — | inclusive-documentation | voice-and-words.md | | V10 | Global audience: no idioms, culture-bound examples, "once" meaning "after" | Low | — | translation | voice-and-words.md | **W — words and sentences** | ID | Rule | Severity | [EN] | Source | Owner file | |----|------|----------|------|--------|-----------| | W1 | Condition or goal before the instruction | Medium | — | sentence-structure | voice-and-words.md | | W2 | One idea per sentence; short sentences | Low | — | tech-writing/one "Short sentences" | voice-and-words.md | | W3 | Abbreviations spelled out on first use | Medium | — | abbreviations | voice-and-words.md | | W4 | No e.g. / i.e. / etc. / vs. | Low | `[EN]` | abbreviations | voice-and-words.md | | W5 | can / may / might used for ability / permission / possibility | Low | `[EN]` | word-list | voice-and-words.md | | W6 | Jargon defined or avoided for the stated reader | Medium | — | jargon | voice-and-words.md | | W7 | Word-list compliance (one rule; entries live in voice-and-words.md) | Low | `[EN]` | word-list | voice-and-words.md | | W8 | American spelling; serial comma | Low | `[EN]` | highlights, commas | voice-and-words.md | **S — structure and formatting** | ID | Rule | Severity | [EN] | Source | Owner file | |----|------|----------|------|--------|-----------| | S1 | Sentence case for titles and headings | Low | — | headings | structure-and-formatting.md | | S2 | Task headings = bare imperative; concept headings = noun phrase; no "-ing" | Medium | — | headings | structure-and-formatting.md | | S3 | No skipped heading levels; no trailing period; (inferred) no heading immediately followed by a heading | Low | — | headings | structure-and-formatting.md | | S4 | Lists: complete intro sentence ending in a colon; parallel items | Medium | — | lists | structure-and-formatting.md | | S5 | Numbered lists only for sequences; bullets otherwise; description lists for pairs | Medium | — | lists | structure-and-formatting.md | | S6 | Tables: header row, intro sentence, no merged or empty cells | High | — | tables, accessibility | structure-and-formatting.md | | S7 | Notices: don't stack; Note / Caution / Warning semantics; (inferred) one per section | Medium | — | notices | structure-and-formatting.md | | S8 | Cross-references use "see"; never above/below | Medium | — | cross-references | structure-and-formatting.md | | S9 | Link text descriptive; never "click here" / "this link" | High | — | link-text, accessibility | structure-and-formatting.md | | S10 | Purposeful alt text; information never only in an image | Blocking | — | images, accessibility | structure-and-formatting.md | | S11 | Unambiguous dates; numbers zero–nine spelled out, 10+ numerals | Low | `[EN]` | dates-times, numbers | structure-and-formatting.md | | S12 | Bold for UI elements, code font for code, italics sparingly for terms | Low | — | text-formatting | structure-and-formatting.md | **P — procedures and code** | ID | Rule | Severity | [EN] | Source | Owner file | |----|------|----------|------|--------|-----------| | P1 | Steps numbered, one imperative action per step; a missing required step escalates to Blocking | High | — | procedures | procedures-and-code.md | | P2 | Single step = bullet; sub-steps a/b/c then i/ii/iii | Low | — | procedures | procedures-and-code.md | | P3 | Optional steps prefixed "Optional:" | Low | — | procedures | procedures-and-code.md | | P4 | Each step says where to act and what result to expect | Medium | — | procedures | procedures-and-code.md | | P5 | UI names bold, casing matches the UI; click / tap / select by device | Low | — | ui-elements | procedures-and-code.md | | P6 | Code font for filenames, paths, commands, flags, params, values — not product names | Medium | — | code-in-text | procedures-and-code.md | | P7 | Every code sample introduced by a sentence ending in a colon | Medium | — | code-samples | procedures-and-code.md | | P8 | Placeholders `ALL_CAPS_WITH_UNDERSCORES`, no MY_/YOUR_, explained after the sample | Medium | — | placeholders | procedures-and-code.md | | P9 | Command-line syntax: `[optional]`, `{a\|b}`, `...` | Low | — | code-syntax | procedures-and-code.md | | P10 | Samples runnable, minimal, ≤80-char lines, expected output shown | Medium | — | code-samples, tech-writing/two "Sample code" | procedures-and-code.md | | P11 | Every command, flag, parameter, and behavior verified in code or given by the user | Blocking | — | (skill rule) | procedures-and-code.md | **A — API reference, docstrings, CLI help** | ID | Rule | Severity | [EN] | Source | Owner file | |----|------|----------|------|--------|-----------| | A1 | Every public class / method / field / constant documented | High | — | api-reference-comments | api-reference.md | | A2 | Method descriptions open with the category verb (Gets / Sets / Checks whether / Creates / Returns…); no "This method…" | Medium | — | api-reference-comments | api-reference.md | | A3 | Non-boolean params start "The…/A…"; booleans "If true, … If false, …" or "True if …; false otherwise" | Medium | — | api-reference-comments | api-reference.md | | A4 | Return values and exceptions documented ("Thrown when…") | High | — | api-reference-comments | api-reference.md | | A5 | Deprecated elements name the replacement in the first sentence | High | — | api-reference-comments | api-reference.md | | A6 | CLI help: usage line, one-line synopsis, every flag described, placeholders in caps | Medium | — | code-syntax + (convention — no Google `--help` page) | api-reference.md | | A7 | Reference text is descriptive third-person present; guides are imperative | Low | — | reference-verbs | api-reference.md | **N — release notes, changelogs, migration** | ID | Rule | Severity | [EN] | Source | Owner file | |----|------|----------|------|--------|-----------| | N1 | Newest version first; `Unreleased` section; ISO dates in version headings | Medium | — | keepachangelog.com | release-notes.md | | N2 | Entries grouped Added / Changed / Deprecated / Removed / Fixed / Security | Medium | — | keepachangelog.com | release-notes.md | | N3 | Entry = what changed + reader impact + action; never raw commit messages | High | — | keepachangelog.com | release-notes.md | | N4 | Breaking changes called out first with a link to migration steps | High | — | (convention) | release-notes.md | | N5 | Deprecation entries name the replacement and removal version/date | High | — | keepachangelog.com, api-reference-comments | release-notes.md | | N6 | Migration guide is a procedure with before/after snippets and a verify step | High | — | procedures (applied) | release-notes.md | Source: Google Developer Documentation Style Guide (slug per row), Google Technical Writing One and Two, keepachangelog.com for N1–N5; labelled rows are this skill's own. --- ## Severity rubric and the shippability gate Severity answers what breaks for the reader, not how many words change. Each rule carries its own severity above. | Severity | Definition | |----------|-----------| | Blocking | Facts wrong or unverifiable; a procedure that can't be completed; information carried only by an image, or an image with no alt text | | High | Wrong doc type, missing audience or prerequisites, steps buried in paragraphs, vague link text, headerless tables, undocumented API elements, changelog entries with no reader impact | | Medium | Passive voice, future tense, "we"/"the user", anthropomorphism, time-bound wording, unexplained abbreviations, misused notices, missing intro sentences, placeholder style | | Low (mostly `[EN]`) | Word list, serial comma, contractions, spelling, UI-label casing, Latin abbreviations, numbers and dates | Blocking is a gate, not a deduction: one Blocking finding makes a doc `Shippable: no` at 9/10, and a doc with none is shippable at 6/10 — separate report lines. Work Blocking → High → Medium → Low, and within that, structure before voice before words: sentences polished in a section you then delete are wasted, and word edits made before a restructure get made twice. Source: skill rule — severity rubric and protocol step 5 in SKILL.md, section 8. --- ## Scoring mechanics Score = Quick Diagnostic rows passed: ten rows, one point each, no partial credit — a row passes only if the whole document satisfies it; a row with no applicable content passes (a concept page with no procedures passes rows 5 and 6), and A- and N-rule findings affect `Shippable` and the Rewrite plan, not the score. Row 10 auto-passes for a non-English doc. Name the band from SKILL.md's Scoring section; don't restate it. Findings cite the rule ID, not the row number: | Row | Checks | Rule IDs | |-----|--------|----------| | 1 | Audience and outcome up front | R1 / R4 | | 2 | Doc type fits the task | R2 | | 3 | Facts verified against code | P11 | | 4 | Headings scan as a contents list | S1 / S2 | | 5 | Numbered imperative steps, condition first | P1 / W1 | | 6 | Samples introduced, placeholders explained | P7 / P8 | | 7 | Second person, active, present, no filler | V1–V6 | | 8 | Links, cross-references, images, tables accessible | S6 / S8 / S9 / S10 | | 9 | Timeless | V7 | | 10 | `[EN]` word list, serial comma, contractions, spelling | W7 / W8 / V8 | The `**Diagnostic:**` line repeats the number and names the failures: `N/10 — failed rows: <n> (<one-line reason>), …`, one clause per failed row phrased as the observed defect; passing rows aren't listed. Source: skill rule (SKILL.md, Scoring and Quick Diagnostic). --- ## Finding a local style guide first A project's conventions outrank Google's; find them before filing a finding against a deliberate choice. | Look for | Usually settles | |----------|-----------------| | `CONTRIBUTING.md` | Docs workflow, tone, required sections | | `STYLE.md`, `STYLEGUIDE.md`, `docs/style-guide.md` | Explicit house style | | `docs/contributing/*.md` | Per-docs-set conventions | | `.vale.ini` | Linter package and word list in force | | `.markdownlint*` | Heading levels, line length, list markers | | `.github/PULL_REQUEST_TEMPLATE.md` | The docs checklist the team applies | When no file states the rules, the docs still do: - `grep -rin "log in\|sign in" docs/` — the winner by count is the house term. - Heading case: sample a dozen `^#{1,3} ` lines; title case throughout is a convention, not twelve findings. - Placeholders: `grep -rn "YOUR_\|<[a-z-]*>" docs/` — a consistent house form beats P8. - Notices: match the toolchain's syntax (`> **Note**`, `:::note`, `{: .note }`). Precedence: project guide → Google → Merriam-Webster (spelling), Chicago (general), Microsoft Writing Style Guide (technical). A convention wins in its scope only if it is consistent; an inconsistent habit is a finding. Record it on the report's `**Local style guide:**` line: the file and that you honored it, any rule you deferred (`STYLE.md — title-case headings, S1 not applied`), or `none — Google applies`. Source: developers.google.com/style/about for precedence; the file list and grep probes are convention. --- ## Non-English documents Structural and content rules apply unchanged: all of R, S1–S10 and S12, P1–P11, A1–A6, N1–N6, plus V1, V2, V3, V6, V7, V9 and W1, W3, W6. A Japanese how-to still needs an audience statement, numbered steps, and verified flags. Skip the `[EN]` rules — V8, W4, W5, W7, W8, S11 — which encode English spelling, word choice, and punctuation. V4, V5, V10, W2, S1, S2, and A7 sit in between: the intent transfers, the exact test doesn't. Record the language and the skipped layer on the report's `**Language:**` line: `de — [EN] rules skipped (V8, W4, W5, W7, W8, S11)`. Row 10 then auto-passes, out of the same 10. For a mixed-language doc, apply the `[EN]` rows to the English passages only and record `mixed (en/pl) — [EN] applied to English text`. Never translate a document unless asked: it hides the page from its readers and turns a style audit into a content change. Source: skill rule (SKILL.md, Core Principle) with translation and global-audience guidance. --- ## Automating the `[EN]` layer with Vale Vale with the Google package catches most Low-severity English findings mechanically. A minimal configuration at the repo root: ```ini StylesPath = .vale/styles MinAlertLevel = suggestion Packages = Google [*.md] BasedOnStyles = Vale, Google ``` Run `vale sync` to fetch the package into `.vale/styles`, then `vale docs/`. Coverage, mapped to these IDs (inferred — the package's rule names are Google's, not this skill's): roughly V2–V5, V8, W4, W7, W8, and S1 — passive voice, "will", "please", "simply/just", contractions, e.g./i.e., the word list, spelling and serial comma, exclamation marks, heading case. Out of reach, so still a human pass: every R, A, and N rule, P11, plus S6 table semantics, S10 image-only information, P1 step decomposition. A green run is evidence for row 10, not a passing audit. Source: Vale — errata-ai/Google package. --- ## Worked mini-audit A README excerpt for `shipd`, a fictional deploy CLI: ```markdown # Getting Started With Shipd Shipd is the fastest way to deploy your app. Currently, we are excited to announce that our new dashboard will be shown once the user has logged in. ## Installing The CLI Please simply run the install script below. After that you should click on the Deploy button in order to deploy, and the status will be changed. For configuration options, click here. `curl -sL get.shipd.io | sh && shipd deploy --key=<your-key> --turbo` | shipd deploy | shipd rollback | | --- | --- | ``` Report header (full template in SKILL.md, section 8): ``` **Score:** 1/10 — ≤4 band **Shippable:** no (Blocking below) **Diagnostic:** 1/10 — failed rows: 1 (no audience or outcome), 3 (`--turbo` unverifiable), 4 (title case, "-ing"), 5 (steps in prose), 6 (no intro, `<your-key>`), 7 ("we", passive, "please", "fastest"), 8 ("click here", headerless table), 9 ("Currently", pre-announced dashboard), 10 ("log in", "in order to") ``` **Findings:** | # | Location | Rule (ID + name) | Before | After | Severity | |---|----------|------------------|--------|-------|----------| | 1 | Headings | S1 sentence case; S2 imperative | "Getting Started With Shipd" | "Get started with Shipd" | Medium | | 2 | Intro | V7 timeless; V1 second person | "Currently, we are excited to announce…" | "After you sign in, the dashboard appears." | Medium | | 3 | Install paragraph | V4 no please; V5 no simply | "Please simply run the install script" | "To install the CLI, run:" | Low | | 4 | Install paragraph | P1 numbered steps; W1 condition first | "you should click on the Deploy button" | "1. Click **Deploy**. The status changes to **Deploying**." | High | | 5 | Command line | P7 colon intro; P8 placeholders | `--key=<your-key>`, no intro | `--key=API_KEY`, explained after the sample | Medium | | 6 | `--turbo` flag | P11 facts verified in code | In no source file or help output | `TODO(verify): --turbo not in cmd/deploy.go` | **Blocking** | | 7 | "click here" | S9 descriptive link text | "For configuration options, click here." | "see Configure Shipd" | High | | 8 | Command table | S6 header row and intro | Commands used as the header row | Intro sentence + `Command` / `Description` header | High | **Score.** Row 2 passes — a README is the right type for a first deploy; the other nine fail as the Diagnostic line lists. That is **1/10**, the `≤4` band: rewrite from the README skeleton; `Shippable: no` until finding 6 resolves. Source: skill rule — worked example applying the rule IDs above. -
document-types.md 11.5 KB
# Document Types Deepens SKILL.md §1 (Know the Reader and the Document's Job): which document type fits a given reader's question, the audience and scope statements that open a page, prerequisite and closing patterns, skeletons for the five common page types, paragraph structure, and the self-editing pass for a full doc set. ## Choose the Document Type Every page answers one reader's question. Match the type to the question before writing a word — mixing types on one page forces the reader to read everything to find their one task. | Type | Reader's question | Signals it's the right type | Must not contain | |---|---|---|---| | Tutorial | "Walk me through building this, start to finish?" | Reader is new to the feature and wants one guaranteed win | Exhaustive option lists; alternate paths; unexplained jargon | | How-to | "How do I do this one task?" | Reader already has a working setup and a specific goal | Onboarding scaffolding; the "why" behind the feature | | Concept | "What is this, and why does it work this way?" | Reader wants a mental model before acting | Numbered steps; command output; UI clicks | | Reference | "What are the exact parameters, fields, or flags?" | Reader already knows what they want and needs the exact facts | Narrative explanation; opinion; "why" content | | README / getting started | "What is this, and how do I start?" | Reader is arriving for the first time, evaluating or installing | Deep conceptual explanations; exhaustive configuration matrices | **One task per page (R2).** A page that answers two of these questions answers neither well. When a draft mixes types — a concept paragraph, then steps, then a reference table — split it along these lines rather than adding more headings to the same page. This taxonomy isn't in Google's guide itself; it's inferred from the guide's Procedures page and its concept-heading guidance (S2, structure-and-formatting.md), generalized to the other common developer-doc shapes. Source: procedures, headings (inferred taxonomy — R2) ## Audience and Scope Statements A page's first paragraph does two jobs before any instruction starts: tell the reader whether they're on the right page, and tell them what they'll be able to do afterward (R1). A scope and non-scope statement extends this by naming what's deliberately left out, with a link to where that content lives (R4). **Audience statement** - Before: "This page describes the notification system." - After: "This page is for backend developers adding Shipit webhook notifications to an existing service. It assumes you can deploy an HTTP endpoint and have admin access to a Shipit project." **Scope and non-scope statement** - Before: "This guide covers webhooks." - After: "This guide shows how to subscribe to deployment events and verify webhook signatures. It doesn't cover configuring retry policies or building a custom event bus — see Webhook retry policies." **"What you'll do" intro** - Before: "Shipit is a deployment platform that many teams use to ship software faster and reduce risk across environments." - After: "By the end of this page, you can configure Shipit to redeploy your app automatically whenever a build passes CI." The non-scope sentence is what rescues a reader who followed a search result to the wrong page — without it, they read the whole doc before discovering it doesn't cover their case. Source: tech-writing/one "Documents" (R1, R4) ## Before You Begin and What's Next **Before you begin (R3)** lists everything the reader needs before step 1 — nothing they'll discover along the way. It belongs there, not folded into step 4. Belongs in prerequisites: - Software and minimum versions ("Shipit CLI 4.2 or later") - Required roles or permissions ("Admin role on the target project") - Tools or SDKs that must already be installed - Accounts, API keys, or a specific starting state ("A GitHub repository connected to Shipit") Doesn't belong: conceptual background (link a concept page instead), and setup steps that belong to a different, already-linked page. - Before: "4. Before running this command, make sure you've installed the Shipit CLI and have admin access on the project." - After: ``` ## Before you begin - Shipit CLI 4.2 or later - Admin role on the target project - A GitHub repository connected to Shipit ``` **Verification and What's next (R5).** A procedure that ends on the last action leaves the reader guessing whether it worked. Close with a verification step phrased as a check, and the expected output, then point to the next task. - Before: "4. Run `shipit deploy`. That's it — you're done." - After: ```` Confirm that the deployment succeeded: ``` shipit status my-app ``` The command prints `Status: healthy`. ## What's next - Configure automatic rollbacks - Send deployment events to Slack ```` The exact headings "Before you begin" and "What's next", and the "Confirm that…" verification phrasing, aren't quoted rules in Google's guide — they're the convention this skill standardizes on, built from the guide's Procedures page and Google's own documentation practice. Source: procedures; tech-writing/one "Documents" (R3, R5; heading wording and verification phrasing are convention) ## Skeletons (convention) None of these page shapes are named in Google's guide — they're this skill's convention for applying R1-R6 consistently. Use them as a starting outline, not a rigid template. **README** 1. Project name and one-line purpose — what it does, in one sentence 2. Who it's for — the audience statement 3. Quick start — three steps: install, configure, run 4. Links out — full docs, contributing guide, license **Getting started (docs-site landing page)** 1. Title 2. What you'll do — the outcome statement (R1) 3. Before you begin — prerequisites (R3) 4. Install, first command, verify 5. What's next (R5) **Tutorial** 1. Title stating the goal ("Build a webhook listener with Shipit") 2. What you'll build — one or two sentences, the end state 3. Before you begin — prerequisites (R3) 4. Numbered steps, each with the expected result stated after the action 5. Clean up — remove anything created only for the tutorial 6. What's next (R5) **How-to** 1. Title as a bare imperative task ("Configure a webhook endpoint") — heading form is S2 (structure-and-formatting.md) 2. One sentence of context, only if more than one approach exists 3. Before you begin — only what's specific to this task, not full onboarding 4. Numbered steps, condition before instruction 5. A short verification step (R5) **Concept** 1. Title as a noun phrase ("Webhook signing") — heading form is S2 (structure-and-formatting.md) 2. What it is — key point first (R6) 3. Why it works this way — the mechanism or trade-off 4. How it fits with other pieces 5. A link to the procedure that acts on this concept Source: convention ## Paragraphs and Key Points First A paragraph carries one idea. Its lead sentence states that idea; everything after supports it. Aim for three to five sentences, and put the information the reader needs before the background that explains it (R6). **Key information before background** - Before: "Shipit's build system uses a layered cache to speed up repeated builds, an approach the team added after users on large monorepos reported slow CI times, and it now applies to every project by default." - After: "Shipit caches build layers by default, so repeated builds finish faster. The team added this after users on large monorepos reported slow CI times." **One idea per paragraph** - Before: "Deployments run in isolated containers, so one project's build never affects another. You can also configure a custom domain for each environment, which requires DNS access and a verified certificate before Shipit will route traffic to it." - After: ``` Deployments run in isolated containers, so one project's build never affects another. You can configure a custom domain for each environment. This requires DNS access and a verified certificate before Shipit routes traffic to it. ``` A reader skimming only lead sentences should still get the page's argument — that's the test for whether the point comes first. Source: tech-writing/one "Paragraphs" (R6) ## Self-Editing and Organizing Large Doc Sets A single page needs a different check than a doc set spanning dozens of pages. Apply both. **Self-editing a page:** | Technique | What to do | Catches | |---|---|---| | Adopt the reader's persona | Reread as the stated audience, not as the author who already knows the system | Missing prerequisites, unexplained jargon | | Read it aloud | Read every sentence out loud, one at a time | Run-ons, missing words, buried passive voice | | Come back later | Leave a draft for a few hours or a day before the final pass | Errors invisible to a tired eye | | Run the checklist | Walk the page against the Quick Diagnostic rows in SKILL.md | Systemic gaps a single read-through misses | | Find a peer editor | Have someone unfamiliar with the feature follow the doc | Steps that only work because the author knows a hidden step | **Organizing a doc set:** - Outline first. Write the heading structure before any prose, and get it approved before filling it in. - State the set's purpose up front. The landing page's first sentence says what the set covers, the same way a single page's first paragraph does (R1). - Headings as a table of contents. A reader who reads only the headings across the set should be able to tell what's where and pick the right page. - Keep tutorials and how-tos on separate pages. A tutorial's teaching narrative and a how-to's task focus interfere with each other on the same page — this is R2's one-task-per-page rule applied across an entire set, not just one page. Source: tech-writing/two "Self-editing" and "Organizing large documents" ## Worked Example A mixed page interleaves concept, steps, and reference. Here's one before a split: ```markdown # Webhooks Shipit webhooks let external services react to deployment events without polling. Every payload is signed with an HMAC-SHA256 digest so subscribers can verify the sender before trusting the contents; this became necessary once webhook endpoints started appearing on the public internet, where anyone could send forged events. 1. Open your project settings and click **Webhooks**. 2. Click **Add endpoint** and enter your HTTPS URL. 3. Copy the signing secret shown after you save. The `event` field is one of `deploy.started`, `deploy.succeeded`, or `deploy.failed`. The `signature` header holds the HMAC digest, and `timestamp` is a Unix epoch integer used to reject replayed requests older than five minutes. 4. Deploy your project and check your endpoint's logs for a request. ``` A reader who only wants to configure an endpoint has to read a security rationale and a payload reference to find steps 1 through 4, scattered across the page. Splitting by task (R2) gives two linked pages — headings only, since the prose above already carries the content: **Page 1 — Concept: "Webhook signing"** 1. Webhook signing 2. Why Shipit signs every payload 3. Payload fields (`event`, `signature`, `timestamp`) — reference material about the payload sits with the concept, not the procedure 4. See "Configure a webhook endpoint" to set one up **Page 2 — How-to: "Configure a webhook endpoint"** 1. Configure a webhook endpoint 2. Before you begin — an HTTPS-reachable endpoint, admin role on the project 3. Numbered steps 1-3 (unchanged) 4. Confirm that your endpoint received a request — the stranded step 4 becomes a verification step, with the expected log line stated 5. What's next Source: convention (applies R1-R6 together) -
procedures-and-code.md 14 KB
# Procedures and Code This file owns **P1–P11**: step-by-step instructions, UI verbs, code in prose, code samples, placeholders, and command-line syntax. W1 (condition before instruction) belongs to [voice-and-words.md](voice-and-words.md); S12 (the bold/code-font/italics summary) belongs to [structure-and-formatting.md](structure-and-formatting.md); A1–A7 (API reference text, docstrings) belong to [api-reference.md](api-reference.md) — mentioned here only where a procedure or sample touches them. ## 1. Anatomy of a Procedure (P1–P4) A procedure exists to get one reader from a starting state to a finished one. Every step names a single imperative action, states or implies where to act, and lets the reader confirm what happened before moving on. **Rules:** - **P1 — Numbered steps, one action each.** Split "click X and then configure Y" into two steps. If a step has two actions, the reader can't tell which one failed. - **Condition before instruction (W1, owned by voice-and-words.md).** "If you want X, do Y" — not "Do Y if you want X." The reader decides whether the step applies before reading how to do it. - **P4 — Where to act, what to expect.** Name the screen, menu, or file the action happens in, and state the observable result: a new status, a redirect, a file on disk. - **P3 — "Optional:" prefix.** Not "(Optional)", not "You can also…" — the word "Optional:" at the start of the step, so a scanning reader can skip it without reading the rest. - **P2 — Single step = bullet.** A procedure with exactly one action is a bullet (`*` or `-`), never "1." — a numbered list of one implies steps 2 and 3 are coming. - **P2 — Sub-steps a/b/c, then i/ii/iii.** Use letters for a sub-sequence inside a numbered step; drop to roman numerals only when a lettered sub-step itself has to branch — for example, by operating system. Don't add a third level to document a style preference. - **Document the shortest path.** When the UI and the CLI both accomplish the task, pick one and mention the other in a single sentence, not as a parallel procedure. **Before:** ```markdown To turn on automatic deployments, you should first go to the project settings and look for the Deployments tab, then enable it, and after that you can pick which branch you want it to watch. You should also set a build command if you have one, and once you're done you can save it, and it will deploy automatically from then on when you push to that branch. ``` **After:** ```markdown 1. In the **shipit** console, open your project and click **Settings**. 2. Click **Deployments**. 3. Turn on **Automatic deployments**. 4. Select the branch to watch: a. Click the **Branch** menu. b. Choose the branch, for example `main`. 5. Optional: Enter a build command, for example `npm run build`. 6. Click **Save**. The **Status** column shows **Watching** next to the selected branch. ``` A step that must branch by platform drops to roman numerals inside the lettered sub-step: ```markdown 1. Open a terminal. a. On macOS or Linux, run `chmod +x shipit`. i. If `shipit` isn't on your `PATH`, move it to `/usr/local/bin`. ii. If it is, skip to step 2. b. On Windows, skip this step. ``` A single-action procedure stays a bullet: ```markdown To restart the deployment watcher: * Click **Restart** in the **Deployments** panel. ``` Source: procedures ## 2. UI Elements and Device Verbs (P5) **Rules:** - **Bold the element's name**, not its type: "Click **Save**", not "Click the **Save** button." Name the type only when the label alone is ambiguous ("click the **Region** dropdown" when a section is also called Region). - **Casing matches the UI**, with one exception: an ALL-CAPS label in the interface is written in sentence case in prose. A button rendered `SUBMIT ORDER` is still "click **Submit order**." - **Menu paths** bold each segment and join with `>`: "**File > Save as**" (inferred — the guide doesn't give this exact notation, but it follows directly from bolding UI element names in sequence). - **Device verbs** match how the reader is expected to interact with the target, not the writer's own device. | Verb | Situation | Example | |------|-----------|---------| | Click | Mouse or trackpad — buttons, links, checkboxes | "Click **Deploy**." | | Tap | Touchscreen — the same targets on mobile or tablet | "Tap **Deploy**." | | Select | Device-agnostic — menu items, dropdown options, radio buttons | "Select **Production** from the **Environment** menu." | | Enter | Typing a value into a field | "Enter your project ID." | | Type | Free-text input where "enter" would also read correctly — the two are interchangeable (inferred) | "Type a description for the release." | | Choose | Picking one option among several presented together — close in meaning to "select" (inferred) | "Choose a region for the new environment." | **Before → after:** - "Click on the SAVE CHANGES button to save your changes." → "Click **Save changes**." - "Go to File, then Export, then click on PDF." → "Click **File > Export > PDF**." Source: ui-elements ## 3. Code in Text (P6) **Gets code font:** filenames, paths, commands, flags, parameters, values, method and function names, class names, HTTP status codes, environment variables. **Doesn't:** product names, UI labels (those are bold, per P5), and general concepts ("the deploy step," not `the deploy step`). Code isn't a part of speech: don't inflect it as a verb or pluralize it by adding a suffix outside the backticks. Add a plain-English noun instead — "call the `close` method," not "`close`ing the file"; "`Widget` objects," not "`Widget`s." **Before → after:** - "Open the config.yaml file and set the timeout value to 30 seconds." → "Open `config.yaml` and set `timeout` to `30`." - "Run shipit deploy with the dash-dash-force flag to skip confirmation." → "Run `shipit deploy --force` to skip confirmation." - "The API returns a 404 Not Found if SHIPIT_API_KEY isn't set." → "The API returns `404 Not Found` if `SHIPIT_API_KEY` isn't set." - "After closing() the file, check for leftover Widgets — the Shipit CLI logs them." → "After you call the `close` method, check for leftover `Widget` objects — the shipit CLI logs them." (the method and class stay in code font; the product name doesn't) Source: code-in-text ## 4. Code Samples (P7, P10) **Rules:** - **P7 — Introduce every sample with a sentence ending in a colon.** A bare code block gives the reader no reason to read it and no way to know what it's for. - **One concept per sample.** A sample that shows authentication and retry logic in one block forces a reader debugging retries to also parse auth. - **Runnable and minimal.** Nothing in the sample should be unrelated to the concept it demonstrates; nothing needed to run it should be missing. - **Realistic names.** Production-shaped data, not `foo`/`bar` — a reader pattern-matches against their own variables and data. - **Wrap at 80 characters.** Long lines force horizontal scrolling in both the doc and the terminal the reader pastes into. - **Show expected output** in a second fenced block, immediately after the sample. - **Comments explain why**, not what the syntax already says. - **Tag the language** on every fence (` ```python `, ` ```bash `) — untagged blocks can't be highlighted or reliably copied (convention). **Before → after (function):** ```python # before — no intro, mixed concepts, foo/bar, no output shown def foo(bar): x = bar['data'] result = [] for i in x: if i['active'] == True and i['score'] > 50 and i['region'] in ['us', 'eu', 'apac']: result.append(i['name']) return result ``` The following function returns the names of active users in a supported region who scored above 50: ```python SUPPORTED_REGIONS = ("us", "eu", "apac") def high_scorers(users): # Scoring only applies in supported regions, so filter first. return [ user["name"] for user in users if user["active"] and user["score"] > 50 and user["region"] in SUPPORTED_REGIONS ] ``` ``` >>> high_scorers(users) ['Priya Patel', 'Sam Nguyen'] ``` **Before → after (CLI):** ```markdown To see your deployments just run this: shipit list-deployments --all --verbose --since=2026-01-01 --until=2026-08-29 --format=json --pretty | jq '.[] | select(.status=="failed")' ``` The following command lists deployments that failed since the start of the year: ```bash shipit list-deployments \ --since=2026-01-01 \ --status=failed ``` ``` DEPLOYMENT BRANCH STATUS FINISHED d-8f2a1c main failed 2026-03-14T10:02:00Z ``` Source: code-samples, tech-writing/two "Sample code" ## 5. Placeholders (P8) A placeholder stands in for a value the reader supplies. Write it `ALL_CAPS_WITH_UNDERSCORES` — never `<your-api-key>` (angle brackets read as literal characters to a reader who doesn't know the convention) and never `MY_PROJECT_ID` or `YOUR_API_KEY` (the prefix reads as part of the name). After the sample, list what each placeholder means in a "Replace the following:" block. **Before:** ```bash shipit deploy --key=<your-api-key> --project=YOUR_PROJECT_ID ``` **After:** Deploy your project by running the following command: ```bash shipit deploy --key=API_KEY --project=PROJECT_ID ``` Replace the following: - `API_KEY`: the key from the project's **Settings > API keys** page. - `PROJECT_ID`: the project ID shown at the top of the **Overview** page. Source: placeholders ## 6. Command-Line Syntax (P9) | Notation | Meaning | Example | |----------|---------|---------| | `[optional]` | The argument may be omitted | `shipit deploy [--dry-run]` | | `{a\|b}` | Choose exactly one | `shipit logs {--tail\|--since=DATE}` | | `...` | The argument repeats | `shipit tag ITEM...` | | `ALL_CAPS` | A placeholder, not a literal | `shipit init PROJECT_NAME` | **One command per line** (convention). Don't chain unrelated commands with `&&` in a sample unless the sample's stated purpose is to show chaining. **Long commands wrap with `\` continuation**, one flag per line, as in the CLI sample in section 4. **Prompt characters.** Whether to show a leading `$` isn't specified in the guide (convention): this skill's default is to omit it — the fenced ` ```bash ` tag already marks the block as a shell command, and a bare `$` gets pasted verbatim by readers who don't know to drop it. Follow a project's existing samples if they already include `$` — local convention wins (see SKILL.md's precedence rule). **Filenames** written inside docs — sample config files, script names — are lowercase, hyphenated, ASCII: - "See Getting_Started.MD for setup instructions." → "See `getting-started.md` for setup instructions." Source: code-syntax, filenames ## 7. Sample-Code Quality Checklist (P10) | Check | Why | Example fix | |-------|-----|--------------| | Language tag on every fence | Without it, editors and readers can't syntax-highlight or copy cleanly | ` ``` ` → ` ```python ` | | Lines wrap at 80 characters | Long lines force horizontal scrolling in docs and terminals | Break a long flag list after the first flag with `\` | | One concept per sample | A reader debugging one idea shouldn't have to parse three | Split an auth-and-retry sample into two samples | | Realistic names, no foo/bar | `foo`/`bar` carries no information about real data | `def foo(bar)` → `def high_scorers(users)` | | Comments explain why, not what | "# add 1" repeats the syntax; "why" earns the reader's attention | `# add 1` → `# Retry once before failing the request` | | Placeholders in `ALL_CAPS`, explained after | `<your-key>` reads as a literal or gets pasted verbatim | `--key=<your-key>` → `--key=API_KEY` + a "Replace the following" entry | | Expected output shown in a second block | Without it, the reader can't tell if the sample worked | Add a fenced block with the actual return value or CLI output | | Sample is runnable as shown | An undefined variable or missing import fails silently for the reader | Add the missing `import` or define the variable inline | | Introduced by a sentence ending in a colon | A bare code block gives no reason to read it | "Here's code:" → "The following command lists failed deployments:" | | Verified against code or `--help`, not invented | An unverified flag teaches a command that fails | Confirm `--status` exists via `shipit deploy --help` (see P11) | Source: code-samples, tech-writing/two "Sample code" ## 8. Verify Facts Before Style (P11) P11 is the one blocking rule in this file, and it comes before every other rule here: a beautifully formatted step for a flag that doesn't exist teaches the reader something false with total confidence. Style makes a doc readable; it does nothing to make a doc true. Trace every command, flag, parameter, default, and described behavior to one of four sources before it goes in a doc: 1. **The parser or argument definitions.** Grep the CLI's flag-parsing code (`argparse`, `cobra`, `clap`, `yargs`, or the project's own dispatcher) for the exact flag name, type, and default value. 2. **`--help` output.** Run the actual command and read its usage text; don't reconstruct it from memory of a similar tool or an older version. 3. **Tests.** A flag's real behavior — including edge cases — often matches its test fixtures more precisely than its comments or its `--help` string. 4. **The user.** When no code is reachable, ask; don't infer a plausible-sounding default. When none of the four resolves a fact, write `TODO(verify): confirm whether --status accepts a comma-separated list` inline and move on. Never fill the gap with a guess that reads as confident prose — a `TODO(verify)` marker is visible and fixable; a wrong sentence that reads well is neither. The skill's rule (SKILL.md, section 8): "Never include a command, flag, or parameter you didn't see in code or receive from the user." A wrong polished doc is worse than an ugly right one — polish signals authority, so a reader trusts a wrong flag name precisely because the sentence around it reads well. An ugly doc with a `TODO(verify)` marker at least tells the reader where the doc stops vouching for itself. Source: skill rule -
release-notes.md 17.8 KB
# Release Notes, Changelogs, and Migration Guides A changelog is read with a version bump half-typed. This file owns rules **N1–N6**: the file skeleton, the entry pattern, breaking-change placement, deprecation wording, and the migration-guide procedure. Structure comes from Keep a Changelog; the sentences inside each entry follow the same rules as the rest of the docs set. Examples use a fictional analytics client, Meridian SDK (`meridian-js`) and its CLI `meridian`. ## Table of Contents - [Who reads a changelog, and when](#who-reads-a-changelog-and-when) - [The Keep a Changelog skeleton (N1, N2)](#the-keep-a-changelog-skeleton-n1-n2) - [Writing an entry (N3)](#writing-an-entry-n3) - [Breaking changes (N4)](#breaking-changes-n4) - [Deprecations (N5)](#deprecations-n5) - [The Google language layer on entries](#the-google-language-layer-on-entries) - [Release notes vs changelog](#release-notes-vs-changelog) - [Migration guides (N6)](#migration-guides-n6) ## Who reads a changelog, and when The reader is upgrading. They arrived from a dependency-bot pull request, a pinned version they are about to unpin, or a bug that appeared after someone else upgraded. They read one version block, maybe two, and leave. They bring three questions: | Reader question | The entry answers it with | The entry fails when it says | |---|---|---| | What changed? | The named API, flag, file, or behavior | "Various fixes and improvements" | | Does it affect me? | The condition under which behavior differs | "Refactored the query internals" | | What do I do? | The concrete action, or nothing at all | "See the diff for details" | A generated commit log answers none of them: it is written for the committer, one line per commit rather than per user-visible change, naming internal modules the reader never imports, with no impact statement because the author already knew the impact. **Before** — the release section pasted from `git log --oneline`: ```markdown ## 4.0.0 - 8f21c0a refactor(query): normalize ordering in the planner - 3ac9e14 Merge pull request #791 from meridian-labs/query-order - b92ee05 fix sync ``` **After** — one entry per user-visible change, carrying impact and action: ```markdown ### Changed - `meridian.query()` returns rows in ascending `timestamp` order instead of insertion order. If your code depended on insertion order, sort explicitly with `orderBy('_ingested')`. ([#791](https://github.com/meridian-labs/meridian-js/pull/791)) ``` The filter: a change belongs in the changelog when a reader could notice it without reading your source. Internal refactors, test additions, and CI edits stay out. Source: keepachangelog.com ## The Keep a Changelog skeleton (N1, N2) Keep one `CHANGELOG.md` at the repository root. Order versions newest first, keep an `[Unreleased]` section on top, date each released version in ISO format, and group entries under the six category sub-headings: ````markdown # Changelog All notable changes to Meridian SDK are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added - `meridian.events.replay()` reprocesses stored events from a saved cursor, so you can rebuild a downstream table without re-sending traffic. ([#812](…)) ## [4.0.0] - 2026-08-14 ### Added - `meridian.events.record()` stores an event and returns the stored record, including the server-assigned `id`. ([#755](…)) ### Changed - **Breaking:** `meridian.query()` returns rows in ascending `timestamp` order instead of insertion order. Sort explicitly with `orderBy('_ingested')` to keep the old order. See [Migrate from 3.x to 4.0](docs/migrate-3-to-4.md). ([#791](…)) ### Removed - **Breaking:** `meridian.track()` is removed. Call `meridian.events.record()`, which accepts the same payload. Deprecated in 3.4.0. See [Migrate from 3.x to 4.0](docs/migrate-3-to-4.md). ([#803](…)) ### Fixed - `meridian sync` no longer drops events when the connection times out mid-batch; an interrupted batch restarts from the last acknowledged offset. Affects 3.1.0 through 3.4.0. ([#799](…)) ## [3.4.0] - 2026-06-30 ### Added - `meridian query` accepts `--output json` and prints one JSON object per row, so you can pipe results into `jq`. ([#780](…)) ### Deprecated - `meridian.track()` is deprecated in favor of `meridian.events.record()` and is removed in 4.0.0. Both accept the same payload; `record()` returns the stored event instead of `undefined`. ([#755](…)) ### Security - Upgraded `node-fetch` to 3.3.2, which fixes redirect handling that could forward the `Authorization` header to a third-party host (CVE-2026-31488). Upgrade if you set a custom `baseUrl`. ([#807](…)) [Unreleased]: https://github.com/meridian-labs/meridian-js/compare/v4.0.0...HEAD [4.0.0]: https://github.com/meridian-labs/meridian-js/compare/v3.4.0...v4.0.0 [3.4.0]: https://github.com/meridian-labs/meridian-js/compare/v3.3.0...v3.4.0 ```` The rules the skeleton encodes: | Rule | Do | Avoid | |---|---|---| | Order | Newest version first, `[Unreleased]` above it | Oldest at the top | | Dates | `## [4.0.0] - 2026-08-14` | `Aug 14, 2026`, `08/14/26`, no date | | Categories | Added, Changed, Deprecated, Removed, Fixed, Security — in that order | Invented headings such as `### Misc` | | Empty categories | Omit a category with no entries this release (convention) | Shipping `### Fixed` with nothing under it | | Duplicates | One category sub-heading per version | Two `### Added` blocks in one version | | Links | Bracketed headings resolved by compare links at the bottom | Bare version numbers, no diff link | At release time, move the `[Unreleased]` entries under a dated heading, add the compare link, and repoint `[Unreleased]` at the new tag. Source: keepachangelog.com ## Writing an entry (N3) An entry is one sentence, or two, in this shape: > **what changed** (named in code font) + **what it means for you** (the condition, the new behavior) + **what to do** (the action, or nothing) + **link** (issue or pull request, in parentheses at the end). The impact clause is what commit messages never carry and what the reader came for. Drop it only when the change is self-evidently free. Per category, commit-message-style line → entry: ```markdown Added - feat(query): add json output + `meridian query` accepts `--output json` and prints one JSON object per row, so you can pipe results into `jq`. ([#780](…)) Changed - refactor(query): normalize ordering in the planner + `meridian.query()` returns rows in ascending `timestamp` order instead of insertion order. If your code depended on insertion order, sort explicitly with `orderBy('_ingested')`. ([#791](…)) Deprecated - deprecate track() + `meridian.track()` is deprecated in favor of `meridian.events.record()` and is removed in 4.0.0. Both accept the same payload. ([#755](…)) Removed - remove legacy auth flag + The `--legacy-auth` flag is removed from `meridian login`. Sign in with `meridian login --token TOKEN`, or set `MERIDIAN_TOKEN`. Deprecated in 3.2.0. ([#803](…)) Fixed - fix sync + `meridian sync` no longer drops events when the connection times out mid-batch; an interrupted batch restarts from the last acknowledged offset. Affects 3.1.0 through 3.4.0. ([#799](…)) Security - chore: bump node-fetch + Upgraded `node-fetch` to 3.3.2, which fixes redirect handling that could forward the `Authorization` header to a third-party host (CVE-2026-31488). Upgrade if you set a custom `baseUrl`. ([#807](…)) ``` Four habits keep entries usable: - **One change per entry.** A pull request that adds a flag and fixes a bug produces two entries in two categories. - **Name the affected versions in a `Fixed` entry** when the bug shipped in several releases — that is how a reader on 3.2.0 learns the fix is for them. - **Put the link last**, as `([#799](url))`; mid-sentence links break the scan. - **Write the entry with the change**, not at release time. Reconstructed impact statements are guesses. Source: keepachangelog.com ## Breaking changes (N4) Keep a Changelog does not prescribe a marker for breaking changes, so the placement and prefix below are a **(convention)** — adopt one form and hold it across the file. 1. **First in the version.** Breaking entries lead their category, and the categories holding them lead the version. A reader who stops after five lines has still seen everything that can break their build. 2. **One consistent marker.** `**Breaking:**` at the start of the entry is a common form. It keeps the entry inside its Keep a Changelog category, so tools that parse the six headings still work — unlike a separate `### Breaking changes` heading, which is outside the six. 3. **Named migration steps** — inline when they fit in a clause, otherwise a link to the guide. "This is a breaking change" with no action is a warning, not documentation. 4. **Never buried in Changed.** A behavior change that silently alters results is breaking even when the signature is untouched. **Before** — breaking and non-breaking interleaved, no marker, no action: ```markdown ### Changed - Improved logging output for the sync command. - Query results are now ordered differently. - Renamed some internal helpers. ``` **After** — marked, first, with the action and the guide: ```markdown ### Changed - **Breaking:** `meridian.query()` returns rows in ascending `timestamp` order instead of insertion order. Sort explicitly with `orderBy('_ingested')` to keep the old order. See [Migrate from 3.x to 4.0](docs/migrate-3-to-4.md). ([#791](…)) - `meridian sync` prints the batch offset with each progress line. ([#786](…)) ``` For a release with several breaking changes, add a short **Upgrade notes** paragraph under the version heading, above the first category, listing them in the order a reader should handle them and linking the guide once **(convention)**. Source: convention ## Deprecations (N5) A deprecation entry names three things: the **replacement** (the exact API, flag, or option to call instead), the **removal version or date** (`4.0.0`, or `2027-01-15` for a service), and the **difference**, when the replacement is not a drop-in. **Before:** ```markdown ### Deprecated - `meridian.track()` is deprecated. ``` **After:** ```markdown ### Deprecated - `meridian.track()` is deprecated in favor of `meridian.events.record()` and is removed in 4.0.0. Both accept the same payload; `record()` returns the stored event instead of `undefined`. See [Migrate from track() to events.record()](docs/migrate-track-to-record.md). ([#755](…)) ``` Keep the entry in place until removal: a reader jumping 3.1.0 → 4.0.0 reads the 3.4.0 block on the day it stops being true. When the removal lands, write a `Removed` entry that back-references the deprecation ("Deprecated in 3.4.0"). The changelog entry, the deprecation note in the API reference (**A5**, [api-reference.md](api-reference.md)), and any runtime warning must name the same replacement and the same removal version. A mismatch is a factual defect, not a style one. Source: keepachangelog.com, api-reference-comments ## The Google language layer on entries Entries are documentation, so the docs-set rules apply inside them. Tense needs one local decision: category headings already carry a tense ("Added"), so entries read as fragments or as sentences about the API. Pick one form per file under **V3** ([voice-and-words.md](voice-and-words.md)) — verb-first present ("Adds a `--dry-run` flag…"), subject-first present ("`meridian sync` accepts `--dry-run`…"), or simple past ("Added a `--dry-run` flag…") — and keep it for every entry in every version. Four rewrites: ```markdown V1 second person for actions ([voice-and-words.md](voice-and-words.md)) - Users should update their config file, and the client will then reconnect. + Update `meridian.config.js` to set `retries`. The client reconnects on the next run. V5 no unquantified "improved", "better", "enhanced" ([voice-and-words.md](voice-and-words.md)) - Greatly improved query performance and better error handling. + `meridian.query()` streams results instead of buffering them, which holds memory flat for result sets above 10,000 rows. Failed queries raise `QueryError` with the failing SQL in `error.query`. V7 timeless: no "new", "currently" ([voice-and-words.md](voice-and-words.md)) - New: the SDK currently supports Deno, with more runtimes coming soon. + Adds Deno support. Import from `npm:meridian-js@4`. P6 code font ([procedures-and-code.md](procedures-and-code.md)) + W7 word list [EN] - Fixed the bug where users couldn't log in via the api using the --token flag, e.g. in CI. + `meridian login --token` no longer fails when `HOME` isn't set — for example, in container builds. ([#788](…)) ``` The last pair also applies the word list `[EN]`: sign in rather than log in, through rather than via, "for example" rather than "e.g." Entries are short enough that a single stray "simply" is the loudest word on the page. Source: person, tense, excessive-claims, timeless-documentation, code-in-text, word-list ## Release notes vs changelog Two artifacts, two readers **(convention)** — a project can ship one, the other, or both: | | Changelog | Release notes | |---|---|---| | Scope | Every user-visible change | The handful that matter this release | | Shape | Categorized list, all versions in one file | Narrative page or post, one per release | | Reader | Upgrading developer checking for impact | Anyone deciding whether to upgrade | | Sections | Added / Changed / Deprecated / Removed / Fixed / Security | Highlights, upgrade notes, known issues | | Cadence | Every release, including patches | Notable releases; patches often skipped | | Home | `CHANGELOG.md` in the repository | Docs site, GitHub release, blog | Ship both when a release carries more context than a list holds — a rewritten subsystem, a quota change, a migration with a deadline. Link them both ways: the notes end with "For the complete list of changes, see the [changelog](CHANGELOG.md)", and the changelog's version heading is the anchor the notes point back to. The same release as a release-notes page: ```markdown # Meridian SDK 4.0 release notes Released 2026-08-14. ## Highlights Events are stored through `meridian.events.record()`, which returns the stored record instead of `undefined`, so you no longer need a second read to get the server-assigned `id`. ## Upgrade notes Two breaking changes need action before you upgrade: `meridian.track()` is removed, and `meridian.query()` orders rows by `timestamp`. Follow [Migrate from 3.x to 4.0](docs/migrate-3-to-4.md). ## Known issues `meridian sync --parallel` reports the offset of the last completed batch rather than the highest one ([#815](…)). Fix planned for 4.0.1. For the complete list of changes, see the [changelog](CHANGELOG.md). ``` Source: convention ## Migration guides (N6) A migration guide is a procedure, so the step mechanics belong to **P1–P4** ([procedures-and-code.md](procedures-and-code.md)): numbered steps, one imperative action each, condition before instruction, expected result stated. The skeleton: | Section | Contents | |---|---| | Title | "Migrate from X to Y" — versions or API names, not "Upgrade guide" | | Intro | Who needs this, what changes, roughly how long it takes | | Before you begin | Source and target versions, a backup or clean tree, feature flags to set | | Numbered steps | One change per step, each with a before and after snippet | | Verify the migration | A command to run and the output to expect | | Roll back | How to return to the previous version, and what is not reversible | | What's next | The reference pages for the new API | A complete guide for a rename plus a signature change: ````markdown # Migrate from meridian.track() to meridian.events.record() Applies to projects on Meridian SDK 3.x that call `meridian.track()`, which is removed in 4.0.0. The method is renamed and its return value changes from `undefined` to the stored event. Expect about 15 minutes for a small codebase. ## Before you begin - Confirm your current version: `npm ls meridian-js` reports 3.4.0 or later. - Commit or stash your work — the steps edit call sites in place. - Set `MERIDIAN_STRICT_EVENTS=1` to make 3.4.0 warn at every remaining `track()` call site. ## Migrate the call sites 1. Upgrade the package: ```bash npm install meridian-js@4.0.0 ``` 2. Replace each `track()` call with `events.record()`. The payload is unchanged: ```javascript // Before meridian.track({ name: "checkout_started", userId }); // After await meridian.events.record({ name: "checkout_started", userId }); ``` 3. Optional: if you read the event back to get its `id`, delete the follow-up read and use the returned record: ```javascript // Before meridian.track({ name: "checkout_started", userId }); const stored = await meridian.events.latest({ userId }); // After const stored = await meridian.events.record({ name: "checkout_started", userId, }); ``` ## Verify the migration Run the linter rule that ships with the package: ```bash npx meridian lint --rule no-removed-apis ``` The output lists remaining call sites, or confirms there are none: ```text meridian lint: 0 findings in 128 files ``` ## Roll back Reinstall the previous version with `npm install meridian-js@3.4.0` and revert the call-site commit. Events recorded through 4.0.0 stay readable in 3.4.0. ## What's next - [events.record() reference](../api/events-record.md) - [Migrate from 3.x to 4.0](migrate-3-to-4.md) for the remaining breaking changes ```` Two defects to check before publishing: a step that says what changed without showing the edit (the snippets are the guide), and a guide with no verification step, which leaves the reader unable to tell a finished migration from a half-finished one. Source: procedures -
structure-and-formatting.md 13.8 KB
# Structure and Formatting 1. [Headings (S1, S2, S3)](#headings-s1-s2-s3) 2. [Lists (S4, S5)](#lists-s4-s5) 3. [Tables (S6)](#tables-s6) 4. [Notices (S7)](#notices-s7) 5. [Cross-references (S8)](#cross-references-s8) 6. [Link text (S9)](#link-text-s9) 7. [Images and alt text (S10)](#images-and-alt-text-s10) 8. [Numbers, dates, and units (S11)](#numbers-dates-and-units-s11-en) 9. [Text formatting summary (S12)](#text-formatting-summary-s12) 10. [Worked example](#worked-example) This file deepens Framework section 4 (Structure: Headings, Lists, Tables, Notices) with the full rule text, more before/after pairs, and the numbers, dates, and text-formatting rules that section only summarizes. It owns **S1–S12**. Code-font specifics, placeholders, and UI-element bold live in `procedures-and-code.md` (P6, P8, P5); paragraph rules live in `document-types.md` (R6). ## Headings (S1, S2, S3) Headings are the reader's table of contents before they read a word of body text — a reader scans the heading list, finds their task, and jumps there. A heading that doesn't say what the section does, or that breaks case or level conventions, breaks the scan. ### S1 — Sentence case Capitalize the first word and proper nouns only; leave common nouns lowercase even when a UI element shows them capitalized. Proper nouns, product and brand names, and code stay exactly as written: Google Cloud, Cascade, `README.md`. - "Configuring The Database Connection" → "Configure the database connection" - "API Reference For The Webhooks Endpoint" → "API reference for the Webhooks endpoint" ### S2 — Task headings vs. concept headings A task heading is a bare imperative — the verb the reader performs: "Create a webhook". A concept heading is a noun phrase — the thing the reader is learning about: "Webhook lifecycle". Neither ever ends in "-ing"; an "-ing" heading hides which of the two it is. - "Creating a Webhook" (task, disguised as a gerund) → "Create a webhook" - "Troubleshooting Deploy Failures" (task) → "Troubleshoot deploy failures" - "Webhook Payload Format" (concept, wrong case) → "Webhook payload format" ### S3 — Levels, endings, and adjacency Don't skip heading levels (H2 straight to H4 with no H3 in between) — screen readers announce level jumps as broken structure. Headings never end with a period. And (inferred) avoid a heading immediately followed by another heading with no body text between them — a reader lands on the child heading with no idea what the parent section covers. **Full outline, before:** ```markdown # Cascade CLI Reference. ### Installing Cascade #### Prerequisites ##### Supported platforms ### Deploying An App ### Troubleshooting Common Errors ``` Problems: trailing period on the title; the outline jumps from H1 to H3 (no H2); "Prerequisites" is immediately followed by "Supported platforms" with nothing said in between; every heading is "-ing" and title case. **After:** ```markdown # Cascade CLI reference ## Install Cascade ### Prerequisites Before you install Cascade, confirm you have Node.js 18 or later. ### Supported platforms ## Deploy an app ## Troubleshoot common errors ``` Source: headings ## Lists (S4, S5) ### S4 — Intro sentences and parallelism Every list needs an intro sentence that is grammatically complete on its own, ending in a colon. A sentence that only works once you mentally append the first bullet — "Use the `--format` flag to:" — breaks the moment an item doesn't start with a verb. Every item in a list takes the same grammatical form as the others: all imperatives, all nouns, all noun phrases. **Before (fragment intro, mixed forms):** ```markdown Use the --format flag to: - Print JSON - Print YAML - Print a table ``` **After (complete intro, parallel nouns):** ```markdown The --format flag supports the following output types: - JSON - YAML - Table ``` **Before (non-parallel steps):** ```markdown Before you deploy, complete these steps: - Installing the CLI - You need an API key - Configure the project ``` **After:** ```markdown Before you deploy, complete these steps: - Install the CLI - Get an API key - Configure the project ``` ### S5 — Numbered vs. bulleted vs. description lists Number a list only when order matters — the reader must do item 1 before item 2. Everything else is bulleted, including options, flags, and requirements that stand independently of each other. **Before (numbered, but the items aren't a sequence):** ```markdown 1. Enable verbose logging 2. Enable colorized output 3. Enable strict mode ``` **After:** ```markdown - Enable verbose logging - Enable colorized output - Enable strict mode ``` For term-and-definition pairs, a description list beats a two-column table — the term reads as a heading, not a table cell, and there's no header row to skip. Markdown has no universal native syntax for this, but the widely supported Markdown Extra / Pandoc form is: ```markdown `--dry-run` : Previews changes without applying them. `--verbose` : Prints each step as it runs. ``` **Punctuation** `[EN]` (inferred): capitalize the first word of every item regardless of form. A complete sentence ends with a period; a fragment doesn't. - Fragment, no period: "- Verbose output" - Complete sentence, period: "- Verbose output is enabled by default." Source: lists ## Tables (S6) A table needs a header row and an intro sentence that states what it lists — "The following table lists the `cascade deploy` flags:" — so a reader (and a screen reader) knows what they're about to scan before they hit the grid. Never merge cells and never leave one empty: an empty cell reads to assistive tech as if nothing is there at all, not as "not applicable". Write "None" or "Not applicable" instead (convention). Keep cell phrasing parallel down a column — if one description is a sentence, they all are. Reach for a bulleted or description list instead of a table when there's only one dimension of comparison (a plain list of flags with no second attribute) or when most cells would carry long prose — tables earn their header row only when the data is genuinely tabular across two or more attributes. **Before:** ```markdown | Flag | | |------|--| | --verbose | | | --dry-run | Preview changes | ``` **After:** ```markdown The following table lists the `cascade deploy` flags: | Flag | Description | |------|-------------| | `--verbose` | Prints each step as it runs. | | `--dry-run` | Previews changes without applying them. | ``` Source: tables, accessibility ## Notices (S7) A notice interrupts the reader's flow, so it earns that interruption only when the information changes what they do next. - **Note** — useful, optional information the reader can act on or skip: "Cascade caches build artifacts in `.cascade/cache`. Delete this directory to force a clean build." - **Caution** — proceed carefully; the mistake is recoverable but costly: "Changing the region after deployment migrates your database and can take up to 30 minutes." - **Warning** — serious harm or irreversible loss: "Running `cascade reset --hard` permanently deletes all environments and can't be undone." Don't stack them. (Inferred) one notice per section is a practical ceiling — three boxes in a row train the reader to skip all three, including the one that matters. Fold a notice into body text whenever it doesn't change the reader's next action — a plain sentence in the paragraph carries the same information without the visual interruption. **Before:** ```markdown Run `cascade deploy` to publish your app. > **Note:** This command requires an active internet connection. ``` **After:** ```markdown Run `cascade deploy` to publish your app. This command requires an internet connection. ``` Source: notices ## Cross-references (S8) Point to other content with "see", never "refer to" or "check out". Never say "above" or "below" — pages reflow, get translated into languages that reorder content, and get read out of order by screen readers and search results. Use "the following" for what comes immediately after in the same page, "the preceding" for what came immediately before, and a named link for anything farther away. Link when the referenced material lives on another page, or is optional depth the current task doesn't require. Inline the fact instead — repeat the one sentence the reader needs — when sending them away would interrupt a procedure they're mid-way through. - "For more info, check out the docs above." → "For more information, see Configure authentication." - "As mentioned below, you'll need an API key." → "You need an API key; see Get an API key." - "Refer to the table above for exit codes." → "See the preceding table for exit codes." Source: cross-references ## Link text (S9) Link text should read as the destination's title, or a close description of it, so it makes sense pulled out of the sentence — many screen readers list a page's links with no surrounding text at all. Never link "click here", "this link", or a bare pasted URL. Put the link on the words that name the target, not on a filler verb. - "To learn about rate limits, click here." → "For rate limit details, see [Rate limits](#)." - "Read more at https://docs.cascade.dev/webhooks." → "For webhook payload formats, see [Webhook payloads](#)." - "This link explains how authentication works." → "See [How authentication works](#) for the full flow." Source: link-text, accessibility ## Images and alt text (S10) Alt text states what the image communicates in this context, not what it literally shows — skip "Image of" and "Screenshot of"; the screen reader already announces that it's an image. Keep it to one concise phrase or sentence, not a full transcription of every pixel. Information must never live only in an image: if a diagram is the sole place a required value, flag, or step appears, the doc is broken for anyone who can't see it and for anyone who needs to copy that value. This is a shippability gate (Blocking) — put the same fact in the surrounding text. Use a screenshot to show where something sits in a UI or to confirm a visual result — a filled-in form, a chart the reader compares theirs against. Use text or a code block for anything the reader types, copies, or runs: text is copyable, searchable, and translates; a screenshot of a command is none of those. Write the caption first — naming the figure's one idea before you draw or crop it keeps the image on message (tech-writing/two) — and never let the caption alone carry a detail that's missing from the alt text. **Before:** ```markdown  Click the button to deploy. ``` **After:** ```markdown  Click **Deploy** in the top-right corner of the dashboard. ``` Source: images, accessibility ## Numbers, dates, and units (S11) `[EN]` Spell out zero through nine; use numerals for 10 and up. Always use numerals with units, versions, and measurements, no matter how small the number: "3 MB", "version 2 of the API", "5 retries". Dates must be unambiguous — "2026-08-29" or "August 29, 2026" — never "08/29/26" or "29/08/26", which read as different dates depending on the reader's locale. Include a time zone whenever a time matters across regions: "2:00 PM UTC", not "2:00 PM". Units get a space and the standard symbol, never a spelled-out or invented abbreviation: "10 MB", not "10MB" or "10 megs". The time-zone and unit-spacing forms are inferred — the sourced pages cover number and date formats. - "The free tier includes 3 projects and up to 100mb of storage." → "The free tier includes three projects and up to 100 MB of storage." - "The migration finished on 08/09/26 at 2pm." → "The migration finished on 2026-08-09 at 2:00 PM UTC." - "This feature requires SDK version two." → "This feature requires SDK version 2." Source: numbers, dates-times ## Text formatting summary (S12) | Format | Use for | Example | |--------|---------|---------| | **Bold** | UI element names, matching on-screen casing (P5) | Click **Deploy**. | | `Code font` | Commands, flags, filenames, code, values (P6) | Run `cascade deploy --dry-run`. | | *Italics* | A new term on its first use; emphasis, used sparingly | A *webhook* is an HTTP callback that Cascade sends when an event occurs. | Don't format product names — plain text, matching the vendor's own capitalization (P6). See `procedures-and-code.md` for the full code-font, placeholder, and UI-element rules behind P5 and P6. Source: text-formatting ## Worked example **Before** — headings break case and nest wrong, the list has a fragment intro and non-parallel items, the table has no intro sentence and empty cells, and two notices stack back to back: ```markdown # Configuring Webhooks. ## Setting Up To set up webhooks you can: - signing secret - Choose a delivery URL - pick which events to send ## The Payload Below is a table of fields you might get back: | Field | | |-------|--| | event | | | id | The event's ID | Note: Webhook retries happen automatically. Caution: If your endpoint returns a non-2xx status the delivery is marked failed and won't retry. ``` **After** — sentence-case imperative headings, one parallel bulleted list with a complete intro, a table with a header row and no empty cells, the routine fact folded into body text, and a single notice: ```markdown # Configure webhooks ## Set up a webhook To set up a webhook, complete these steps: - Generate a signing secret. - Choose a delivery URL. - Select which events to send. ## Webhook payload fields The following table lists the fields in every webhook payload: | Field | Description | |-------|-------------| | `event` | The event type, for example `payment.succeeded`. | | `id` | The event's unique ID. | Cascade retries a failed delivery up to five times before it gives up. **Caution:** Changing the delivery URL cancels any retries already queued for the old URL. ``` -
voice-and-words.md 16.2 KB
# Voice and Words Deepens SKILL.md §2 (Voice: You, Active, Present, Timeless) and §3 (Sentences and Words). Owns rule IDs V1-V10 and W1-W8 — cite these IDs in audit findings; S9 ("click here") and P8 (placeholders) are cited here but explained in their owner files. ## Contents - [Second person](#second-person-v1) · [Active voice](#active-voice-v2) · [Present tense](#present-tense-v3) - [Please, simply, and excessive claims](#please-simply-and-excessive-claims-v4-v5) · [Anthropomorphism](#anthropomorphism-v6) - [Timeless documentation](#timeless-documentation-v7) · [Contractions \[EN\]](#contractions-en-v8) - [Inclusive language](#inclusive-language-v9) · [Global audience](#global-audience-v10) - [Condition before instruction](#condition-before-instruction-w1) · [Short sentences, one idea](#short-sentences-one-idea-w2) - [Abbreviations](#abbreviations-w3-and-latin-abbreviations-en-w4) · [can/may/might \[EN\]](#canmaymight-en-w5) - [Jargon](#jargon-w6) · [Word list \[EN\]](#word-list-en-w7) - [American spelling and serial comma \[EN\]](#american-spelling-and-serial-comma-en-w8) · [Non-English documents](#non-english-documents) ## Second person (V1) Address the reader as "you." Never "we" (hides who has to act) or "the user" (turns the reader into a third party watching someone else's instructions). Default to the imperative for steps — the subject "you" is implied, not written. | Avoid | Use instead | Why | |---|---|---| | "We recommend restarting the Nimbus CLI daemon after a config change." | "Restart the Nimbus CLI daemon after you change the config." | "We" hides who has to act | | "The user must set an API key before calling the endpoint." | "Set an API key before you call the endpoint." | "The user" makes the reader a bystander in their own instructions | | "You should click Deploy to start the build." | "Click **Deploy** to start the build." | Imperative drops the throat-clearing subject | **The one allowed "we":** Google, or the team that owns the product, speaking as the actual actor — stating a design decision, not giving an instruction. Rare, and confined to prose about the product's history or rationale. - Allowed: "We built the Vantage API to replace polling with webhooks." - Not allowed: "We suggest you enable webhooks." → "Enable webhooks." Source: person ## Active voice (V2) **Spot it:** a form of "be" (is, was, are, were, been, being) followed by a past participle, with the actor missing or trailing in a "by" phrase. | Passive | Active | Note | |---|---|---| | "The manifest is validated by the Kiln build server." | "The Kiln build server validates the manifest." | Actor is named — no reason for passive | | "Deployments are triggered when a tag is pushed." | "Pushing a tag triggers a deployment." | Actor recoverable — rewrite active | **Three allowed passive cases:** 1. Actor unknown: "The request was rejected by an upstream proxy." → allowed as "The request was rejected" when the Wayfinder SDK genuinely can't identify which proxy. 2. Actor irrelevant to the reader's task: "Sessions are rotated every 15 minutes." — the reader needs the interval, not the internal job that does the rotating. 3. To emphasize the object over the actor: "Expired records are purged nightly." — the record is the point; naming the cron job would bury it. Source: voice ## Present tense (V3) Default to present tense for behavior. Reserve "will" for effects genuinely later than the action described, not for the next line of the same sequence. | Rewrite to present | Legitimate "will" | |---|---| | "The server will send an ack." → "The server sends an ack." | "If you delete a project, Fleetlog will permanently remove its logs after a 90-day grace period." — the effect is deferred by a stated delay, not immediate | | "The Fleetlog service will purge logs older than 30 days nightly." → "The Fleetlog service purges logs older than 30 days nightly." | "After three consecutive failed health checks, the load balancer will mark the instance unhealthy." — a threshold-triggered future event | Rule of thumb: if the result follows directly from the action in the same step, use present tense. If it arrives after a stated delay or a later condition, "will" is accurate. Source: tense ## Please, simply, and excessive claims (V4, V5) **"Please":** omit from instructions. Reserve it for asking the reader's permission or forgiveness, not for softening a command. - "Please click Save to persist your changes." → "Click **Save** to persist your changes." - Allowed: "Please allow up to 24 hours for DNS changes to propagate." — asking for patience, not issuing a step. **"Simply / easily / just / quickly":** omit. They grade the reader's experience for them; if a step really is easy, the reader notices without being told. - "Just add the `--watch` flag to simply enable live reload." → "Add the `--watch` flag to enable live reload." **Superlatives, absolutes, and competitor comparisons:** cut, or replace with a verifiable, specific claim. | Avoid | Use instead | |---|---| | "The fastest way to deploy" | "One way to deploy" — or a stated number: "deploys in under 10 seconds on the free tier" | | "Never loses a message" | "Retries delivery up to five times before moving the message to a dead-letter queue" | | "Faster than Relay's queue" | Cut the comparison, or link to a published benchmark | Source: tone, excessive-claims ## Anthropomorphism (V6) Software doesn't want, see, think, know, tell, or complain. Name what actually happens. | Avoid | Use instead | Example | |---|---|---| | wants | requires, needs | "The build script wants a `NODE_ENV` value." → "The build script requires a `NODE_ENV` value." | | sees | detects | "The linter sees an unused import." → "The linter detects an unused import." | | thinks | determines, evaluates | "The scheduler thinks the job is stuck." → "The scheduler determines the job is stuck after a 10-minute timeout." | | knows | stores, has | "The cache knows the last ETag." → "The cache stores the last ETag." | | tells | notifies, reports | "The webhook tells the queue the job failed." → "The webhook reports the failure to the queue." | | complains | returns an error, logs | "The parser complains about invalid syntax." → "The parser returns a syntax error." | Source: anthropomorphism ## Timeless documentation (V7) Cut "currently," "now," "new," "soon," and "at the time of writing." A doc that never refers to today stays correct for as long as the behavior holds; one anchored to today starts rotting the day it ships. | Avoid | Use instead | |---|---| | "Currently, the Cortex API rate-limits requests to 100/min." | "The Cortex API rate-limits requests to 100/min." | | "This new dashboard shows deploy history." | "The dashboard shows deploy history." | | "Soon you'll be able to export CSV." | Cut it — document only what's shipped | **Never pre-announce.** A feature that isn't released yet doesn't belong in the docs, even hedged as "coming soon." **Version differences aren't a timeless-docs violation** — they name a fact tied to a version number, not to the calendar: - "In Nimbus CLI 2.x and later, `--dry-run` prints a diff before applying changes." — fine, because the boundary is the version, not "now." Source: future, timeless-documentation ## Contractions [EN] (V8) Contractions read as conversational, not sloppy, and Google's guide allows them. For negations, prefer the contraction over the two-word form. | Prefer | Over | |---|---| | isn't | is not | | don't | do not | | won't | will not | - "The API key is not valid for staging." → "The API key isn't valid for staging." - "Do not delete the retention record." → "Don't delete the retention record." Source: contractions ## Inclusive language (V9) | Avoid | Use instead | |---|---| | master / slave | primary / replica, controller / worker | | blacklist / whitelist | denylist / allowlist (or blocklist / safelist) | | sanity check | final check | | dummy (variable, value) | placeholder | | crazy | unexpected, erratic | | cripple | disable, degrade | | guys | everyone, team, folks | | he / she (generic) | they | **Fix pairs together.** Replacing only "blacklist" with "denylist" while leaving "whitelist" untouched keeps the asymmetry the swap was meant to remove — change both halves of a pair in the same edit, even when only one half appears on the page in front of you. **Gender-neutral "they":** use it for a person of unspecified gender, singular or plural, instead of switching to "he or she" or alternating pronouns. - "A developer must configure his API key before deploying." → "A developer must configure their API key before deploying." Source: inclusive-documentation ## Global audience (V10) | Avoid | Use instead | Why | |---|---|---| | "Once you deploy the service, health checks start." | "After you deploy the service, health checks start." | "Once" reads as a time word in some dialects and a conditional in others | | "This endpoint hits the ground running with zero config." | "This endpoint works with no configuration." | Idioms don't translate | | "Configure it like setting up a Thanksgiving dinner — plan ahead." | Cut the analogy; describe the steps directly | Culture-bound reference | | "the customer order fulfillment status notification service" | "the service that notifies customers about fulfillment status" | Long noun stacks don't parse for non-native readers | | "The team, the config file having been updated, redeployed." | "The team updated the config file, then redeployed." | Keep subject-verb-object order | Date, time, currency, and number formatting for a global audience are S11 (structure-and-formatting.md) — this section covers sentence-level and word-choice habits only. Source: translation, global-audience ## Condition before instruction (W1) State the condition or goal first, so the reader can tell whether the step applies to them before they act on it. 1. "Click **Delete** if you want to remove the workspace." → "To remove the workspace, click **Delete**." 2. "Restart the Anchor auth service after you edit `config.yaml`." → "After you edit `config.yaml`, restart the Anchor auth service." 3. Multi-condition: "Retry the request if the response is a 503 and a `Retry-After` header is present, and otherwise fail immediately." → "If the response is a 503 and includes a `Retry-After` header, retry the request. Otherwise, fail immediately." Source: sentence-structure ## Short sentences, one idea (W2) One clause, one idea. When a sentence accumulates a "which" clause, a comma-and, and a second instruction, split it into separate sentences. Before: "The Vantage API returns a 429 status code when you exceed the rate limit, which is 100 requests per minute for free-tier accounts, and you should back off using the Retry-After header." After: "The Vantage API returns a 429 status code when you exceed the rate limit. Free-tier accounts are limited to 100 requests per minute. Back off using the value in the `Retry-After` header." **Convert a "which" clause into a list when it enumerates items:** Before: "The deploy command validates the manifest, which checks the schema, the image tag, and the resource limits." After: "The deploy command validates the manifest. It checks: - The schema - The image tag - The resource limits" Source: tech-writing/one "Short sentences" ## Abbreviations (W3) and Latin abbreviations [EN] (W4) **First use:** spell out the term with the abbreviation in parentheses, then use the abbreviation for the rest of the page. - "Configure the command-line interface (CLI) before running the first build. The CLI reads `~/.nimbusrc` on startup." **Universally known exceptions:** skip the spell-out for terms every reader already knows — URL and HTML, and similarly ubiquitous ones (inferred: API, CPU). **Latin abbreviations** don't translate and scan poorly in the middle of a sentence — spell out the meaning instead. | Avoid | Use instead | |---|---| | e.g. | for example | | i.e. | that is | | etc. | omit, or finish the list | | vs. | versus, or "compared with" | | cf. (inferred) | see, or compare | Source: abbreviations, word-list ## can/may/might [EN] (W5) | Modal | Meaning | Example | |---|---|---| | can | ability | "Free-tier accounts can make up to 100 requests per minute." | | may | permission | "You may cache a response for up to 60 seconds." | | might | possibility | "The migration might take several minutes for databases over 10 GB." | Don't use "may" for possibility — "The build may fail" reads as the build having permission to fail. Use "might." Source: word-list ## Jargon (W6) Jargon is a defect only for the reader who doesn't have it. Define, link, or replace it based on the reader named at intake. | For this reader | Do this | |---|---| | Experienced backend engineers | Use the term as-is: "The write is idempotent." | | Mixed technical audience | Define inline on first use: "The write is idempotent — repeating it produces the same result." | | Non-technical or new readers | Link to a concept page, or replace with plain language: "Repeating the request is safe; it won't create duplicates." | Source: jargon ## Word list [EN] (W7) "Click here" is not a word-list row — it's rule S9 (structure-and-formatting.md), a link-text defect, not a word choice. Entries owned by their own rule aren't repeated here — please (V4), just/simply (V5), e.g./i.e./etc. (W4), the inclusive-language pairs (V9), and once→after (V10) live in their sections; "click here" is rule S9 in structure-and-formatting.md. | Avoid | Use instead | Why | |---|---|---| | above / below | preceding / following | Breaks on reflow, print, and translation | | abort / kill | stop, cancel, end | Violent connotation | | log in | sign in (unless the product itself says "log in") | Google's preferred term | | setup (as a verb) | set up | "Setup" is the noun; "set up" is the verb | | check box | checkbox | One word | | and/or | pick one | Ambiguous | | in order to | to | Wordy | | desire | want | Plainer | | leverage | use | Jargon | | utilize | use (inferred) | Plainer | | pop-up / dialog box | dialog | Google's preferred term | | e-mail | email (don't use it as a verb) | Modern spelling; not a verb | | Internet | internet | Common noun now | | back-end / front-end | backend / frontend | One word, no hyphen | | file name | filename | One word | | Id / id | ID | Always capitalized | | admin | administrator (except literal UI labels) | Plainer, except where the UI itself reads "Admin" | | application | app (for end-user programs) | Google's preferred term in consumer contexts | | click and drag | drag | Simpler | | via | through, by using | Latin-derived; doesn't localize | | allows you to | lets you / you can | Wordy | | wish | want | Plainer | | terminate | end, stop | Plainer | | enable (a person) | let, lets | "Enable" a feature, not a person | | display (intransitive) | appears | "Display" needs an object | | execute | run | Plainer | | illegal | invalid, not allowed | "Illegal" implies law-breaking | | foo / bar | meaningful names in samples | Realistic names read better and copy-paste safely | | Note that | omit | Filler opener | | going forward | omit | Filler; also a timeless-docs violation | | it's / its | "it's" only for "it is"; "its" is possessive | Commonly confused | | toggle (as a verb) | turn on, turn off | Plainer | | uncheck | clear | Google's UI term | | unselect | deselect | Google's UI term | Source: word-list ## American spelling and serial comma [EN] (W8) | UK | US (use this) | |---|---| | colour | color | | behaviour | behavior | | licence (noun) | license | | centre | center | | optimise | optimize | **Serial comma:** place a comma before the final "and" or "or" in a list of three or more. - "Install the CLI, configure the API key and run the migration." → "Install the CLI, configure the API key, and run the migration." Source: highlights, commas ## Non-English documents The authoritative split lives in [audit-checklist.md](audit-checklist.md), "Non-English documents": the `[EN]` rules — contractions (V8), Latin abbreviations (W4), can/may/might (W5), the word list (W7), spelling and the serial comma (W8) — are skipped, and every other rule in this file, spelling out abbreviations (W3) included, applies in any language. Never translate a document unless asked (skill rule) — edit or write in the language the source already uses, and flag translation as a separate task if one looks needed.
-
-
SKILL.md 21.2 KB
--- name: technical-documentation description: 'Audit, write, and improve developer documentation using Google''s Developer Documentation Style Guide and Technical Writing courses. Use this skill for any documentation work, even when the user names no style guide: "audit our docs", "review this README", "write a README", "getting started guide", "how-to or tutorial", "API reference", "docstrings", "CLI help text", "changelog or release notes", "migration guide", or "our docs are confusing". Also use it when writing docs from code, rewriting a doc for clarity, fixing headings, procedures, or code samples, or enforcing consistent voice and terminology across a docs set — prefer it over editing documentation unaided. Covers reader and doc-type fit, second person and active voice, procedures, headings, lists and tables, code samples and placeholders, link text, notices, the word list, timeless docs, and accessibility. For code comments and naming, see clean-code. For marketing or landing-page copy, see storybrand-messaging.' license: MIT metadata: author: wondelai version: "1.0.0" --- # Technical Documentation Audit, write, and improve developer documentation the way Google's technical writers do: start from the reader's task, verify every fact against the code, then apply the style guide in severity order — structure before voice, voice before word choice. ## Core Principle **Write for the reader's task, not the product's feature list.** Google's guide asks for prose that is conversational but not frivolous, precise, and consistent, because a developer reading docs is trying to get something done, not to admire the product. Two framing rules from the guide shape everything below: - **Guidelines, not rules.** Depart from the guide when doing so improves the content — established domain terminology wins — but stay consistent within the document. - **Precedence.** A project's own style guide comes first, then Google's guide, then Merriam-Webster (spelling), the Chicago Manual of Style (general style), and the Microsoft Writing Style Guide (technical style). Rules come in two layers. Structural and content rules (headings, procedures, code samples, second person, active voice, timeless docs, accessibility) apply to documentation in any language. Rules tagged `[EN]` (spelling, serial comma, contractions, the word list) apply only to English text — skip them for other languages, and never translate a document unless asked. ## Scoring **Goal: 10/10.** Score = number of Quick Diagnostic rows passed (10 rows, 1 point each; the `[EN]` row auto-passes for non-English docs). Bands: **9-10** = ships as is; **7-8** = word- and voice-level edits only; **5-6** = restructure sections, then re-edit; **≤4** = rewrite from the doc-type skeleton. Blocking findings — wrong or unverifiable facts, a procedure that can't be completed, information that exists only in an image or in an image without alt text — are a separate gate: the doc is **not shippable** at any score until they're fixed. Report the score, the failed rows, and the exact edits that reach 10/10. ## Framework ### 1. Know the Reader and the Document's Job **Core concept:** Every page serves one reader with one task. Name both before writing a word — audience and level, what they'll be able to do afterwards — and pick the document type that fits: tutorial (learn by doing), how-to (accomplish a task), concept (understand), reference (look up), README (orient and start). **Why it works:** Readers scan for their task; a page that mixes concept, procedure, and reference forces them to read everything to find anything. **Key insights:** - Google's Technical Writing course opens a doc with an audience statement and a scope plus non-scope statement — the non-scope rescues readers who are on the wrong page - "Before you begin" lists prerequisites before step 1, not inside step 4 (convention) - Key points first: the intro states what the reader gets, not the product's history - Every procedural page ends with verification ("Confirm that…") and "What's next" (convention) **Applications:** | Context | Application | Example | |---------|-------------|---------| | README | Orient: what it is, who it's for, three-step start, links out | Purpose → install → first run → docs map | | Mixed page | Split concept from procedure into linked pages | "How OAuth works" + "Configure OAuth" | | Tutorial vs how-to | Tutorial teaches one path end to end; how-to assumes context | "Build your first plugin" vs "Add a hook" | See [references/document-types.md](references/document-types.md) when choosing or restructuring a doc type — skeletons for README, getting started, tutorial, how-to, and concept pages, the audience and scope statements, and the self-editing pass for large doc sets. ### 2. Voice: You, Active, Present, Timeless **Core concept:** Address the reader as "you", make the actor of every sentence explicit, describe behavior in the present tense, and write as if the page will be read in five years. **Key insights:** - "We" hides who acts; "the user" turns the reader into a third party — both weaken an instruction - Passive voice is allowed only when the actor is unknown or irrelevant ("The file is encrypted at rest") - "Will" belongs only to genuinely later effects: "The server sends an ack", not "will send" - Contractions are fine — Google prefers "isn't" over "is not" for negations `[EN]` - Software doesn't want, see, or think: "The API detects", not "the API sees" - No "please" (reserve it for asking permission), no "simply / easily / just", no superlatives — if a step is easy, the reader will notice - Timeless: cut "currently", "new", and "soon"; never pre-announce unreleased features **Before → after:** - "Please note that the new dashboard will simply be shown once the user has logged in." → "After you sign in, the dashboard appears." - "We recommend that the token is refreshed by the client." → "Refresh the token from the client." See [references/voice-and-words.md](references/voice-and-words.md) when a doc's tone is off or inconsistent — the voice rules with the guide's exact exceptions, inclusive and global-audience language, and the full word list. ### 3. Sentences and Words **Core concept:** Put the condition before the instruction, keep one idea per sentence, and choose the plain word the guide's word list prefers. **Key insights:** - "To delete the document, click **Delete**" — readers decide whether a step applies before they act, not after - Spell out an abbreviation on first use with the short form in parentheses; skip only universally known ones (URL, HTML) - Latin abbreviations translate and scan poorly: "for example", not "e.g."; "that is", not "i.e."; omit "etc." or finish the list `[EN]` - "can" = ability, "may" = permission, "might" = possibility `[EN]` - Word list samples `[EN]`: sign in (not log in) · set up as a verb · lets you (not allows you to) · through or by using (not via) · after (not once) · use (not leverage or utilize) · checkbox · email - Jargon is fine for the stated reader and a defect for anyone else — define it or link it **Before → after:** - "Click Save in order to persist the settings once you are done, i.e. when all fields are filled." → "After you fill in all fields, click **Save**." - "The CLI utilizes the GCP SDK (e.g. for auth)." → "The CLI uses the Google Cloud SDK, for example for authentication." See [references/voice-and-words.md](references/voice-and-words.md) when auditing word choice — the word list table (avoid → use → why), abbreviation rules, and modal verbs. ### 4. Structure: Headings, Lists, Tables, Notices **Core concept:** Structure is the reader's map. Headings in sentence case read as a table of contents; lists carry parallel items introduced by a full sentence; tables have header rows; notices are rare and mean something. **Key insights:** - Task headings are bare imperatives ("Create an instance"); concept headings are noun phrases ("Instance lifecycle"); no "-ing" headings - A list needs an introductory sentence ending in a colon, and every item in the same grammatical form; numbered only when order matters - Description lists (term → definition) beat two-column tables for paired data - Tables: header row, an intro sentence, no merged or empty cells — screen readers depend on it - Note = useful but optional; Caution = proceed carefully; Warning = harm or irreversible loss. Don't stack them; one per section is a practical ceiling (inferred) - Cross-references say "see", never "above" or "below" — pages reflow and get translated - Link text names the target ("see Configure a custom domain"), never "click here" - Alt text states the image's purpose; information must never live only in a picture **Applications:** | Context | Application | Example | |---------|-------------|---------| | Wall-of-text page | Insert a task heading wherever the task changes | "Install", "Configure", "Verify" | | Three stacked notes | Fold two into body text; keep the one that changes behavior | One **Caution** about data loss | | Options table | Header row + intro sentence + parallel cell phrasing | "The following flags control output:" | See [references/structure-and-formatting.md](references/structure-and-formatting.md) when fixing page structure — heading, list, table, notice, cross-reference, link-text, image, number, and date rules with before/after pairs. ### 5. Procedures and Code **Core concept:** A procedure is a numbered list of single imperative actions, each stating where to act and what to expect. Code is set in code font, introduced by a sentence ending in a colon, and uses placeholders the reader can't mistake for literals. **Key insights:** - One action per step; "Optional:" prefix for optional steps; a single step is a bullet, not "1." - Sub-steps run a, b, c; document the shortest path, not every alternative - UI element names in bold, matching on-screen casing; click for a mouse, tap for touch, select when device-agnostic - Code font for filenames, paths, commands, flags, parameters, and values — not for product names - Placeholders are `ALL_CAPS_WITH_UNDERSCORES`, never `<your-key>` or `YOUR_API_KEY`, and are explained right after the sample ("Replace `PROJECT_ID` with…") - Command syntax: `[optional]`, `{a|b}` for exclusive choices, `...` for repeatable arguments - Samples are runnable, minimal, wrapped at 80 characters, and show the expected output **Before → after:** - "Run the command below with your key: `shipit deploy --key=<your-key>`" → "To deploy, run the following command:" → fenced `shipit deploy --key=API_KEY` → "Replace `API_KEY` with the key from the **Settings** page." - "1. You should now click on the Deploy button to deploy." → "1. Click **Deploy**. The status changes to **Deploying**." See [references/procedures-and-code.md](references/procedures-and-code.md) when writing steps or samples — the full procedure rules, UI-element and device verbs, code-in-text, placeholder, command-line syntax, and the sample-code quality checklist. ### 6. Reference Docs: API, Docstrings, CLI Help **Core concept:** Reference text is descriptive, complete, and formulaic on purpose — readers look things up, so every entry must exist and read the same way. **Key insights:** - Document every public class, method, field, constant, and enum value; a missing entry reads as "unsupported" - Open method descriptions with the category verb: "Gets the…", "Sets the…", "Checks whether…", "Creates a…", "Returns…" — never "This method…" - Non-boolean parameters start "The…" or "A…"; booleans read "If true, … If false, …" (action) or "True if …; false otherwise" (state) - Document return values and exceptions ("Thrown when…") for every method that has them - A deprecated element names its replacement in the first sentence - CLI `--help` (convention — Google has no `--help` page): usage line in `[optional]` syntax, one-line synopsis, every flag described with the same placeholder style **Before → after:** - "This method is used for getting the customer." → "Gets the customer for the given `customerId`. Throws `NotFoundError` when no customer exists." - "@param force - force flag" → "@param force If true, deletes the bucket even if it contains objects. If false, fails when the bucket isn't empty." See [references/api-reference.md](references/api-reference.md) when writing or auditing reference material — the verb-by-category table, parameter, return, and exception patterns, one complete JSDoc example, and CLI help conventions. ### 7. Release Notes, Changelogs, Migration Guides **Core concept:** A changelog is documentation for the reader who is about to upgrade. Each entry states what changed, what it means for them, and what to do — in the structure of Keep a Changelog, in the voice of the rest of the docs. **Key insights:** - Newest version first, an `Unreleased` section on top, ISO dates in version headings, version headings linked to diffs (Keep a Changelog) - Group entries under Added / Changed / Deprecated / Removed / Fixed / Security; never paste commit messages - Breaking changes go first in the version, with a link to migration steps (convention) - A deprecation entry names the replacement and the removal version or date - A migration guide is a procedure: "Before you begin" (versions, backups), numbered steps with before/after snippets, "Verify the migration", rollback - Apply the Google layer to every entry: second person for actions, no "currently/new", code font for flags and APIs, one tense used consistently **Before → after:** - "Various improvements to the auth module (#412)" → "Changed: `login()` now returns a `Session` instead of a token string. Update callers that read `.token` — see *Migrate to sessions*." See [references/release-notes.md](references/release-notes.md) when writing release notes or a migration guide — the Keep a Changelog skeleton, entry patterns per category, deprecation wording, and the migration-guide procedure. ### 8. Running the Audit, Rewrite, or Write **Core concept:** Three modes, one discipline: intake → local conventions → read as the reader → verify facts → apply rules by severity → output in a fixed shape. **Protocol:** 1. **Intake.** Confirm the mode (audit, improve, or write), document type, reader and level, and language. For *write*, the reader's task and the fact sources (code paths, existing docs) are required — don't start without them. 2. **Local style guide.** Look for `CONTRIBUTING.md`, `STYLE.md`, `docs/style-guide.md`, `.vale.ini`, and the conventions existing docs already follow (for example, "log in" everywhere). They win over Google. Vale with the `Google` package automates the `[EN]` word and punctuation layer if the project wants a linter. 3. **Read `references/audit-checklist.md`** before any audit or improve pass — the rule IDs cited in findings live there; never cite an ID you haven't read. 4. **Read the doc cold** as the target reader, then check every command, flag, parameter, and behavior against the code before judging style. A stylish wrong doc is worse than an ugly right one. 5. **Apply rules in severity order:** Blocking → High (structure, accessibility, missing reference entries) → Medium (voice, notices, intro sentences) → Low (word list, punctuation `[EN]`). 6. **Output.** A finding's location is one the reader can find: the heading path, plus the line number when auditing a file. Improve = a one-line `Score before → after`, the full rewritten document, then a `## Change log` table (Change | Rule ID + name | Why). Facts stay untouched — a fact stated in the source document counts as received from the user, so keep it (with `TODO(verify): …` when no code confirms it) rather than deleting it. Write = the document, with `TODO(verify)` for every gap. Never include a command, flag, or parameter you didn't see in code or receive from the user. ALWAYS output audits in this format: ``` # Documentation Audit: [path or title] **Score:** X/10 — [band] **Shippable:** yes | no (blocking findings below) **Diagnostic:** N/10 — failed rows: [row numbers + one-line reason each] **Doc type / reader:** [type] for [audience, level] **Language:** [en | xx — [EN] rules skipped] **Local style guide:** [file found and honored | none — Google applies] **Blocking:** [wrong/unverifiable facts, unfollowable steps, image-only information — or "none"] **Findings:** | # | Location | Rule (ID + name) | Before | After | Severity | **Rewrite plan:** [ordered: structure → voice → words; what to do first to reach 10/10] ``` See [references/audit-checklist.md](references/audit-checklist.md) when running any audit or rewrite — the full rule table with IDs and severities, the severity rubric, non-English handling, a Vale configuration, and a worked mini-audit. ## Common Mistakes | Mistake | Why It Fails | Fix | |---------|-------------|-----| | Organizing by feature instead of reader task | Readers hunt across sections for one workflow | Name the reader's task; pick the doc type; one task per page | | Fixing style before verifying facts | Polished wrong instructions are trusted longer | Check every command and parameter against code first | | "Click here" and "see below" | Meaningless out of context, to screen readers, and after reflow | Link text names the target; cross-refs say "see" | | Steps buried in paragraphs, passive and future tense | Reader can't tell who does what, or in what order | Numbered imperative steps, condition first, present tense | | Stacked Note/Warning boxes | Everything shouted, nothing heard | One notice per section; the rest becomes body text | | `<your-key>` or `YOUR_API_KEY` placeholders | Reader types the brackets or reads the prefix as a literal | `API_KEY` in caps, explained after the sample | | Rewriting the meaning while "fixing style" | Reviewer approves prose, ships wrong behavior | Facts unchanged; unknowns become `TODO(verify)` | ## Quick Diagnostic | Question | If No | Action | |----------|-------|--------| | Does the first paragraph say who the doc is for and what they'll be able to do? | Readers can't tell if they're on the right page | Add audience, outcome, and non-scope statements | | Does the doc type match the reader's task (tutorial · how-to · concept · reference · README)? | Concept and steps interleave; nothing is findable | Split by type; link between pages | | Is every command, flag, parameter, and behavior verified against code or the user? | The doc teaches something false | Verify or mark `TODO(verify)`; not shippable until fixed | | Do headings read as a sentence-case table of contents (tasks imperative, concepts noun phrases)? | Scanning fails; "-ing" headings hide the action | Rewrite headings; add one where each new task starts | | Are all sequences numbered steps, one imperative action each, condition first? | Readers miss steps or act before checking | Convert paragraphs to steps; move conditions forward | | Is every code sample introduced by a colon sentence, with `ALL_CAPS` placeholders explained? | Readers paste literals or don't know what the sample does | Add intro sentences; fix and explain placeholders | | Is the text in second person, active voice, present tense, with no please/simply/just and no anthropomorphism? | Instructions read as narration | Rewrite sentence by sentence; cut filler | | Are links descriptive, cross-refs "see"-based, images alt-texted, tables headed? | Screen readers and reflow break the page | Fix each; move image-only information into text | | Is it timeless — no "currently/new/soon", no pre-announced features? | The doc rots the day it ships | Remove time words; describe only shipped behavior | | `[EN]` Does it follow the word list, serial comma, contractions, and American spelling — or the local guide? | Small inconsistencies erode trust | Apply the word list; run Vale if configured | ## About the Source Google's Developer Documentation Style Guide is the public house style that Google's technical writers maintain for developers.google.com, Android, and Google Cloud documentation; the companion Technical Writing One and Two courses are Google's internal engineer training, released publicly. This skill adapts both under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) (per [Google's site policies](https://developers.google.com/terms/site-policies)) and adds Keep a Changelog for release notes; it is an independent adaptation, not endorsed by Google. ## Further Reading - [Google Developer Documentation Style Guide](https://developers.google.com/style) — start with [Highlights](https://developers.google.com/style/highlights) and the [Word list](https://developers.google.com/style/word-list) - [Technical Writing One](https://developers.google.com/tech-writing/one) and [Technical Writing Two](https://developers.google.com/tech-writing/two) — Google's courses on words, sentences, documents, self-editing, and sample code - [Keep a Changelog](https://keepachangelog.com/) — the changelog structure this skill uses for release notes - [*"Docs for Developers: An Engineer's Field Guide to Technical Writing"*](https://www.amazon.com/Docs-Developers-Engineers-Technical-Writing/dp/1484272161?tag=wondelai00-20) by Jared Bhatti, Zachary Sarah Corleissen, Jen Lambourne, David Nunez, and Heidi Waterhouse - [*"Every Page Is Page One: Topic-Based Writing for Technical Communication and the Web"*](https://www.amazon.com/Every-Page-One-Topic-Based-Technical/dp/1937434281?tag=wondelai00-20) by Mark Baker
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.