From 1c5f7e040b006996d8e70a3f1362158ed1837eb4 Mon Sep 17 00:00:00 2001 From: vraspar Date: Sun, 17 May 2026 15:36:13 -0700 Subject: [PATCH 01/13] docs: enforce writing style at edit time via Vale + hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inconsistent voice and AI slop survived our PR-time-only Vale check. This adds: - PostToolUse hook in .claude/settings.json that runs Vale on .mdx edits, surfacing violations immediately instead of at PR review time - New FirstPersonPlural Vale rule blocking "we"/"our"/"let's" — the hardest manual rule from prior writing feedback - Remove AllowsYouTo rule; proselint + write-good cover the patterns - Slim CLAUDE.md to project context + hard constraints + skill pointer The full prose style guide moves to a workspace-level writing-docs skill (separate change). Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/settings.json | 15 +++++++++++++++ CLAUDE.md | 20 +++++++++++++++----- styles/x402r/AllowsYouTo.yml | 10 ---------- styles/x402r/FirstPersonPlural.yml | 10 ++++++++++ 4 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 .claude/settings.json delete mode 100644 styles/x402r/AllowsYouTo.yml create mode 100644 styles/x402r/FirstPersonPlural.yml diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..d4f1813 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "f=$(jq -r '.tool_input.file_path // empty' 2>/dev/null); case \"$f\" in *.mdx) command -v vale >/dev/null 2>&1 && (cd \"$CLAUDE_PROJECT_DIR/docs\" && vale \"$f\" 2>&1) || true ;; esac" + } + ] + } + ] + } +} diff --git a/CLAUDE.md b/CLAUDE.md index d587c8a..fbce74c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,19 +2,29 @@ Mintlify docs at docs.x402r.org. Config in `docs.json`. +## Workflow + +For any non-trivial doc work, invoke the **`writing-docs` skill** — it loads the style guide, per-page creative briefs, quality gates, and source-file map, and walks the full workflow. + ## Commands ```bash npx mint dev # Preview at localhost:3000 ``` -## Standards +## Hard constraints - YAML frontmatter required on all MDX files (`title`, `description`) -- Second person ("You can..."), active voice, short paragraphs (2-4 sentences) -- **Never use em dashes (`—`).** Use a comma, period, or rewrite the sentence instead. +- Real addresses from `x402r-sdk/packages/core/src/config/index.ts` — never placeholders like `0xYourAddress` or `` +- Verify every export, function name, and file path exists before referencing +- Test all code examples before including +- Never use em dashes (`—`) — use a comma, period, or rewrite - Use Mermaid for diagrams, never ASCII art - Relative paths for internal links, alt text on images -- Test all code examples before including, use realistic values -- All install commands must be copy-pasteable. Use `CodeGroup` with npm/pnpm/bun tabs for every install command. - Components: `Note`, `Tip`, `Warning`, `Info`, `Steps`, `CardGroup`, `Tabs`, `Accordion`, `CodeGroup` + +## Style + +Style rules (anti-slop, voice, structure) live in: +- `.vale.ini` + `styles/x402r/` — deterministic enforcement (runs in CI and via PostToolUse hook on `.mdx` edits) +- `.claude/skills/writing-docs/references/style-guide.md` — full prose style guide (loaded by the skill) diff --git a/styles/x402r/AllowsYouTo.yml b/styles/x402r/AllowsYouTo.yml deleted file mode 100644 index 74d0c45..0000000 --- a/styles/x402r/AllowsYouTo.yml +++ /dev/null @@ -1,10 +0,0 @@ -extends: existence -message: "Rewrite without 'allows you to' — state what it does directly." -level: warning -ignorecase: true -tokens: - - allows you to - - enabling you to - - enables you to - - makes it easy to - - gives you the ability to diff --git a/styles/x402r/FirstPersonPlural.yml b/styles/x402r/FirstPersonPlural.yml new file mode 100644 index 0000000..088e50d --- /dev/null +++ b/styles/x402r/FirstPersonPlural.yml @@ -0,0 +1,10 @@ +extends: existence +message: "Use second person — avoid first-person plural: '%s'" +level: warning +ignorecase: true +tokens: + - '\bwe\b' + - '\bour\b' + - '\bours\b' + - '\bourselves\b' + - '\blet''s\b' From 0f768eeb4cfd3f6171f2f9df029001bd3544248c Mon Sep 17 00:00:00 2001 From: vraspar Date: Sun, 17 May 2026 15:54:21 -0700 Subject: [PATCH 02/13] docs: parse .mdx as md so local Vale runs without mdx2vast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PostToolUse hook would error on every .mdx edit because Vale needs a separate mdx2vast binary to parse MDX, which isn't bundled with the brew install. Treating .mdx as Markdown via [formats] lets Vale lint prose with zero external dependencies. CI is unaffected — errata-ai/vale-action installs its own MDX parser. Verified locally: FirstPersonPlural, Slop, and Microsoft.We all fire on a test .mdx fixture as expected. Co-Authored-By: Claude Opus 4.7 (1M context) --- .vale.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.vale.ini b/.vale.ini index f0d3760..451469b 100644 --- a/.vale.ini +++ b/.vale.ini @@ -3,6 +3,12 @@ MinAlertLevel = warning Packages = Microsoft, write-good, proselint +# Parse .mdx as Markdown so Vale doesn't need the separate mdx2vast binary +# (used by the PostToolUse hook for local edit-time linting). The CI action +# (errata-ai/vale-action) installs its own MDX parser server-side. +[formats] +mdx = md + [*.mdx] BasedOnStyles = Vale, Microsoft, write-good, proselint, x402r From 1d116610eabb69bc8bf36f779159ccbed316ef09 Mon Sep 17 00:00:00 2001 From: vraspar Date: Sun, 17 May 2026 16:02:54 -0700 Subject: [PATCH 03/13] docs: add writing-docs skill packaging the prose guide as a workflow Same diagnosis as the Vale hook commit: voice and structure feedback weren't landing consistently because nothing forced Claude to load the existing prose guide before writing. This wraps the guide as a Claude Code Skill with progressive disclosure (short SKILL.md + on-demand references): - SKILL.md: 10-step workflow (clarify, load refs, match brief, read source, read neighbors, outline, draft, quality gates, vale, preview). Auto-triggers on docs/**/*.mdx work. - references/style-guide.md: 7 principles, anti-slop list, sentence rules, style exemplar - references/page-briefs.md: per-page creative briefs for Getting Started, Guides, API Reference, Configuration - references/quality-gates.md: 7-gate pre-merge checklist + Vale enforcement notes - references/source-files.md: critical source files map + role availability table + protocol facts The skill points at the PostToolUse hook added earlier on this branch so Vale runs automatically after each .mdx edit and Claude self-corrects before the user sees the draft. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/writing-docs/SKILL.md | 92 ++++++++ .../writing-docs/references/page-briefs.md | 212 ++++++++++++++++++ .../writing-docs/references/quality-gates.md | 48 ++++ .../writing-docs/references/source-files.md | 77 +++++++ .../writing-docs/references/style-guide.md | 116 ++++++++++ 5 files changed, 545 insertions(+) create mode 100644 .claude/skills/writing-docs/SKILL.md create mode 100644 .claude/skills/writing-docs/references/page-briefs.md create mode 100644 .claude/skills/writing-docs/references/quality-gates.md create mode 100644 .claude/skills/writing-docs/references/source-files.md create mode 100644 .claude/skills/writing-docs/references/style-guide.md diff --git a/.claude/skills/writing-docs/SKILL.md b/.claude/skills/writing-docs/SKILL.md new file mode 100644 index 0000000..977a5ef --- /dev/null +++ b/.claude/skills/writing-docs/SKILL.md @@ -0,0 +1,92 @@ +--- +name: writing-docs +description: Use when writing, updating, or editing pages in the x402r Mintlify docs (docs/**/*.mdx) — including new feature documentation, guide updates, API reference pages, or any content authored for docs.x402r.org. Loads the x402r writing style, anti-slop rules, per-page creative briefs, quality gates, and source-file map. Walks through clarify → load references → match brief → read source → read neighbors → outline → draft → quality gates → vale → preview. +--- + +# writing-docs + +Workflow for writing x402r docs that match the existing voice, structure, and technical accuracy bar. + +## When this applies + +- Writing a new docs page in `docs/**/*.mdx` +- Updating an existing docs page significantly (not just typo fixes) +- Documenting a newly shipped SDK feature +- Drafting reference content for `@x402r/*` packages + +For tiny edits (one-line fix, typo, link update), skip the full workflow. + +## Workflow + +Create a TodoWrite task per step. + +### 1. Clarify + +Confirm: +- What's being documented (specific feature, API, concept) +- Page type (overview, installation, tutorial, guide, API reference, configuration) +- Audience (first-time reader, integrator, reference seeker) + +If anything is ambiguous, ask the user before proceeding. + +### 2. Load references + +Read `references/style-guide.md` and `references/page-briefs.md`. + +### 3. Match the page to a Creative Brief + +Check `references/page-briefs.md` for a matching brief. If one exists, follow its Angle, Element table, and Omit list verbatim. + +If no brief matches, pick a page type using Diataxis (reference / how-to / tutorial / explanation) and tell the user — they may want to add a brief. + +### 4. Read source + +From `references/source-files.md`, identify the source files relevant to this page. Read them. Never document an API without reading its implementation. Verify role availability from the table in `source-files.md`. + +### 5. Read neighbors + +Read 2–3 existing docs pages in the same `docs/` section. Match heading depth, intro pattern, code-block conventions, Mintlify component usage, and frontmatter shape. + +### 6. Propose outline + +For new pages or significant rewrites, propose the section structure and Diataxis framing before drafting. Wait for user approval. + +For minor additions to existing pages, skip outline approval. + +### 7. Draft + +Apply `references/style-guide.md` while writing. Hard rules: + +- Second person only — no "we", "our", "let's" (enforced by Vale via `FirstPersonPlural`) +- Real addresses from `x402r-sdk/packages/core/src/config/index.ts` — never `0xYourAddress` or `` +- Verify every export, function name, and file path exists before referencing +- Lead with WHY, code first, prose second (see `style-guide.md` § Seven Principles) +- No em dashes (`—`) — use commas, periods, or rewrite + +### 8. Quality gates + +Run through `references/quality-gates.md` (7 gates): +- Cover the code (prose-only readthrough makes sense?) +- Cover the prose (code-only readthrough makes sense?) +- Grep for slop (zero matches on kill-list) +- Unique info per paragraph +- Technical accuracy (every signature, import, address) +- Structural variety vs neighbor pages +- Mintlify check (broken links, frontmatter) + +### 9. Vale + +Run `cd docs && vale `. Fix every error and warning unless justified in writing as a false positive. + +The PostToolUse hook in `docs/.claude/settings.json` runs Vale automatically on `.mdx` edits — fix the violations it surfaces in the next turn. + +### 10. Preview + +Run `npx mint dev` from `docs/`. Visually confirm the page renders correctly before declaring done. + +## References + +- `references/style-guide.md` — 7 principles, anti-slop list, sentence rules, style exemplar +- `references/page-briefs.md` — per-page creative briefs + drafting prompt structure +- `references/quality-gates.md` — 7-gate pre-merge checklist + anti-slop verification pass +- `references/source-files.md` — critical source files map + role availability table diff --git a/.claude/skills/writing-docs/references/page-briefs.md b/.claude/skills/writing-docs/references/page-briefs.md new file mode 100644 index 0000000..008b7f6 --- /dev/null +++ b/.claude/skills/writing-docs/references/page-briefs.md @@ -0,0 +1,212 @@ +# Page Creative Briefs + +WHAT to write for each x402r SDK doc page. Per-page angle, element guidance, omit list, and source files. + +For pages not listed here, pick a Diataxis type (reference / how-to / tutorial / explanation) and propose adding a brief. + +--- + +## Variation Injection + +Add a page-specific angle to prevent uniformity: + +| Page type | Angle instruction | +|-----------|-------------------| +| Overview | "Write like a landing page. Reader is deciding whether to invest time." | +| Installation | "Shortest possible useful page. Zero to running in 60 seconds of reading." | +| Create-client | "Write like a decision tree. Reader picks between `createX402r()` and role presets." | +| TypeScript | "Write as a reference sheet. Someone will Ctrl+F here. Prioritize scannability." | +| Guides | "Write like a story: setup, action, verification. Reader copies and runs." | +| API reference | "Write as a lookup table. Developers arrive from other pages to check a method." | + +--- + +## Drafting prompt structure + +When drafting a page, assemble these five inputs (mentally — they shape the draft): + +**Input 1 — Source files** (not just types — implementation too, for edge cases) +Read the relevant `.ts` files from `source-files.md`. + +**Input 2 — Working example** from `x402r-sdk/examples/` +Find and read the relevant example file. + +**Input 3 — Creative brief** +Pull the brief for this page from the sections below. + +**Input 4 — Style exemplar** +Always re-read `docs/x402-integration/overview.mdx`. It leads with contrast ("exact scheme works well for immediate-delivery... but creates friction for"), uses dollar amounts, shows concrete scenarios. + +**Input 5 — Positive writing rules** (from `style-guide.md`) +1. Open with a sentence that answers "why would I read this page?" +2. Show code before explaining it. +3. Use real addresses. +4. Explain non-obvious defaults and constraints; skip the obvious. +5. State tradeoffs when there are two ways. +6. Acknowledge limitations. +7. Vary sentence length. +8. Never use slop list words. No exclamation marks. +9. Every paragraph adds new information. +10. Code comments explain "why," not "what." + +--- + +## Getting Started (4 pages) + +### `sdk/overview.mdx` — "Am I in the Right Place?" + +**Angle:** Landing page. Reader decides whether to invest time. + +| Element | Guidance | +|---------|----------| +| Opening hook | Lead with x402 vs x402r delta (what x402r *adds*), not a definition | +| Packages | 3 cards: `@x402r/sdk` (most users), `@x402r/core` (low-level), `@x402r/helpers` (x402 server). Link each to its next page | +| Action groups | Table of 8 groups, one-line descriptions, link to API ref | +| Role presets | Brief: "TypeScript autocomplete only shows methods your role can call." | +| Chains | Real table from `x402rChains` — all chains with USDC addresses | +| Honest status | Warning: testnet-tested, mainnet deployed but less battle-tested | +| **Omit** | Don't explain escrow (link to Protocol tab). Don't explain viem. | + +**Source files:** `client.ts`, `types.ts` (X402r interface), `config/index.ts` + +### `sdk/installation.mdx` — "60 Seconds to Running" + +**Angle:** Shortest page on the site. Zero to running in under a minute. + +| Element | Guidance | +|---------|----------| +| Opening | No intro paragraph. Jump straight to install command | +| Primary install | `npm install @x402r/sdk viem` (CodeGroup: npm/pnpm/bun). One path. | +| Secondary | Accordion for `@x402r/core` and `@x402r/helpers` (for those who know they need them) | +| Viem clients | `createPublicClient` + `createWalletClient` with `baseSepolia`. Reference viem docs. | +| Chain config | `getChainConfig(84532)` showing return shape | +| **Omit** | No `.env` file ceremony. No lengthy private key warnings (guide territory). | + +**Source files:** `config/index.ts` (for `getChainConfig` return type) + +### `sdk/create-client.mdx` — "Choose Your Path" + +**Angle:** Decision tree. Reader picks between full client and role presets. + +| Element | Guidance | +|---------|----------| +| Opening | Frame as a decision, not a catalog | +| Decision guidance | When to use `createX402r()` (multi-role, read-only, admin) vs presets (most apps). Be opinionated. | +| Full client | Real config with Base Sepolia addresses. Config table — only non-obvious fields get prose. | +| Role presets | 3 concise examples. Highlight narrowing: "exposes `refund.request()` but hides `payment.authorize()` — payers don't authorize" | +| `canExecute` | One-liner. "Checks condition slots before submitting. Saves gas." | +| `createMemoryStore` | One-liner. "In-memory store for dev. Data lost on exit." | +| `.extend()` | Brief example, link to extend-plugins guide | +| **Omit** | Don't enumerate every method per role (that's typescript.mdx). Don't repeat viem setup. | + +**Source files:** `client.ts`, `presets.ts`, `types.ts`, `can-execute.ts` + +### `sdk/typescript.mdx` — "Reference Sheet" + +**Angle:** Scannable reference. People Ctrl+F here for type names. + +| Element | Guidance | +|---------|----------| +| Opening | One line: "TypeScript 5.0+ with `strict: true`." | +| Type imports | Key types from `@x402r/sdk` and `@x402r/core` | +| Role narrowing | Tables per preset: which groups, which methods. From `types.ts` Pick types (lines 235-379) | +| Conditional groups | Why `escrow`/`refund`/`evidence`/`freeze`/`query` can be `undefined`. Null-check pattern. | +| Error types | `ValidationError`, `ConfigError`, `ContractCallError` — when each is thrown | +| **Omit** | Don't repeat config table from create-client. Don't list full method signatures. | + +**Source files:** `types.ts` (lines 235-379 for Pick types), `presets.ts` + +--- + +## Guides (6 pages) + +### `sdk/guides/deploy-operator.mdx` — "Get Your Operator Running" + +**Hook:** "Every x402r payment flows through a PaymentOperator. Deploy one before you can authorize, release, or refund." + +**Format:** Recipe. Prerequisites -> `deployMarketplaceOperator()` with real options -> What gets deployed -> Verify on basescan -> Connect result to SDK config. + +**Source:** `deploy/presets.ts`, `examples/shared/anvil-setup.ts` + +### `sdk/guides/accept-payments.mdx` — "Merchant Payment Lifecycle" + +**Hook:** "A payment arrives when the payer's authorization hits your operator. After escrow, release the funds. If disputed, handle the refund." + +**Arc:** Payment arrives (check state) -> Wait for escrow -> Release -> Handle disputes -> Post-escrow refund. Key insight: `payment.authorize()` is called by facilitator, not merchant. Merchant's first interaction is checking state. + +**Source:** `examples/merchant/release-escrow.ts`, `examples/scenarios/happy-path-release.ts` + +### `sdk/guides/request-refund.mdx` — "Payer Dispute Flow" + +**Hook:** "Paid for something that didn't arrive? Request a refund during escrow. If ignored, freeze and submit evidence." + +**Flow:** Request -> Check status -> Cancel (optional) -> Freeze -> Submit evidence -> Wait for arbiter. + +**Source:** `examples/payer/request-refund.ts`, `examples/payer/freeze-payment.ts` + +### `sdk/guides/resolve-disputes.mdx` — "Arbiter Decision Guide" + +**Hook:** "Review refund requests and decide: approve, deny, or decline to rule." + +**Flow:** List pending -> Review evidence -> Decide -> Unfreeze -> Distribute fees. Key insight: `refundInEscrow()` triggers RefundRequest recorder auto-approve — no separate `approve()` call. + +**Source:** `examples/arbiter/approve-refund.ts`, `examples/scenarios/dispute-resolution.ts` + +### `sdk/guides/watch-events.mdx` — "Real-Time Events" + +Short page. 4 watchers, unsubscribe pattern, transport note, type caveat about `unknown`. + +**Source:** `types.ts` (WatchActions) + +### `sdk/guides/extend-plugins.mdx` — "Custom Plugins" + +Short page. `.extend()` pattern, when to use, built-in plugin factories. + +**Source:** `client.ts` (buildExtend function) + +--- + +## API Reference (8 pages) + +Consistent format per method (uniform is intentional for lookup material): + +``` +### methodName + +[One sentence: what it does and when you use it] + +\`\`\`typescript +const result = await client.group.methodName(param1, param2) +\`\`\` + +| Parameter | Type | Description | +|-----------|------|-------------| +| param1 | `Type` | [what it means, not what it is] | + +**Returns:** `Type` — [what the return value represents] +**Role availability:** Payer / Merchant / Arbiter +**Errors:** [common error cases] +``` + +| Page | Key notes | +|------|-----------| +| `sdk/api/payment.mdx` (9 methods) | Open with `authorize` vs `charge` difference. `getState` returns `[boolean, bigint, bigint]` — name them. | +| `sdk/api/escrow.mdx` (3 methods) | All read-only. Brief page. | +| `sdk/api/refund.mdx` (15 methods) | Split: Write ops / Read ops. Document `RefundRequestStatus` enum. | +| `sdk/api/evidence.mdx` (4 methods) | IPFS CID convention. `submit` is write, rest are read. | +| `sdk/api/freeze.mdx` (3 methods) | Payer freezes, arbiter unfreezes. | +| `sdk/api/query.mdx` (3 methods) | Explain resolver priority: store -> recorder -> events. | +| `sdk/api/operator.mdx` (8 methods) | `getConfig()` returns full slot layout. `distributeFees` is write. | +| `sdk/api/watch.mdx` (4 methods) | Unsubscribe pattern. `unknown` type note. | + +--- + +## Configuration + Integrations (5 pages) + +| Page | Content | +|------|---------| +| `sdk/config/chains.mdx` | Full chain table. All lookup functions. "Identical addresses via CREATE3. Only USDC differs." | +| `sdk/config/addresses.mdx` | Protocol, factory, singleton address tables. CREATE3 explanation. | +| `sdk/integrations/x402-server.mdx` | `forwardToArbiter()` from `@x402r/helpers`. Express + Hono examples. `@x402r/evm` peer dep. | +| `sdk/integrations/facilitator.mdx` | Minimal. Port from existing. | +| `sdk/integrations/examples.mdx` | CardGroup linking to GitHub example directories. | diff --git a/.claude/skills/writing-docs/references/quality-gates.md b/.claude/skills/writing-docs/references/quality-gates.md new file mode 100644 index 0000000..b2df523 --- /dev/null +++ b/.claude/skills/writing-docs/references/quality-gates.md @@ -0,0 +1,48 @@ +# Quality Gates + +HOW to verify a docs page before declaring done. Pre-merge checklist (7 gates) and anti-slop verification pass. + +--- + +## Pre-Merge Checklist + +| # | Gate | How to test | +|---|------|-------------| +| 1 | **Cover the code** | Read only prose (ignore code blocks). Does it tell a coherent story? | +| 2 | **Cover the prose** | Read only code blocks. Can you follow the flow? If yes, prose may be redundant. | +| 3 | **Grep for slop** | Search for kill-list words. Zero matches required. | +| 4 | **Unique info** | Annotate each paragraph: what NEW info does it add? No annotation = filler = delete. | +| 5 | **Technical accuracy** | Every signature matches `types.ts`. Every import resolves. Every address matches `config/index.ts`. | +| 6 | **Structural variety** | Compare first paragraphs across same-batch pages. >50% same pattern = rewrite outliers. | +| 7 | **Mintlify check** | `npx mintlify broken-links` passes. Preview deploy loads. Frontmatter has `title` + `description`. | + +--- + +## Anti-Slop Verification Pass + +After generating a page, run this review checklist: + +1. Does the opening sentence *motivate* (not describe)? +2. Any sentences true of *any* SDK? (Generic = slop. Delete.) +3. Placeholder values that could be real addresses? +4. Consecutive paragraphs mergeable without info loss? +5. Every code block shows something the reader will actually type or run? +6. Any "allows you to" / "enables" / "provides" / "makes it easy"? +7. Any sentence a developer would skip because it states the obvious? + +For each issue, suggest a concrete replacement. Then rewrite. + +--- + +## Vale enforcement + +Vale runs automatically via the PostToolUse hook in `docs/.claude/settings.json` on any `.mdx` Edit/Write. It catches: + +- `Slop.yml` — filler/marketing language ("seamless", "leverage", "unlock the", etc.) +- `Enthusiasm.yml` — enthusiasm markers ("Amazing", "Exciting", etc.) +- `FirstPersonPlural.yml` — "we", "our", "let's" +- `Microsoft`, `write-good`, `proselint` — standard prose-quality packs + +Run manually: `cd docs && vale path/to/file.mdx` + +Fix every error and warning before claiming done. If a warning is a false positive, justify it in writing (do not silently ignore). diff --git a/.claude/skills/writing-docs/references/source-files.md b/.claude/skills/writing-docs/references/source-files.md new file mode 100644 index 0000000..fce637c --- /dev/null +++ b/.claude/skills/writing-docs/references/source-files.md @@ -0,0 +1,77 @@ +# Source Files & Role Availability + +WHERE to find the source code that backs each docs page, and which roles can call each method. + +--- + +## Critical Source Files + +| Purpose | Path | +|---------|------| +| Action group interfaces + role Pick types | `x402r-sdk/packages/sdk/src/types.ts` | +| Client factory + extend pattern | `x402r-sdk/packages/sdk/src/client.ts` | +| Role presets + validation | `x402r-sdk/packages/sdk/src/presets.ts` | +| Chain config + all addresses | `x402r-sdk/packages/core/src/config/index.ts` | +| Deploy presets | `x402r-sdk/packages/core/src/deploy/presets.ts` | +| Working scenarios | `x402r-sdk/examples/scenarios/` | +| Style exemplar | `docs/x402-integration/overview.mdx` | + +Always read the source before documenting an API. Verify file paths still exist — the SDK reorganizes occasionally. + +--- + +## Role Availability Reference + +From `types.ts` Pick types (lines 235-379). **Verify against source before publishing** — methods get added and removed. + +| Method | Payer | Merchant | Arbiter | +|--------|:-----:|:--------:|:-------:| +| `payment.authorize` | | Y | | +| `payment.charge` | | Y | | +| `payment.release` | | Y | | +| `payment.refundInEscrow` | | Y | Y | +| `payment.refundPostEscrow` | | Y | | +| `payment.approvePostEscrowRefund` | | Y | | +| `payment.getPostEscrowRefundAllowance` | | Y | | +| `payment.getState` | Y | Y | Y | +| `payment.getAmounts` | Y | Y | Y | +| `refund.request` | Y | | | +| `refund.cancel` | Y | | | +| `refund.deny` | | | Y | +| `refund.refuse` | | | Y | +| `refund.get` | Y | Y | Y | +| `refund.getByKey` | Y | Y | Y | +| `refund.getStatus` | Y | Y | Y | +| `refund.has` | Y | Y | Y | +| `refund.getStoredPaymentInfo` | Y | Y | Y | +| `refund.getPayerRequests` | Y | | | +| `refund.getReceiverRequests` | | Y | | +| `refund.getOperatorRequests` | | | Y | +| `refund.getCancelCount` | Y | Y | Y | +| `refund.getCancelledAmount` | Y | Y | Y | +| `freeze.freeze` | Y | | | +| `freeze.unfreeze` | | | Y | +| `freeze.isFrozen` | Y | Y | Y | +| `evidence.*` (all) | Y | Y | Y | +| `escrow.*` (all) | Y | Y | Y | +| `query.*` (all) | Y | Y | Y | +| `operator.getConfig` | Y | Y | Y | +| `operator.getFeeAddresses` | Y | Y | Y | +| `operator.calculateFees` | | Y | | +| `operator.calculateOperatorFeeBps` | | Y | | +| `operator.calculateProtocolFeeBps` | | Y | | +| `operator.getAuthorizedFees` | | Y | | +| `operator.getAccumulatedProtocolFees` | | Y | Y | +| `operator.distributeFees` | | Y | Y | +| `watch.*` (all) | Y | Y | Y | + +--- + +## Protocol facts + +x402r-specific accuracy notes that don't live in the code: + +- Payer signs the authorization; merchant submits. Never say "merchant authorizes." +- `viem` and `@x402r/core` are regular deps of `@x402r/sdk`, not peer deps. Don't tell users to install them separately unless importing directly from `@x402r/core`. +- `@x402r/helpers` is merchant-server only — client-side utilities go in `@x402r/sdk`. +- Verify function signatures against `types.ts` and `presets.ts` before documenting. diff --git a/.claude/skills/writing-docs/references/style-guide.md b/.claude/skills/writing-docs/references/style-guide.md new file mode 100644 index 0000000..da8a62e --- /dev/null +++ b/.claude/skills/writing-docs/references/style-guide.md @@ -0,0 +1,116 @@ +# Style Guide + +Rules for HOW to write x402r docs. The Diagnostic Test, seven principles, anti-slop word list, sentence-level rules, and the style exemplar. + +--- + +## The Diagnostic Test + +Every paragraph must pass: **"Would a developer who already has the source code learn something from this sentence that the code alone does not tell them?"** If the answer is no, delete the paragraph. + +--- + +## Seven Principles + +### 1. Lead with WHY, not WHAT + +**Bad:** "The x402r SDK provides TypeScript bindings for the x402r refundable payments protocol." + +**Good:** "x402 payments are instant and irreversible. x402r adds escrow holds, refund windows, and dispute resolution on top." + +The bad version describes the page topic. The good version tells you why you need it by establishing contrast. Open every page by answering the question the reader actually has: "Should I keep reading?" + +### 2. Code first, prose second + +Show the code block, then annotate only what the code does NOT make obvious. If `getChainConfig(84532)` is self-explanatory, don't add "This retrieves the chain configuration for Base Sepolia." Instead note something useful: "USDC is the only address that differs between chains — everything else is identical via CREATE3." + +### 3. Real values, never placeholders + +Use actual addresses from `x402r-sdk/packages/core/src/config/index.ts`: + +| Use this | Not this | +|----------|----------| +| `0x3Cd5c76Fefe46CB07788Ee8f80B93B20D81941D4` | `'0xYourOperator...'` | +| `84532` | `` | +| `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | `'YOUR_USDC_ADDRESS'` | + +Where addresses are per-deployment (operator address after deploy), link to the deploy guide: "Your operator address comes from `deployMarketplaceOperator()` — see [Deploy an Operator](/sdk/guides/deploy-operator)." + +### 4. Acknowledge the non-obvious + +The things that trip developers up are never the things the code is explicit about. Call them out: + +- `client.escrow` is `undefined` if no `escrowPeriodAddress` in config +- `refundInEscrow()` is gated by a StaticAddressCondition on the operator +- Watch callbacks currently receive `unknown` (typed events coming later) +- `eventFromBlock` must be set to enable event-based payment lookups +- Role presets require `walletClient` — omit it and you get a `ValidationError` + +### 5. Vary structure page by page + +| Page type | Feel | Structure | +|-----------|------|-----------| +| Overview | Landing page, orientation | Cards, quick taxonomy, "go here based on your role" | +| Installation | Shortest page on the site | One install command, one code block, done | +| Create-client | Decision tree | "Use X when Y", opinionated guidance | +| TypeScript | Reference sheet | Tables, scannable, Ctrl+F friendly | +| Guides | Story with beginning/middle/end | Setup -> Action -> Verification | +| API reference | Lookup table | Uniform format (intentionally) | + +If more than half the pages in a batch start with the same structural pattern, rewrite the outliers. + +### 6. Be opinionated + +Save the reader from making bad decisions: + +**Good:** "For most applications, use `createMerchantClient()`. Use `createX402r()` only when your app acts as multiple roles or needs read-only access without a wallet." + +**Bad:** "You can use either `createX402r()` or `createMerchantClient()` depending on your needs." + +The first version makes a recommendation. The second punts. + +### 7. Be honest about limitations + +"Event watchers currently return untyped logs (`unknown`). Cast them until typed events ship in a future release." + +Honesty builds trust. Pretending the API is polished when it's v0.0.2 does not. + +--- + +## Anti-Slop Word List + +Search and destroy these patterns in every draft: + +| Kill | Replace with | +|------|-------------| +| "allows you to" / "enables you to" / "provides" | Direct statement: "Release transfers escrowed funds to the receiver." | +| "powerful" / "robust" / "seamless" / "comprehensive" | Delete entirely | +| "leverage" / "empower" / "unlock" / "revolutionize" | Delete entirely | +| `!` (exclamation marks) | Period | +| "In this guide" / "In this section" | Delete — reader knows where they are | +| "Let's" / "We'll" / "We" / "Our" | Imperative: "Create a client" not "Let's create a client" | +| `0x...` / `YOUR_KEY` / `` | Real address or link to where you get it | +| "makes it easy" / "simple" / "straightforward" | Show the easy thing. A 3-line example proves simplicity. | +| "cutting-edge" / "state-of-the-art" / "next-generation" | Delete | +| "Amazing" / "Great" / "Exciting" | Delete | + +### Sentence-Level Rules + +- Vary sentence length. Mix 5-word sentences with 20-word sentences. Uniform length = machine writing. +- Every paragraph must add information the previous paragraph did not. If two paragraphs merge without info loss, merge them. +- Comments in code blocks explain the "why," not the "what." Skip comments on self-evident lines. +- Second person, imperative mood. "Create a client" not "Creating a client." +- 2-3 sentences per paragraph max. + +--- + +## Style Exemplar + +The best existing page is `docs/x402-integration/overview.mdx`. Key patterns to replicate: + +1. **Leads with contrast:** "The exact scheme works well for immediate-delivery... but creates friction for" — then lists concrete scenarios +2. **Uses dollar amounts:** "$500 → Server crashes → Money lost" — not abstract descriptions +3. **Problem/Solution pairs:** Each use case states the problem first, then shows escrow solving it +4. **Concrete scenarios:** "LLM agent making API calls", "GPU cluster for training job", "real-time data feed" — specific enough to imagine +5. **Mermaid diagrams:** Sequence diagrams for the payment flow — visual, not prose +6. **Minimal jargon introduction:** Defines terms inline with one-sentence definitions, not a glossary page From 940b04a517f1651882f6f7486f2e799a54e2689b Mon Sep 17 00:00:00 2001 From: vraspar Date: Sun, 17 May 2026 16:10:30 -0700 Subject: [PATCH 04/13] docs: drop duplicated skill copy; lives at workspace level instead The writing-docs skill now lives at the monorepo workspace .claude/ skills/ so it auto-loads from any cwd. Keeping a second copy here just creates a sync burden. CLAUDE.md updated to point at "the writing-docs skill" by name rather than by path. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/writing-docs/SKILL.md | 92 -------- .../writing-docs/references/page-briefs.md | 212 ------------------ .../writing-docs/references/quality-gates.md | 48 ---- .../writing-docs/references/source-files.md | 77 ------- .../writing-docs/references/style-guide.md | 116 ---------- CLAUDE.md | 2 +- 6 files changed, 1 insertion(+), 546 deletions(-) delete mode 100644 .claude/skills/writing-docs/SKILL.md delete mode 100644 .claude/skills/writing-docs/references/page-briefs.md delete mode 100644 .claude/skills/writing-docs/references/quality-gates.md delete mode 100644 .claude/skills/writing-docs/references/source-files.md delete mode 100644 .claude/skills/writing-docs/references/style-guide.md diff --git a/.claude/skills/writing-docs/SKILL.md b/.claude/skills/writing-docs/SKILL.md deleted file mode 100644 index 977a5ef..0000000 --- a/.claude/skills/writing-docs/SKILL.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -name: writing-docs -description: Use when writing, updating, or editing pages in the x402r Mintlify docs (docs/**/*.mdx) — including new feature documentation, guide updates, API reference pages, or any content authored for docs.x402r.org. Loads the x402r writing style, anti-slop rules, per-page creative briefs, quality gates, and source-file map. Walks through clarify → load references → match brief → read source → read neighbors → outline → draft → quality gates → vale → preview. ---- - -# writing-docs - -Workflow for writing x402r docs that match the existing voice, structure, and technical accuracy bar. - -## When this applies - -- Writing a new docs page in `docs/**/*.mdx` -- Updating an existing docs page significantly (not just typo fixes) -- Documenting a newly shipped SDK feature -- Drafting reference content for `@x402r/*` packages - -For tiny edits (one-line fix, typo, link update), skip the full workflow. - -## Workflow - -Create a TodoWrite task per step. - -### 1. Clarify - -Confirm: -- What's being documented (specific feature, API, concept) -- Page type (overview, installation, tutorial, guide, API reference, configuration) -- Audience (first-time reader, integrator, reference seeker) - -If anything is ambiguous, ask the user before proceeding. - -### 2. Load references - -Read `references/style-guide.md` and `references/page-briefs.md`. - -### 3. Match the page to a Creative Brief - -Check `references/page-briefs.md` for a matching brief. If one exists, follow its Angle, Element table, and Omit list verbatim. - -If no brief matches, pick a page type using Diataxis (reference / how-to / tutorial / explanation) and tell the user — they may want to add a brief. - -### 4. Read source - -From `references/source-files.md`, identify the source files relevant to this page. Read them. Never document an API without reading its implementation. Verify role availability from the table in `source-files.md`. - -### 5. Read neighbors - -Read 2–3 existing docs pages in the same `docs/` section. Match heading depth, intro pattern, code-block conventions, Mintlify component usage, and frontmatter shape. - -### 6. Propose outline - -For new pages or significant rewrites, propose the section structure and Diataxis framing before drafting. Wait for user approval. - -For minor additions to existing pages, skip outline approval. - -### 7. Draft - -Apply `references/style-guide.md` while writing. Hard rules: - -- Second person only — no "we", "our", "let's" (enforced by Vale via `FirstPersonPlural`) -- Real addresses from `x402r-sdk/packages/core/src/config/index.ts` — never `0xYourAddress` or `` -- Verify every export, function name, and file path exists before referencing -- Lead with WHY, code first, prose second (see `style-guide.md` § Seven Principles) -- No em dashes (`—`) — use commas, periods, or rewrite - -### 8. Quality gates - -Run through `references/quality-gates.md` (7 gates): -- Cover the code (prose-only readthrough makes sense?) -- Cover the prose (code-only readthrough makes sense?) -- Grep for slop (zero matches on kill-list) -- Unique info per paragraph -- Technical accuracy (every signature, import, address) -- Structural variety vs neighbor pages -- Mintlify check (broken links, frontmatter) - -### 9. Vale - -Run `cd docs && vale `. Fix every error and warning unless justified in writing as a false positive. - -The PostToolUse hook in `docs/.claude/settings.json` runs Vale automatically on `.mdx` edits — fix the violations it surfaces in the next turn. - -### 10. Preview - -Run `npx mint dev` from `docs/`. Visually confirm the page renders correctly before declaring done. - -## References - -- `references/style-guide.md` — 7 principles, anti-slop list, sentence rules, style exemplar -- `references/page-briefs.md` — per-page creative briefs + drafting prompt structure -- `references/quality-gates.md` — 7-gate pre-merge checklist + anti-slop verification pass -- `references/source-files.md` — critical source files map + role availability table diff --git a/.claude/skills/writing-docs/references/page-briefs.md b/.claude/skills/writing-docs/references/page-briefs.md deleted file mode 100644 index 008b7f6..0000000 --- a/.claude/skills/writing-docs/references/page-briefs.md +++ /dev/null @@ -1,212 +0,0 @@ -# Page Creative Briefs - -WHAT to write for each x402r SDK doc page. Per-page angle, element guidance, omit list, and source files. - -For pages not listed here, pick a Diataxis type (reference / how-to / tutorial / explanation) and propose adding a brief. - ---- - -## Variation Injection - -Add a page-specific angle to prevent uniformity: - -| Page type | Angle instruction | -|-----------|-------------------| -| Overview | "Write like a landing page. Reader is deciding whether to invest time." | -| Installation | "Shortest possible useful page. Zero to running in 60 seconds of reading." | -| Create-client | "Write like a decision tree. Reader picks between `createX402r()` and role presets." | -| TypeScript | "Write as a reference sheet. Someone will Ctrl+F here. Prioritize scannability." | -| Guides | "Write like a story: setup, action, verification. Reader copies and runs." | -| API reference | "Write as a lookup table. Developers arrive from other pages to check a method." | - ---- - -## Drafting prompt structure - -When drafting a page, assemble these five inputs (mentally — they shape the draft): - -**Input 1 — Source files** (not just types — implementation too, for edge cases) -Read the relevant `.ts` files from `source-files.md`. - -**Input 2 — Working example** from `x402r-sdk/examples/` -Find and read the relevant example file. - -**Input 3 — Creative brief** -Pull the brief for this page from the sections below. - -**Input 4 — Style exemplar** -Always re-read `docs/x402-integration/overview.mdx`. It leads with contrast ("exact scheme works well for immediate-delivery... but creates friction for"), uses dollar amounts, shows concrete scenarios. - -**Input 5 — Positive writing rules** (from `style-guide.md`) -1. Open with a sentence that answers "why would I read this page?" -2. Show code before explaining it. -3. Use real addresses. -4. Explain non-obvious defaults and constraints; skip the obvious. -5. State tradeoffs when there are two ways. -6. Acknowledge limitations. -7. Vary sentence length. -8. Never use slop list words. No exclamation marks. -9. Every paragraph adds new information. -10. Code comments explain "why," not "what." - ---- - -## Getting Started (4 pages) - -### `sdk/overview.mdx` — "Am I in the Right Place?" - -**Angle:** Landing page. Reader decides whether to invest time. - -| Element | Guidance | -|---------|----------| -| Opening hook | Lead with x402 vs x402r delta (what x402r *adds*), not a definition | -| Packages | 3 cards: `@x402r/sdk` (most users), `@x402r/core` (low-level), `@x402r/helpers` (x402 server). Link each to its next page | -| Action groups | Table of 8 groups, one-line descriptions, link to API ref | -| Role presets | Brief: "TypeScript autocomplete only shows methods your role can call." | -| Chains | Real table from `x402rChains` — all chains with USDC addresses | -| Honest status | Warning: testnet-tested, mainnet deployed but less battle-tested | -| **Omit** | Don't explain escrow (link to Protocol tab). Don't explain viem. | - -**Source files:** `client.ts`, `types.ts` (X402r interface), `config/index.ts` - -### `sdk/installation.mdx` — "60 Seconds to Running" - -**Angle:** Shortest page on the site. Zero to running in under a minute. - -| Element | Guidance | -|---------|----------| -| Opening | No intro paragraph. Jump straight to install command | -| Primary install | `npm install @x402r/sdk viem` (CodeGroup: npm/pnpm/bun). One path. | -| Secondary | Accordion for `@x402r/core` and `@x402r/helpers` (for those who know they need them) | -| Viem clients | `createPublicClient` + `createWalletClient` with `baseSepolia`. Reference viem docs. | -| Chain config | `getChainConfig(84532)` showing return shape | -| **Omit** | No `.env` file ceremony. No lengthy private key warnings (guide territory). | - -**Source files:** `config/index.ts` (for `getChainConfig` return type) - -### `sdk/create-client.mdx` — "Choose Your Path" - -**Angle:** Decision tree. Reader picks between full client and role presets. - -| Element | Guidance | -|---------|----------| -| Opening | Frame as a decision, not a catalog | -| Decision guidance | When to use `createX402r()` (multi-role, read-only, admin) vs presets (most apps). Be opinionated. | -| Full client | Real config with Base Sepolia addresses. Config table — only non-obvious fields get prose. | -| Role presets | 3 concise examples. Highlight narrowing: "exposes `refund.request()` but hides `payment.authorize()` — payers don't authorize" | -| `canExecute` | One-liner. "Checks condition slots before submitting. Saves gas." | -| `createMemoryStore` | One-liner. "In-memory store for dev. Data lost on exit." | -| `.extend()` | Brief example, link to extend-plugins guide | -| **Omit** | Don't enumerate every method per role (that's typescript.mdx). Don't repeat viem setup. | - -**Source files:** `client.ts`, `presets.ts`, `types.ts`, `can-execute.ts` - -### `sdk/typescript.mdx` — "Reference Sheet" - -**Angle:** Scannable reference. People Ctrl+F here for type names. - -| Element | Guidance | -|---------|----------| -| Opening | One line: "TypeScript 5.0+ with `strict: true`." | -| Type imports | Key types from `@x402r/sdk` and `@x402r/core` | -| Role narrowing | Tables per preset: which groups, which methods. From `types.ts` Pick types (lines 235-379) | -| Conditional groups | Why `escrow`/`refund`/`evidence`/`freeze`/`query` can be `undefined`. Null-check pattern. | -| Error types | `ValidationError`, `ConfigError`, `ContractCallError` — when each is thrown | -| **Omit** | Don't repeat config table from create-client. Don't list full method signatures. | - -**Source files:** `types.ts` (lines 235-379 for Pick types), `presets.ts` - ---- - -## Guides (6 pages) - -### `sdk/guides/deploy-operator.mdx` — "Get Your Operator Running" - -**Hook:** "Every x402r payment flows through a PaymentOperator. Deploy one before you can authorize, release, or refund." - -**Format:** Recipe. Prerequisites -> `deployMarketplaceOperator()` with real options -> What gets deployed -> Verify on basescan -> Connect result to SDK config. - -**Source:** `deploy/presets.ts`, `examples/shared/anvil-setup.ts` - -### `sdk/guides/accept-payments.mdx` — "Merchant Payment Lifecycle" - -**Hook:** "A payment arrives when the payer's authorization hits your operator. After escrow, release the funds. If disputed, handle the refund." - -**Arc:** Payment arrives (check state) -> Wait for escrow -> Release -> Handle disputes -> Post-escrow refund. Key insight: `payment.authorize()` is called by facilitator, not merchant. Merchant's first interaction is checking state. - -**Source:** `examples/merchant/release-escrow.ts`, `examples/scenarios/happy-path-release.ts` - -### `sdk/guides/request-refund.mdx` — "Payer Dispute Flow" - -**Hook:** "Paid for something that didn't arrive? Request a refund during escrow. If ignored, freeze and submit evidence." - -**Flow:** Request -> Check status -> Cancel (optional) -> Freeze -> Submit evidence -> Wait for arbiter. - -**Source:** `examples/payer/request-refund.ts`, `examples/payer/freeze-payment.ts` - -### `sdk/guides/resolve-disputes.mdx` — "Arbiter Decision Guide" - -**Hook:** "Review refund requests and decide: approve, deny, or decline to rule." - -**Flow:** List pending -> Review evidence -> Decide -> Unfreeze -> Distribute fees. Key insight: `refundInEscrow()` triggers RefundRequest recorder auto-approve — no separate `approve()` call. - -**Source:** `examples/arbiter/approve-refund.ts`, `examples/scenarios/dispute-resolution.ts` - -### `sdk/guides/watch-events.mdx` — "Real-Time Events" - -Short page. 4 watchers, unsubscribe pattern, transport note, type caveat about `unknown`. - -**Source:** `types.ts` (WatchActions) - -### `sdk/guides/extend-plugins.mdx` — "Custom Plugins" - -Short page. `.extend()` pattern, when to use, built-in plugin factories. - -**Source:** `client.ts` (buildExtend function) - ---- - -## API Reference (8 pages) - -Consistent format per method (uniform is intentional for lookup material): - -``` -### methodName - -[One sentence: what it does and when you use it] - -\`\`\`typescript -const result = await client.group.methodName(param1, param2) -\`\`\` - -| Parameter | Type | Description | -|-----------|------|-------------| -| param1 | `Type` | [what it means, not what it is] | - -**Returns:** `Type` — [what the return value represents] -**Role availability:** Payer / Merchant / Arbiter -**Errors:** [common error cases] -``` - -| Page | Key notes | -|------|-----------| -| `sdk/api/payment.mdx` (9 methods) | Open with `authorize` vs `charge` difference. `getState` returns `[boolean, bigint, bigint]` — name them. | -| `sdk/api/escrow.mdx` (3 methods) | All read-only. Brief page. | -| `sdk/api/refund.mdx` (15 methods) | Split: Write ops / Read ops. Document `RefundRequestStatus` enum. | -| `sdk/api/evidence.mdx` (4 methods) | IPFS CID convention. `submit` is write, rest are read. | -| `sdk/api/freeze.mdx` (3 methods) | Payer freezes, arbiter unfreezes. | -| `sdk/api/query.mdx` (3 methods) | Explain resolver priority: store -> recorder -> events. | -| `sdk/api/operator.mdx` (8 methods) | `getConfig()` returns full slot layout. `distributeFees` is write. | -| `sdk/api/watch.mdx` (4 methods) | Unsubscribe pattern. `unknown` type note. | - ---- - -## Configuration + Integrations (5 pages) - -| Page | Content | -|------|---------| -| `sdk/config/chains.mdx` | Full chain table. All lookup functions. "Identical addresses via CREATE3. Only USDC differs." | -| `sdk/config/addresses.mdx` | Protocol, factory, singleton address tables. CREATE3 explanation. | -| `sdk/integrations/x402-server.mdx` | `forwardToArbiter()` from `@x402r/helpers`. Express + Hono examples. `@x402r/evm` peer dep. | -| `sdk/integrations/facilitator.mdx` | Minimal. Port from existing. | -| `sdk/integrations/examples.mdx` | CardGroup linking to GitHub example directories. | diff --git a/.claude/skills/writing-docs/references/quality-gates.md b/.claude/skills/writing-docs/references/quality-gates.md deleted file mode 100644 index b2df523..0000000 --- a/.claude/skills/writing-docs/references/quality-gates.md +++ /dev/null @@ -1,48 +0,0 @@ -# Quality Gates - -HOW to verify a docs page before declaring done. Pre-merge checklist (7 gates) and anti-slop verification pass. - ---- - -## Pre-Merge Checklist - -| # | Gate | How to test | -|---|------|-------------| -| 1 | **Cover the code** | Read only prose (ignore code blocks). Does it tell a coherent story? | -| 2 | **Cover the prose** | Read only code blocks. Can you follow the flow? If yes, prose may be redundant. | -| 3 | **Grep for slop** | Search for kill-list words. Zero matches required. | -| 4 | **Unique info** | Annotate each paragraph: what NEW info does it add? No annotation = filler = delete. | -| 5 | **Technical accuracy** | Every signature matches `types.ts`. Every import resolves. Every address matches `config/index.ts`. | -| 6 | **Structural variety** | Compare first paragraphs across same-batch pages. >50% same pattern = rewrite outliers. | -| 7 | **Mintlify check** | `npx mintlify broken-links` passes. Preview deploy loads. Frontmatter has `title` + `description`. | - ---- - -## Anti-Slop Verification Pass - -After generating a page, run this review checklist: - -1. Does the opening sentence *motivate* (not describe)? -2. Any sentences true of *any* SDK? (Generic = slop. Delete.) -3. Placeholder values that could be real addresses? -4. Consecutive paragraphs mergeable without info loss? -5. Every code block shows something the reader will actually type or run? -6. Any "allows you to" / "enables" / "provides" / "makes it easy"? -7. Any sentence a developer would skip because it states the obvious? - -For each issue, suggest a concrete replacement. Then rewrite. - ---- - -## Vale enforcement - -Vale runs automatically via the PostToolUse hook in `docs/.claude/settings.json` on any `.mdx` Edit/Write. It catches: - -- `Slop.yml` — filler/marketing language ("seamless", "leverage", "unlock the", etc.) -- `Enthusiasm.yml` — enthusiasm markers ("Amazing", "Exciting", etc.) -- `FirstPersonPlural.yml` — "we", "our", "let's" -- `Microsoft`, `write-good`, `proselint` — standard prose-quality packs - -Run manually: `cd docs && vale path/to/file.mdx` - -Fix every error and warning before claiming done. If a warning is a false positive, justify it in writing (do not silently ignore). diff --git a/.claude/skills/writing-docs/references/source-files.md b/.claude/skills/writing-docs/references/source-files.md deleted file mode 100644 index fce637c..0000000 --- a/.claude/skills/writing-docs/references/source-files.md +++ /dev/null @@ -1,77 +0,0 @@ -# Source Files & Role Availability - -WHERE to find the source code that backs each docs page, and which roles can call each method. - ---- - -## Critical Source Files - -| Purpose | Path | -|---------|------| -| Action group interfaces + role Pick types | `x402r-sdk/packages/sdk/src/types.ts` | -| Client factory + extend pattern | `x402r-sdk/packages/sdk/src/client.ts` | -| Role presets + validation | `x402r-sdk/packages/sdk/src/presets.ts` | -| Chain config + all addresses | `x402r-sdk/packages/core/src/config/index.ts` | -| Deploy presets | `x402r-sdk/packages/core/src/deploy/presets.ts` | -| Working scenarios | `x402r-sdk/examples/scenarios/` | -| Style exemplar | `docs/x402-integration/overview.mdx` | - -Always read the source before documenting an API. Verify file paths still exist — the SDK reorganizes occasionally. - ---- - -## Role Availability Reference - -From `types.ts` Pick types (lines 235-379). **Verify against source before publishing** — methods get added and removed. - -| Method | Payer | Merchant | Arbiter | -|--------|:-----:|:--------:|:-------:| -| `payment.authorize` | | Y | | -| `payment.charge` | | Y | | -| `payment.release` | | Y | | -| `payment.refundInEscrow` | | Y | Y | -| `payment.refundPostEscrow` | | Y | | -| `payment.approvePostEscrowRefund` | | Y | | -| `payment.getPostEscrowRefundAllowance` | | Y | | -| `payment.getState` | Y | Y | Y | -| `payment.getAmounts` | Y | Y | Y | -| `refund.request` | Y | | | -| `refund.cancel` | Y | | | -| `refund.deny` | | | Y | -| `refund.refuse` | | | Y | -| `refund.get` | Y | Y | Y | -| `refund.getByKey` | Y | Y | Y | -| `refund.getStatus` | Y | Y | Y | -| `refund.has` | Y | Y | Y | -| `refund.getStoredPaymentInfo` | Y | Y | Y | -| `refund.getPayerRequests` | Y | | | -| `refund.getReceiverRequests` | | Y | | -| `refund.getOperatorRequests` | | | Y | -| `refund.getCancelCount` | Y | Y | Y | -| `refund.getCancelledAmount` | Y | Y | Y | -| `freeze.freeze` | Y | | | -| `freeze.unfreeze` | | | Y | -| `freeze.isFrozen` | Y | Y | Y | -| `evidence.*` (all) | Y | Y | Y | -| `escrow.*` (all) | Y | Y | Y | -| `query.*` (all) | Y | Y | Y | -| `operator.getConfig` | Y | Y | Y | -| `operator.getFeeAddresses` | Y | Y | Y | -| `operator.calculateFees` | | Y | | -| `operator.calculateOperatorFeeBps` | | Y | | -| `operator.calculateProtocolFeeBps` | | Y | | -| `operator.getAuthorizedFees` | | Y | | -| `operator.getAccumulatedProtocolFees` | | Y | Y | -| `operator.distributeFees` | | Y | Y | -| `watch.*` (all) | Y | Y | Y | - ---- - -## Protocol facts - -x402r-specific accuracy notes that don't live in the code: - -- Payer signs the authorization; merchant submits. Never say "merchant authorizes." -- `viem` and `@x402r/core` are regular deps of `@x402r/sdk`, not peer deps. Don't tell users to install them separately unless importing directly from `@x402r/core`. -- `@x402r/helpers` is merchant-server only — client-side utilities go in `@x402r/sdk`. -- Verify function signatures against `types.ts` and `presets.ts` before documenting. diff --git a/.claude/skills/writing-docs/references/style-guide.md b/.claude/skills/writing-docs/references/style-guide.md deleted file mode 100644 index da8a62e..0000000 --- a/.claude/skills/writing-docs/references/style-guide.md +++ /dev/null @@ -1,116 +0,0 @@ -# Style Guide - -Rules for HOW to write x402r docs. The Diagnostic Test, seven principles, anti-slop word list, sentence-level rules, and the style exemplar. - ---- - -## The Diagnostic Test - -Every paragraph must pass: **"Would a developer who already has the source code learn something from this sentence that the code alone does not tell them?"** If the answer is no, delete the paragraph. - ---- - -## Seven Principles - -### 1. Lead with WHY, not WHAT - -**Bad:** "The x402r SDK provides TypeScript bindings for the x402r refundable payments protocol." - -**Good:** "x402 payments are instant and irreversible. x402r adds escrow holds, refund windows, and dispute resolution on top." - -The bad version describes the page topic. The good version tells you why you need it by establishing contrast. Open every page by answering the question the reader actually has: "Should I keep reading?" - -### 2. Code first, prose second - -Show the code block, then annotate only what the code does NOT make obvious. If `getChainConfig(84532)` is self-explanatory, don't add "This retrieves the chain configuration for Base Sepolia." Instead note something useful: "USDC is the only address that differs between chains — everything else is identical via CREATE3." - -### 3. Real values, never placeholders - -Use actual addresses from `x402r-sdk/packages/core/src/config/index.ts`: - -| Use this | Not this | -|----------|----------| -| `0x3Cd5c76Fefe46CB07788Ee8f80B93B20D81941D4` | `'0xYourOperator...'` | -| `84532` | `` | -| `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | `'YOUR_USDC_ADDRESS'` | - -Where addresses are per-deployment (operator address after deploy), link to the deploy guide: "Your operator address comes from `deployMarketplaceOperator()` — see [Deploy an Operator](/sdk/guides/deploy-operator)." - -### 4. Acknowledge the non-obvious - -The things that trip developers up are never the things the code is explicit about. Call them out: - -- `client.escrow` is `undefined` if no `escrowPeriodAddress` in config -- `refundInEscrow()` is gated by a StaticAddressCondition on the operator -- Watch callbacks currently receive `unknown` (typed events coming later) -- `eventFromBlock` must be set to enable event-based payment lookups -- Role presets require `walletClient` — omit it and you get a `ValidationError` - -### 5. Vary structure page by page - -| Page type | Feel | Structure | -|-----------|------|-----------| -| Overview | Landing page, orientation | Cards, quick taxonomy, "go here based on your role" | -| Installation | Shortest page on the site | One install command, one code block, done | -| Create-client | Decision tree | "Use X when Y", opinionated guidance | -| TypeScript | Reference sheet | Tables, scannable, Ctrl+F friendly | -| Guides | Story with beginning/middle/end | Setup -> Action -> Verification | -| API reference | Lookup table | Uniform format (intentionally) | - -If more than half the pages in a batch start with the same structural pattern, rewrite the outliers. - -### 6. Be opinionated - -Save the reader from making bad decisions: - -**Good:** "For most applications, use `createMerchantClient()`. Use `createX402r()` only when your app acts as multiple roles or needs read-only access without a wallet." - -**Bad:** "You can use either `createX402r()` or `createMerchantClient()` depending on your needs." - -The first version makes a recommendation. The second punts. - -### 7. Be honest about limitations - -"Event watchers currently return untyped logs (`unknown`). Cast them until typed events ship in a future release." - -Honesty builds trust. Pretending the API is polished when it's v0.0.2 does not. - ---- - -## Anti-Slop Word List - -Search and destroy these patterns in every draft: - -| Kill | Replace with | -|------|-------------| -| "allows you to" / "enables you to" / "provides" | Direct statement: "Release transfers escrowed funds to the receiver." | -| "powerful" / "robust" / "seamless" / "comprehensive" | Delete entirely | -| "leverage" / "empower" / "unlock" / "revolutionize" | Delete entirely | -| `!` (exclamation marks) | Period | -| "In this guide" / "In this section" | Delete — reader knows where they are | -| "Let's" / "We'll" / "We" / "Our" | Imperative: "Create a client" not "Let's create a client" | -| `0x...` / `YOUR_KEY` / `` | Real address or link to where you get it | -| "makes it easy" / "simple" / "straightforward" | Show the easy thing. A 3-line example proves simplicity. | -| "cutting-edge" / "state-of-the-art" / "next-generation" | Delete | -| "Amazing" / "Great" / "Exciting" | Delete | - -### Sentence-Level Rules - -- Vary sentence length. Mix 5-word sentences with 20-word sentences. Uniform length = machine writing. -- Every paragraph must add information the previous paragraph did not. If two paragraphs merge without info loss, merge them. -- Comments in code blocks explain the "why," not the "what." Skip comments on self-evident lines. -- Second person, imperative mood. "Create a client" not "Creating a client." -- 2-3 sentences per paragraph max. - ---- - -## Style Exemplar - -The best existing page is `docs/x402-integration/overview.mdx`. Key patterns to replicate: - -1. **Leads with contrast:** "The exact scheme works well for immediate-delivery... but creates friction for" — then lists concrete scenarios -2. **Uses dollar amounts:** "$500 → Server crashes → Money lost" — not abstract descriptions -3. **Problem/Solution pairs:** Each use case states the problem first, then shows escrow solving it -4. **Concrete scenarios:** "LLM agent making API calls", "GPU cluster for training job", "real-time data feed" — specific enough to imagine -5. **Mermaid diagrams:** Sequence diagrams for the payment flow — visual, not prose -6. **Minimal jargon introduction:** Defines terms inline with one-sentence definitions, not a glossary page diff --git a/CLAUDE.md b/CLAUDE.md index fbce74c..16c5cf2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,4 +27,4 @@ npx mint dev # Preview at localhost:3000 Style rules (anti-slop, voice, structure) live in: - `.vale.ini` + `styles/x402r/` — deterministic enforcement (runs in CI and via PostToolUse hook on `.mdx` edits) -- `.claude/skills/writing-docs/references/style-guide.md` — full prose style guide (loaded by the skill) +- The `writing-docs` skill (installed at the monorepo workspace level) — full prose style guide, per-page briefs, quality gates, source-files map From 15ea6ba1a87513772a12ffd1c803022e41f210cd Mon Sep 17 00:00:00 2001 From: vraspar Date: Sun, 17 May 2026 16:34:20 -0700 Subject: [PATCH 05/13] docs: drop unused hook config; hook moved to workspace settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subdirectory .claude/settings.json files aren't documented to load in Claude Code (only workspace root and ~/.claude/settings.json). The Vale hook now lives at workspace .claude/settings.json with an 'if' filter scoping it to docs/**/*.mdx — same effect, actually supported. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/settings.json | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .claude/settings.json diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index d4f1813..0000000 --- a/.claude/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "hooks": { - "PostToolUse": [ - { - "matcher": "Edit|Write|MultiEdit", - "hooks": [ - { - "type": "command", - "command": "f=$(jq -r '.tool_input.file_path // empty' 2>/dev/null); case \"$f\" in *.mdx) command -v vale >/dev/null 2>&1 && (cd \"$CLAUDE_PROJECT_DIR/docs\" && vale \"$f\" 2>&1) || true ;; esac" - } - ] - } - ] - } -} From 3f0580da819c03bc8d139596a89747c344ec4208 Mon Sep 17 00:00:00 2001 From: vraspar Date: Sun, 17 May 2026 23:03:08 -0700 Subject: [PATCH 06/13] docs: drop FirstPersonPlural rule, Microsoft.We covers it Custom Vale rule duplicated the Microsoft style pack's `Microsoft.We` rule, which catches the same we/our/let's surface. Keeping both produces double warnings on every match. Stock pack wins. Co-Authored-By: Claude Opus 4.7 (1M context) --- styles/x402r/FirstPersonPlural.yml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 styles/x402r/FirstPersonPlural.yml diff --git a/styles/x402r/FirstPersonPlural.yml b/styles/x402r/FirstPersonPlural.yml deleted file mode 100644 index 088e50d..0000000 --- a/styles/x402r/FirstPersonPlural.yml +++ /dev/null @@ -1,10 +0,0 @@ -extends: existence -message: "Use second person — avoid first-person plural: '%s'" -level: warning -ignorecase: true -tokens: - - '\bwe\b' - - '\bour\b' - - '\bours\b' - - '\bourselves\b' - - '\blet''s\b' From 820d004aad1005d502d8dafd274845d46102a0d5 Mon Sep 17 00:00:00 2001 From: vraspar Date: Sun, 17 May 2026 23:49:51 -0700 Subject: [PATCH 07/13] docs: add Vale vocab + gitignore synced style packs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vale's default spell-check flags every project-specific term as unknown (config, Sepolia, Hono, middleware, USDC, x402r, etc). Adding a Vocab file at styles/config/vocabularies/x402r/accept.txt silences the false positives without disabling the rule entirely. Also gitignore the Microsoft/proselint/write-good style packs — those are fetched via `vale sync` per machine and shouldn't live in the repo. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 4 ++ .vale.ini | 1 + styles/config/vocabularies/x402r/accept.txt | 53 +++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 .gitignore create mode 100644 styles/config/vocabularies/x402r/accept.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bb209d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Vale-synced style packs — fetch with `vale sync` +styles/Microsoft/ +styles/proselint/ +styles/write-good/ diff --git a/.vale.ini b/.vale.ini index 451469b..bf93e1f 100644 --- a/.vale.ini +++ b/.vale.ini @@ -1,5 +1,6 @@ StylesPath = styles MinAlertLevel = warning +Vocab = x402r Packages = Microsoft, write-good, proselint diff --git a/styles/config/vocabularies/x402r/accept.txt b/styles/config/vocabularies/x402r/accept.txt new file mode 100644 index 0000000..afe083b --- /dev/null +++ b/styles/config/vocabularies/x402r/accept.txt @@ -0,0 +1,53 @@ +[Ss]epolia +[Mm]iddleware +[Mm]isconfigured +RPCs? +CAIP +Hono +Permit2 +ERC3009 +ERC-3009 +Codehash +[Bb]asescan +[Bb]igint +viem +[Mm]ulticall3 +EIP-155 +EIP-712 +EIP-3009 +[Pp]aginated +[Pp]agination +[Cc]onfig +[Cc]onfigs +[Ll]ookups? +PaymentInfo +PaymentInfoHash +PaymentOperator +PaymentIndexRecorderHook +HookCombinator +AuthCaptureEscrow +RefundRequest +RefundRequestStatus +ReceiverRefundCollector +USDC +EURC +PYUSD +DAI +EOA +CREATE2 +walletClient +publicClient +operatorAddress +paymentInfo +fromBlock +eventFromBlock +paymentIndexRecorderHook +[Ee]nums? +codehash +multicall3 +async +SDK +SDKs? +[Mm]ainnet +[Pp]roxying +[Pp]roxy From c9941c9a9d7ecb78f1101bc6c06b52d2d4b55057 Mon Sep 17 00:00:00 2001 From: vraspar Date: Mon, 18 May 2026 01:38:00 -0700 Subject: [PATCH 08/13] docs: downgrade Vale.Spelling to suggestion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default error severity drowns out style/voice rules whenever a page contains chain names, identifiers, or contract names (Ethereum, Arbitrum, pluggable, combinators, ABIs, etc). Per A1igator's PR review, the noise floods the PostToolUse hook output and buries actually-useful rules (Microsoft.Dashes, Microsoft.We, etc). Follows GitLab's tiered Vale convention: error = render-breaking, warning = style guide, suggestion = preference. Spelling false positives don't break builds and don't make sense as the loudest signal. Real-typo detection lives in editor review. The existing vocab file stays — it now serves Vale.Terms (case enforcement, e.g. flags 'hono' → 'Hono', 'sdk' → 'SDKs?') which is genuinely useful and not what the downgrade addresses. Co-Authored-By: Claude Opus 4.7 (1M context) --- .vale.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.vale.ini b/.vale.ini index bf93e1f..1f6710c 100644 --- a/.vale.ini +++ b/.vale.ini @@ -17,3 +17,11 @@ BasedOnStyles = Vale, Microsoft, write-good, proselint, x402r Microsoft.Contractions = NO Microsoft.SentenceLength = suggestion Microsoft.Passive = suggestion + +# Downgrade Vale's built-in spell-check to suggestion. Technical docs trip its +# default dictionary constantly (chain names, identifiers, contract names), and +# false positives at error level drown out style/voice rules in the PostToolUse +# hook output. Per GitLab's tiered convention: error = render-breaking, warning +# = style guide, suggestion = preference. Real-typo detection lives in editor +# review, not CI gating. +Vale.Spelling = suggestion From 2823c5f7e90d9aba4c2a993790c6a871b9f91186 Mon Sep 17 00:00:00 2001 From: vraspar Date: Mon, 18 May 2026 12:25:36 -0700 Subject: [PATCH 09/13] docs: Vale.Spelling at warning, seed chain vocab from x402rChains Two-part fix for the noise/detection tradeoff per A1igator's PR review: 1. Vale.Spelling: suggestion -> warning. The earlier suggestion-tier downgrade combined with MinAlertLevel = warning silently dropped spelling from both hook output and CI, regressing typo detection to "editor review only". At warning level spelling still surfaces (catches recieve/transferd/unkown) but ranks below errors so it doesn't dominate. 2. Seed chain-name vocab from x402rChains in x402r-sdk/packages/core/src/config/index.ts. Bounded source of truth: only chains with deployed contracts get accepted (currently just Base; Sepolia was already in vocab). Regen one-liner lives in accept.txt so the next chain deploy triggers a clear vocab update. Per-the-investigation finding: the noise A1igator was seeing on docs/index.mdx (Ethereum, Arbitrum, Celo, Linea) isn't masked by this vocab seed because those chains aren't in x402rChains -- the docs/index.mdx Supported Networks table lists 11 networks while code deploys to 2. Those warnings are now correctly tagged as warnings, not errors, and serve as signal that the table is aspirational. Separate concern from this PR. Co-Authored-By: Claude Opus 4.7 (1M context) --- .vale.ini | 15 ++++++++------- styles/config/vocabularies/x402r/accept.txt | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.vale.ini b/.vale.ini index 1f6710c..cf5d82d 100644 --- a/.vale.ini +++ b/.vale.ini @@ -18,10 +18,11 @@ Microsoft.Contractions = NO Microsoft.SentenceLength = suggestion Microsoft.Passive = suggestion -# Downgrade Vale's built-in spell-check to suggestion. Technical docs trip its -# default dictionary constantly (chain names, identifiers, contract names), and -# false positives at error level drown out style/voice rules in the PostToolUse -# hook output. Per GitLab's tiered convention: error = render-breaking, warning -# = style guide, suggestion = preference. Real-typo detection lives in editor -# review, not CI gating. -Vale.Spelling = suggestion +# Downgrade Vale's built-in spell-check to warning. Default error severity +# means a few false positives dominate the PostToolUse hook output and bury +# style/voice rules. At warning level spelling still surfaces (and still +# catches real typos), but ranks below errors. Per GitLab's tiered Vale +# convention: error = render-breaking, warning = style guide, suggestion = +# preference. Suggestion would have hidden spelling entirely under our +# current MinAlertLevel = warning floor, regressing typo detection. +Vale.Spelling = warning diff --git a/styles/config/vocabularies/x402r/accept.txt b/styles/config/vocabularies/x402r/accept.txt index afe083b..e4d069b 100644 --- a/styles/config/vocabularies/x402r/accept.txt +++ b/styles/config/vocabularies/x402r/accept.txt @@ -51,3 +51,17 @@ SDKs? [Mm]ainnet [Pp]roxying [Pp]roxy + +# --- Chain names --- +# Source of truth: `x402rChains` in +# `x402r-sdk/packages/core/src/config/index.ts`. Only chains with deployed +# contracts. When chains are added/removed in core, regenerate via: +# +# awk '/export const x402rChains/,/^} as const/' \ +# ../x402r-sdk/packages/core/src/config/index.ts \ +# | grep -oE "'[A-Z][a-zA-Z ]+'" | sort -u +# +# Chain names referenced in marketing/roadmap docs but NOT in x402rChains +# (e.g. Ethereum, Polygon, Arbitrum, Optimism) will fire as Vale warnings. +# That is intentional signal — those mentions document unshipped support. +Base From f662841b0db74a99b98679fe4b1d08e72443d1fc Mon Sep 17 00:00:00 2001 From: vraspar Date: Mon, 18 May 2026 13:35:05 -0700 Subject: [PATCH 10/13] docs: restore Vale.Spelling = error, defer hook scope to filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pair with workspace commit c193202, which adds --filter='.Name != "Vale.Spelling"' to the PostToolUse hook command. Strict-in-CI / lenient-in-hook architecture per established practice (GitLab Vale docs, Vaadin .vale-pr.ini, ESLint warnings-as-anti- pattern). Spell-check at default error level in CI gates real typos before merge. The hook filters Vale.Spelling out of Claude's self-correction context so chain-name and identifier false positives don't bury Microsoft.Dashes / write-good rules. Why this beats the warning-tier compromise from 2823c5f: - Hook noise drops to ZERO instead of "less but still there". - CI signal stays at error tier where it belongs. - Single .vale.ini, no two-config sync footgun. - Native --filter, not a fragile grep post-process. A1igator's review pushed this in the right direction. Earlier compromise (Vale.Spelling = warning) reduced noise by one tier but didn't fix the architectural issue — wrong place to apply the lever. Co-Authored-By: Claude Opus 4.7 (1M context) --- .vale.ini | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.vale.ini b/.vale.ini index cf5d82d..5fb286b 100644 --- a/.vale.ini +++ b/.vale.ini @@ -18,11 +18,13 @@ Microsoft.Contractions = NO Microsoft.SentenceLength = suggestion Microsoft.Passive = suggestion -# Downgrade Vale's built-in spell-check to warning. Default error severity -# means a few false positives dominate the PostToolUse hook output and bury -# style/voice rules. At warning level spelling still surfaces (and still -# catches real typos), but ranks below errors. Per GitLab's tiered Vale -# convention: error = render-breaking, warning = style guide, suggestion = -# preference. Suggestion would have hidden spelling entirely under our -# current MinAlertLevel = warning floor, regressing typo detection. -Vale.Spelling = warning +# Vale.Spelling stays at its default (error) severity here. CI gates real +# typos before merge — the right place for strict spell-check. +# +# The PostToolUse hook in workspace .claude/settings.json filters Vale.Spelling +# out of its output via `vale --filter='.Name != "Vale.Spelling"'` so Claude's +# self-correction context isn't drowned by chain-name and identifier false +# positives. CI (errata-ai/vale-action) does not apply that filter, so PR +# review still sees the full spell-check. +# +# Strict-in-CI / lenient-in-hook pattern, per GitLab and Vaadin Vale configs. From 378ea1dafede67b4d41b89daf508c69cf613dd25 Mon Sep 17 00:00:00 2001 From: vraspar Date: Mon, 18 May 2026 16:29:24 -0700 Subject: [PATCH 11/13] docs: disable Vale.Terms, vocab is spelling-only Per A1igator's tree-wide review on f662841: accept.txt entries are dual-used by Vale as both spelling acceptance AND Vale.Terms canonical-case enforcement, the latter at error severity. That fires 47 false positives across docs, including: - SDKs? -> 'Use SDKs? instead of sdk' (regex ? leaks literally, 24x) - paymentInfo -> flags TS type PaymentInfo (10x) - Base -> flags base code identifier / chain key (8x, my seed) - [Bb]igint -> tells users to type the regex string - Hono -> flags hono npm package in import paths - PaymentInfoHash -> flags variable paymentInfoHash (2x) - async -> flags Async capitalization Vocab here was added for spelling acceptance only. Vale.Terms was an accidental side-effect, never an intended feature. Disabled. If we later want real terminology enforcement, the right path is a dedicated styles/x402r/Terms.yml (or similar) with hand-curated canonical forms, not the spelling accept-list. Co-Authored-By: Claude Opus 4.7 (1M context) --- .vale.ini | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.vale.ini b/.vale.ini index 5fb286b..a65e8ec 100644 --- a/.vale.ini +++ b/.vale.ini @@ -18,6 +18,15 @@ Microsoft.Contractions = NO Microsoft.SentenceLength = suggestion Microsoft.Passive = suggestion +# Vale.Terms reads the same accept.txt as Vale.Spelling but treats each entry +# as the canonical case form. That fires false positives whenever a vocab +# entry collides with a code identifier (e.g. PaymentInfo type vs paymentInfo +# variable, Hono package vs hono import path, Base chain vs base code +# identifier) or when the entry uses Vale's regex syntax (SDKs? becomes a +# literal canonical-form suggestion). Vocab here is for spelling acceptance +# only, not terminology enforcement — disabled. +Vale.Terms = NO + # Vale.Spelling stays at its default (error) severity here. CI gates real # typos before merge — the right place for strict spell-check. # From 1969bad6a310b4c70ba8d47571e87796700bc74f Mon Sep 17 00:00:00 2001 From: vraspar Date: Mon, 18 May 2026 17:37:49 -0700 Subject: [PATCH 12/13] docs: fix silently-dead Microsoft rules, document MDX tradeoff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the config audit comment on f662841: Microsoft.SentenceLength and Microsoft.Passive were set to `suggestion` while MinAlertLevel = warning, meaning Vale filters them out of output entirely. Same bug class as the Vale.Spelling regression caught earlier in this thread — `suggestion` looks like "downgrade" but is actually "off" under our floor. Original intent (per the rule comment header) was to silence these. Now matches that intent explicitly: - Microsoft.Contractions = NO (already was, explicit) - Microsoft.SentenceLength = NO (was: silently off via suggestion) - Microsoft.Passive = NO (was: silently off via suggestion) Why each is off: - Contractions: irrelevant for technical docs. - SentenceLength: low-ROI, no actionable threshold. - Passive: write-good.Passive already covers it with better signal. Also documented the [formats] mdx = md tradeoff per Vale's MDX docs (https://vale.sh/docs/formats/mdx): this is an extension substitution, not real MDX support. JSX components, JSX expressions, and ESM imports can leak into prose linting. Hook view (md mode) is strictly less accurate than CI view (mdx2vast). Acceptable tradeoff: zero-dep local install, with CI as the merge gate. Co-Authored-By: Claude Opus 4.7 (1M context) --- .vale.ini | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.vale.ini b/.vale.ini index a65e8ec..54d355f 100644 --- a/.vale.ini +++ b/.vale.ini @@ -7,16 +7,34 @@ Packages = Microsoft, write-good, proselint # Parse .mdx as Markdown so Vale doesn't need the separate mdx2vast binary # (used by the PostToolUse hook for local edit-time linting). The CI action # (errata-ai/vale-action) installs its own MDX parser server-side. +# +# Tradeoff per Vale's MDX docs (https://vale.sh/docs/formats/mdx): mdx = md is +# an extension-level substitution, not real MDX support. JSX components +# (, , ), JSX expressions, and ESM imports/exports +# aren't parsed out and can leak into prose linting. Hook view (md mode) is +# strictly less accurate than CI view (mdx2vast). Acceptable here because the +# hook trades local accuracy for zero-dep installation, and CI is the actual +# merge gate. [formats] mdx = md [*.mdx] BasedOnStyles = Vale, Microsoft, write-good, proselint, x402r -# Disable overly noisy Microsoft rules for docs +# Disabled Microsoft rules. +# +# = suggestion looks like "downgrade" but with MinAlertLevel = warning it +# means "filtered out entirely" — same bug class as the Vale.Spelling +# regression earlier in this thread. Using = NO to match actual intent. +# +# Contractions: irrelevant for technical docs, encourages informal voice. +# SentenceLength: low-ROI rule with no actionable threshold; "this sentence +# is long" rarely improves a draft. +# Passive: write-good.Passive (enabled below) already covers passive voice +# with better signal. Microsoft's version is duplicative. Microsoft.Contractions = NO -Microsoft.SentenceLength = suggestion -Microsoft.Passive = suggestion +Microsoft.SentenceLength = NO +Microsoft.Passive = NO # Vale.Terms reads the same accept.txt as Vale.Spelling but treats each entry # as the canonical case form. That fires false positives whenever a vocab From b6e54111bac0142915305ff6f27a5a8cb95b0c58 Mon Sep 17 00:00:00 2001 From: vraspar Date: Mon, 18 May 2026 17:47:15 -0700 Subject: [PATCH 13/13] docs: correct comment on disabled Microsoft rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous comment block on 1969bad attributed the dead state of Microsoft.SentenceLength and Microsoft.Passive to the same MinAlertLevel trap as the Vale.Spelling regression. That's wrong — both rules ship with `level: suggestion` in their YAML files (verified styles/Microsoft/*.yml), so they were already filtered by the warning floor regardless of any .vale.ini override. The old `= suggestion` lines were redundant restatements of defaults, not severity downgrades. Only Microsoft.Contractions (default `level: error`) had its behavior actually changed by `= NO` — that line suppresses ~37 hits across docs. Updated comment block to teach the actual mechanism per-rule. Also added a "footgun to watch for" note: the trap is real (a default-error rule downgraded to suggestion goes silent under warning floor), but only fires when the rule's default is error. Pure documentation fix. End-state unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- .vale.ini | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/.vale.ini b/.vale.ini index 54d355f..028b7e7 100644 --- a/.vale.ini +++ b/.vale.ini @@ -21,17 +21,26 @@ mdx = md [*.mdx] BasedOnStyles = Vale, Microsoft, write-good, proselint, x402r -# Disabled Microsoft rules. +# Disabled Microsoft rules. = NO is explicit-off; the runtime effect varies +# per rule because each ships with a different default `level:` in its YAML. # -# = suggestion looks like "downgrade" but with MinAlertLevel = warning it -# means "filtered out entirely" — same bug class as the Vale.Spelling -# regression earlier in this thread. Using = NO to match actual intent. +# Contractions: rule default = error. Without `= NO` it fires ~37x +# across docs (irrelevant for technical writing, encourages +# informal voice). This is the only real suppression here. +# SentenceLength: rule default = suggestion. Already filtered by our +# MinAlertLevel = warning floor regardless of this line. +# `= NO` makes intent explicit; behavior unchanged. +# Low-ROI rule with no actionable threshold. +# Passive: rule default = suggestion. Same story as SentenceLength. +# Also duplicative — write-good.Passive (enabled below) +# fires on identical spans, so enabling both would +# double-warn every passive sentence. # -# Contractions: irrelevant for technical docs, encourages informal voice. -# SentenceLength: low-ROI rule with no actionable threshold; "this sentence -# is long" rarely improves a draft. -# Passive: write-good.Passive (enabled below) already covers passive voice -# with better signal. Microsoft's version is duplicative. +# Footgun to watch for: a rule that ships with `level: error` and gets +# = suggestion in this file IS a real downgrade — and with our warning +# floor it becomes silent. That's how the earlier Vale.Spelling regression +# happened. Always check the rule file's default before assuming +# `= suggestion` is a downgrade rather than a redundant restatement. Microsoft.Contractions = NO Microsoft.SentenceLength = NO Microsoft.Passive = NO