From cad58f0a6da53f7fa1e38e33531d3b4fff956a97 Mon Sep 17 00:00:00 2001 From: Sam Gutentag <1404219+samgutentag@users.noreply.github.com> Date: Wed, 13 May 2026 00:03:40 -0700 Subject: [PATCH 1/4] chore(claude): scaffold .claude/ with agents, skills, drafts for docs2 Adds Claude Code configuration for the Mintlify-based docs site: - 1 subagent: doc-researcher (Sonnet 4.6, reads Linear/PRs/docs) - 6 skills: outline-docs, write-docs, review-docs, draft-docs, verify-docs-pr, docs-review - drafts/TEMPLATE.md as scaffold for new draft notes - settings.json with curated shared permissions (settings.local.json gitignored for per-machine overrides) - README.md documenting each skill, agent, and the gutils relationship Skills are ported from trunk-io/docs and ~/Developer/gutils/claude-code with Mintlify adaptations: - Filenames .md -> .mdx for content references - summary.md (Docusaurus nav) -> docs.json (Mintlify nav) - .gitbook.yaml redirect audit -> docs.json redirects audit - GitBook {% hint %} blocks -> Mintlify /// - GitBook [page](path.md "mention") -> standard MDX [page](/path) - trunk-io/docs repo refs -> trunk-io/docs2 (verify-docs-pr, write-docs/OVERLAP-CHECK.md, write-docs/README.md) - draft-docs writes directly to .claude/drafts/ (no copy step) - Removed historical verify-docs-pr/PLAN.md (executed migration plan) - Fixed duplicate Question block in outline-docs Phase 1 Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/.gitignore | 6 + .claude/README.md | 178 +++++++++++ .claude/agents/doc-researcher.md | 49 +++ .claude/drafts/TEMPLATE.md | 44 +++ .claude/settings.json | 97 ++++++ .claude/skills/docs-review/SKILL.md | 133 ++++++++ .claude/skills/draft-docs/SKILL.md | 156 ++++++++++ .claude/skills/outline-docs/SKILL.md | 345 +++++++++++++++++++++ .claude/skills/review-docs/SKILL.md | 298 ++++++++++++++++++ .claude/skills/verify-docs-pr/DESIGN.md | 230 ++++++++++++++ .claude/skills/verify-docs-pr/SKILL.md | 258 +++++++++++++++ .claude/skills/write-docs/OUTPUTS.md | 59 ++++ .claude/skills/write-docs/OVERLAP-CHECK.md | 51 +++ .claude/skills/write-docs/README.md | 278 +++++++++++++++++ .claude/skills/write-docs/SKILL.md | 150 +++++++++ .claude/tmp/.gitkeep | 0 16 files changed, 2332 insertions(+) create mode 100644 .claude/.gitignore create mode 100644 .claude/README.md create mode 100644 .claude/agents/doc-researcher.md create mode 100644 .claude/drafts/TEMPLATE.md create mode 100644 .claude/settings.json create mode 100644 .claude/skills/docs-review/SKILL.md create mode 100644 .claude/skills/draft-docs/SKILL.md create mode 100644 .claude/skills/outline-docs/SKILL.md create mode 100644 .claude/skills/review-docs/SKILL.md create mode 100644 .claude/skills/verify-docs-pr/DESIGN.md create mode 100644 .claude/skills/verify-docs-pr/SKILL.md create mode 100644 .claude/skills/write-docs/OUTPUTS.md create mode 100644 .claude/skills/write-docs/OVERLAP-CHECK.md create mode 100644 .claude/skills/write-docs/README.md create mode 100644 .claude/skills/write-docs/SKILL.md create mode 100644 .claude/tmp/.gitkeep diff --git a/.claude/.gitignore b/.claude/.gitignore new file mode 100644 index 0000000..611db70 --- /dev/null +++ b/.claude/.gitignore @@ -0,0 +1,6 @@ +# Per-machine permission overrides (each contributor curates their own) +settings.local.json + +# Scratch space written by skills (sources.md, slack.md, report.html, etc.) +tmp/* +!tmp/.gitkeep diff --git a/.claude/README.md b/.claude/README.md new file mode 100644 index 0000000..595f632 --- /dev/null +++ b/.claude/README.md @@ -0,0 +1,178 @@ +# .claude — Agents, Skills, and Drafts + +Claude Code configuration for the Trunk docs (Mintlify) repo. Skills are documentation-focused: scaffolding new pages, processing notes into PRs, reviewing changes, verifying PRs against production. + +## Structure + +``` +.claude/ +├── agents/ # Subagent definitions (spawned via the Agent tool) +│ └── doc-researcher.md # Gathers Linear/PR/Slite/docs context before writing +├── skills/ # User-invoked workflows (triggered via /skill-name) +│ ├── outline-docs/ # Scaffold a new docs page from scratch +│ ├── write-docs/ # Notes → PR pipeline (9 phases) +│ ├── review-docs/ # Pre-PR review of local changes +│ ├── draft-docs/ # Generate notes files from trunk2 context +│ ├── verify-docs-pr/ # Verify a docs PR's feature is live in prod +│ └── docs-review/ # Audit live docs.trunk.io pages +├── drafts/ # Input notes files for write-docs +│ └── TEMPLATE.md # Scaffold for new draft notes +├── tmp/ # Scratch outputs (gitignored) +├── settings.json # Shared permissions (committed) +└── settings.local.json # Per-machine overrides (gitignored) +``` + +## Agents vs. Skills + +**Agents** (`agents/`) are autonomous subprocesses spawned by the `Agent` tool. They run with a specific model and limited toolset, do their work, and return results to the parent conversation. Use agents for parallelizable research tasks. + +**Skills** (`skills/`) are user-invoked workflows triggered with `/skill-name`. They run in the main conversation with full tool access and follow a structured multi-phase pipeline. Use skills for end-to-end tasks that produce artifacts (PRs, tickets, reports). + +| Type | How to invoke | Runs where | Best for | +|---|---|---|---| +| Agent | Spawned via the `Agent` tool | Background subprocess | Research, context gathering, parallel work | +| Skill | `/skill-name` | Main conversation | Multi-step workflows with user checkpoints | + +## Skill reference + +### `/outline-docs` + +**When:** Starting a brand-new docs page from scratch with no prior spec. + +**What it does:** Asks page type (Overview / Reference / Guide), title, and save path. Generates a scaffolded `.mdx` file with sections pre-filled with 1-2 sentences plus focused `` markers. Adds the page to `docs.json` navigation. Runs `trunk fmt`. + +**Inputs:** Conversation context (title, page type, topic) — asks only for what's missing. + +**Outputs:** A new `.mdx` file ready for content, plus a post-checklist. + +--- + +### `/write-docs` + +**When:** Given a draft notes file, trunk2 PR numbers, Linear ticket IDs, a deploy tag, or Slite links — anything that says "document this feature." + +**What it does:** Full 9-phase pipeline: +1. Overlap detection (Phase 0) — refuses to proceed if another PR covers the same topic +2. Research across Linear, Slite, Slack, trunk2 PR diffs, and existing docs +3. Drafts new content or in-place edits, updating `docs.json` if adding pages +4. Creates a branch, commits, opens a **draft** PR with author tags +5. Updates Linear and writes a Slack post draft +6. Invokes `/verify-docs-pr` to check whether the feature is actually live in prod + +**Inputs:** A `.claude/drafts/.md` file (preferred), or raw PR/ticket references. + +**Outputs:** Branch, draft PR, Linear update, Slack post in `tmp//slack.md`. + +**Discipline:** One draft = one PR. Always opens as draft for human review. + +--- + +### `/review-docs` + +**When:** Docs changes are ready locally and you want a structural review before opening a PR. + +**What it does:** Five-phase review: +1. Identifies changed `.mdx` files via `git diff main...HEAD` +2. **Audits `docs.json` redirects** for stale or missing entries when files have been moved or deleted +3. Runs `trunk fmt` and `trunk check` +4. Reads sibling pages in the same product area to establish a style baseline +5. Reviews each file for repetition, structural completeness, logic errors, and Trunk style consistency + +**Inputs:** Optional file path (single-file mode), otherwise the full branch diff. + +**Outputs:** A structured report. Offers to apply mechanical fixes directly. + +--- + +### `/draft-docs` + +**When:** After a trunk2 deploy, or anytime you want to pre-populate drafts from PR / Linear / deploy-tag context. + +**What it does:** Reads trunk2 PRs, Linear tickets, and Slack/Slite context. Classifies which PRs need docs. For each, generates a `.md` notes file in `.claude/drafts/` containing: feature summary, target pages, gap analysis, code references, and "what changed" prose. + +**Inputs:** A deploy tag, `latest`, PR numbers, Linear IDs, or a freeform feature description. + +**Outputs:** One notes file per documentable feature, written directly to `.claude/drafts/` for processing by `/write-docs`. + +--- + +### `/verify-docs-pr` + +**When:** A docs PR is open and you need to confirm the feature it documents is actually shipped to customers before publishing. + +**What it does:** Classifies a PR as `live`, `staged`, `pending`, `blocked`, or `unknown` using indirect signals: linked eng PR merge state, follow-up PRs in trunk2, Slack rollout chatter, e2e flag defaults, and legacy code presence. Posts the verdict as a comment on the docs PR and the linked Linear ticket. Updates the PR title with a verdict prefix (`[ready to merge]`, `[blocked]`, etc.) and flips non-live PRs to draft. + +**Inputs:** A PR number (single mode), or no arg for a sweep across all open PRs. + +**Outputs:** PR comment, Linear comment, title prefix update, draft state. + +**Auto-invoked:** by `/write-docs` Phase 4 after PR creation. + +--- + +### `/docs-review` + +**When:** Auditing existing docs.trunk.io pages for accuracy, naming consistency, AI-readability, or running a site-wide quality pass. + +**What it does:** Reads local `.mdx` files and reviews them for: factual accuracy against the code, naming consistency, structural issues, and AI-friendliness (clear hierarchy, scannable headers, no ambiguous pronouns). Reports findings. + +**Inputs:** A page path or glob, a product area name, `full` for a site-wide pass, or no arg to ask. + +**Outputs:** A structured audit report. + +**vs. `/review-docs`:** `/review-docs` is pre-PR diff review on your local branch. `/docs-review` audits already-published pages for ongoing quality. + +--- + +## Agent reference + +### `doc-researcher` + +Subagent (not a slash command). Spawned via the `Agent` tool with `subagent_type: doc-researcher`. + +**Model:** Sonnet 4.6 (chosen for speed). + +**Tools:** `Read`, `Grep`, `Glob`, `Bash` — read-only. + +**When:** Before `/write-docs` when the scope is unclear or multiple tickets need surveying. + +**What it does:** Reads Linear tickets, linked PRs, and existing docs pages. Returns a structured research brief: feature summary, source PRs (with GitHub author handles), current docs coverage, key technical details pulled from code, and a suggested doc structure. + +**Use it in parallel** with other research (Slack, Slite searches) to save time. + +## Drafts + +`drafts/` holds input notes files that feed into `/write-docs`. Each file represents one documentable feature; processing it produces one PR. + +- **`TEMPLATE.md`** — scaffold for new drafts. Don't edit this; copy it. +- **`.md`** — one per feature. Generated either manually or by `/draft-docs`. + +Drafts are inputs, not outputs. Never modify or delete a draft mid-pipeline — it's the source of truth for what was requested. + +## Tmp + +`tmp/` is scratch space written by skills during execution: + +- `tmp//sources.md` — research audit trail for the reviewer +- `tmp//slack.md` — Slack post draft (mrkdwn format, ready to paste) +- `tmp/report.html` — cumulative HTML report from `/write-docs` runs + +Everything under `tmp/` is gitignored except `.gitkeep`. + +## Settings + +- **`settings.json`** — shared permissions baseline. Committed. Curate carefully; anything in here is auto-approved for every contributor on this repo. +- **`settings.local.json`** — per-machine overrides. Gitignored. Each contributor curates their own. + +## Source of truth + +Generic versions of these skills live in `~/Developer/gutils/claude-code/skills/trunk/` (the canonical personal-use copy). The versions in this directory are **project-tuned for Mintlify**: + +- `outline-docs` — uses `.mdx`, updates `docs.json` nav, generates Mintlify callouts +- `review-docs` — audits `docs.json` redirects, expects Mintlify callout components, no GitBook syntax +- `write-docs` — updates `docs.json` instead of `summary.md` +- `verify-docs-pr` — hardcoded to `trunk-io/docs2` +- `draft-docs` — outputs directly to `.claude/drafts/` (no copy step needed) +- `docs-review` — reviews `.mdx` files + +When updating one of these skills, decide whether the change is generic (also update gutils) or project-specific (only update here). Intentional drift between the two is fine and expected. diff --git a/.claude/agents/doc-researcher.md b/.claude/agents/doc-researcher.md new file mode 100644 index 0000000..d28f391 --- /dev/null +++ b/.claude/agents/doc-researcher.md @@ -0,0 +1,49 @@ +--- +name: doc-researcher +description: "Researches Linear tickets, PRs, and existing docs to compile context for documentation work. Use PROACTIVELY before write-docs when the scope is unclear or when multiple tickets need to be surveyed." +model: claude-sonnet-4-6 +tools: Read, Grep, Glob, Bash +--- + +You are a documentation researcher for Trunk.io. Your job is to gather +and organize all available context about a feature before docs are written. + +You are working inside the local Mintlify-powered Trunk docs repository (`trunk-io/docs2`). Pages are `.mdx` files organized by product area, with site navigation defined in `docs.json`. + +## Process +1. Look up all provided Linear ticket IDs — extract descriptions, + comments, linked PRs, related tickets +2. Explore the local filesystem to find any current docs coverage of the topic +3. For each linked trunk2 PR, note: what changed, who authored it, + any inline comments or review discussion that explains behavior +4. Check if the feature has any existing changelog entries or roadmap mentions + +## Output Format + +Produce a structured research brief: + +### Feature Summary +2-3 sentence plain-English summary of what shipped and why. + +### Source Tickets +List of Linear ticket IDs with their status and one-line description. + +### Source PRs +| PR | Author (GitHub handle) | Status | Key changes | +|----|------------------------|--------|-------------| + +### Current Docs Coverage +- What exists already (file paths + one-line summary of content) +- What's missing or stale + +### Key Technical Details +Bullet list of specific behaviors, defaults, flag names, API shapes, +edge cases — pulled directly from PR code/comments, not inferred. + +### Suggested Doc Structure +Where new/updated content should live, and what sections it needs. + +### Handoff Note for doc-writer +Any context the writer needs that isn't obvious from the tickets — +e.g., "the UI was redesigned; old screenshots in existing docs are wrong" +or "this feature is gated behind a settings toggle, document that clearly." diff --git a/.claude/drafts/TEMPLATE.md b/.claude/drafts/TEMPLATE.md new file mode 100644 index 0000000..57852d0 --- /dev/null +++ b/.claude/drafts/TEMPLATE.md @@ -0,0 +1,44 @@ +# [Feature/Change Title] + +## Type + + + +## Priority + + + +## Linear Tickets + + + +- TRUNK-XXXXX + +## What Changed + + + +## GitHub PRs + + + +## Context Links + + + +## Target Docs + + + +## Context + + diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..68856a3 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,97 @@ +{ + "permissions": { + "allow": [ + "Bash(claude mcp list:*)", + "Bash(echo:*)", + "Bash(date:*)", + "Bash(ls:*)", + "Bash(chmod:*)", + "Bash(open:*)", + "Bash(code:*)", + + "Bash(git add:*)", + "Bash(git branch:*)", + "Bash(git checkout:*)", + "Bash(git cherry-pick:*)", + "Bash(git check-ignore:*)", + "Bash(git clean:*)", + "Bash(git commit:*)", + "Bash(git config:*)", + "Bash(git diff:*)", + "Bash(git fetch:*)", + "Bash(git log:*)", + "Bash(git pull:*)", + "Bash(git push:*)", + "Bash(git rebase:*)", + "Bash(git reset:*)", + "Bash(git revert:*)", + "Bash(git rm:*)", + "Bash(git stash:*)", + "Bash(git status:*)", + "Bash(git worktree:*)", + + "Bash(gh api:*)", + "Bash(gh auth:*)", + "Bash(gh issue:*)", + "Bash(gh pr:*)", + "Bash(gh pr comment:*)", + "Bash(gh pr create:*)", + "Bash(gh pr edit:*)", + "Bash(gh pr ready:*)", + "Bash(gh pr view:*)", + "Bash(gh release:*)", + "Bash(gh repo:*)", + "Bash(gh run:*)", + "Bash(gh search:*)", + + "Bash(npm:*)", + "Bash(npx:*)", + "Bash(mint:*)", + "Bash(trunk:*)", + "Bash(curl:*)", + "Bash(jq:*)", + "Bash(xargs:*)", + "Bash(find:*)", + "Bash(grep:*)", + "Bash(awk:*)", + "Bash(python3:*)", + + "WebFetch(domain:trunk.io)", + "WebFetch(domain:docs.trunk.io)", + "WebFetch(domain:mintlify.com)", + "WebSearch", + + "mcp__claude_ai_trunk_docs__searchDocumentation", + + "mcp__claude_ai_Linear__list_teams", + "mcp__claude_ai_Linear__list_issues", + "mcp__claude_ai_Linear__list_issue_labels", + "mcp__claude_ai_Linear__list_comments", + "mcp__claude_ai_Linear__get_issue", + "mcp__claude_ai_Linear__get_project", + "mcp__claude_ai_Linear__save_issue", + "mcp__claude_ai_Linear__save_comment", + "mcp__claude_ai_Linear__create_comment", + + "mcp__claude_ai_Trunk_Slite__search-notes", + "mcp__claude_ai_Trunk_Slite__list-channels", + "mcp__claude_ai_Trunk_Slite__get-note", + "mcp__claude_ai_Trunk_Slite__get-note-children", + "mcp__claude_ai_Trunk_Slite__create-note", + "mcp__claude_ai_Trunk_Slite__append-blocks", + + "mcp__claude_ai_Slack__slack_search_public_and_private", + "mcp__claude_ai_Slack__slack_search_channels", + "mcp__claude_ai_Slack__slack_read_thread", + "mcp__claude_ai_Slack__slack_send_message", + + "Skill(write-docs)", + "Skill(outline-docs)", + "Skill(review-docs)", + "Skill(draft-docs)", + "Skill(verify-docs-pr)", + "Skill(docs-review)", + "Skill(schedule)" + ] + } +} diff --git a/.claude/skills/docs-review/SKILL.md b/.claude/skills/docs-review/SKILL.md new file mode 100644 index 0000000..2dfdd46 --- /dev/null +++ b/.claude/skills/docs-review/SKILL.md @@ -0,0 +1,133 @@ +--- +name: docs-review +description: Review and audit docs.trunk.io pages for accuracy, naming consistency, structural issues, and AI-readability. Use when auditing existing docs, reviewing docs PRs, or running a site-wide quality pass. +--- + +Review Trunk documentation for accuracy, consistency, and structure. Operates as a senior technical writer auditing docs.trunk.io. + +The audience is two groups simultaneously: human developers integrating Trunk into their workflows, and AI coding agents (Claude Code, Cursor, Copilot) that ingest documentation to surface tools autonomously. Both are first-class readers. + +## Core Beliefs + +- **Accuracy over completeness.** A shorter, correct doc beats a longer, stale one. +- **The reader is mid-task.** Lead with what they need to do, not background. +- **AI-readability is not dumbed-down.** Consistent structure, explicit vocabulary, zero ambiguity. This also makes docs better for humans. +- **Naming conventions are load-bearing.** Inconsistent names create bugs in AI reasoning and human mental models. +- **Obsolete content is actively harmful.** A doc that was accurate 18 months ago and hasn't been updated is worse than no doc. + +## Canonical Naming Conventions + +Enforce without exception. Flag any deviation. + +| Concept | Correct | Never Use | +|---|---|---| +| Product name | Trunk | trunk (lowercase in prose) | +| CLI tool | Trunk CLI | `trunk` CLI, the CLI | +| Merge Queue product | Trunk Merge Queue | merge queue (unbranded), MQ | +| Flaky test detection | Trunk Flaky Tests | flaky test detection, CI Autopilot (DEPRECATED) | +| Code quality / linter tool | Trunk Code Quality | Trunk Check, trunk check | +| Config file | `.trunk/trunk.yaml` | trunk.yaml (without path), `.trunk/config` | +| MCP server | Trunk MCP | trunk MCP server (lowercase T) | +| Docs site | docs.trunk.io | Trunk docs, the docs | +| GitHub Actions integration | Trunk's GitHub Actions | Trunk GHA | + +Product feature names are Title Case as proper nouns. Lowercase when used generically ("the merge queue held 12 PRs"). + +## Formatting Standards + +### Page Structure (every doc page must follow this order) + +1. **H1 title** -- noun phrase, not a sentence. "Merge Queue Configuration" not "How to Configure the Merge Queue" +2. **One-sentence summary** -- plain text, no bold, immediately after H1. Answers: what is this and why does it exist? +3. **Prerequisites block** (if applicable) -- bulleted, linked, before procedural content +4. **Body** -- organized by H2s describing tasks or concepts, not document structure ("## Connect Your Repo" not "## Setup Section") +5. **Troubleshooting** (if applicable) -- H2, at the bottom, before reference tables +6. **Reference tables** (if applicable) -- last section, clearly labeled + +### Writing Rules + +- Second person ("you", "your repo"). Never first person plural ("we recommend", "our system") +- Active voice only. "Trunk detects the conflict" not "the conflict is detected" +- One idea per sentence. Max 25 words per sentence in procedural steps +- Imperative mood for steps: "Run `trunk merge enable`" not "You should run..." +- No filler openers: never start a section with "In this guide", "Overview", or "Introduction" +- Avoid: leverage, utilize, seamlessly, robust, powerful, streamline, cutting-edge, delve, nuanced, ensure (use "make sure"), simply (delete it) +- Oxford comma always + +### Code Blocks + +- Every CLI command in its own fenced code block with language tag (`bash`, `yaml`, `json`, etc.) +- Realistic, non-placeholder values in examples where possible +- Unavoidable placeholders use `` format (angle brackets, screaming snake case) +- YAML examples must be complete enough to copy-paste +- Show minimum viable config first, extended options separately + +### Admonitions + +- `:::note` -- neutral extra context +- `:::tip` -- genuine shortcut or best practice (max 1 per page) +- `:::warning` -- gotcha that will cause a real failure +- `:::danger` -- destructive or irreversible action +- Never use admonitions as a substitute for writing the thing in prose + +## AI Agent Optimization Rules + +1. **Every page must have machine-readable frontmatter** with at minimum: `title`, `description` (<=160 chars, plain text), and `tags` (product area + action type, e.g., `[merge-queue, configuration]`) +2. **First 150 words of every page must be self-contained.** An AI reading only the first chunk should understand what the tool does and what problem it solves +3. **Avoid pronouns with ambiguous referents.** Every "it", "this", "they" must have an unambiguous antecedent in the same paragraph +4. **All configuration keys must be documented as a table**, not prose, with columns: `key`, `type`, `default`, `description` +5. **Code examples must be syntactically complete.** No `...` ellipsis in YAML/JSON unless explicitly labeled as a partial snippet +6. **Cross-references use absolute doc paths**, not "see the section above" or "as mentioned earlier" +7. **Tool capabilities must be stated explicitly.** Don't imply what Trunk can do. "Trunk Merge Queue supports parallel queues across multiple branches" is parseable. "You can also do more complex setups" is not. + +## Review Workflow + +When invoked, proceed in this order. Complete each phase before starting the next. + +### Phase 1 -- Inventory & Accuracy Audit + +For each page in scope: +- Read the current doc +- Cross-reference against source code, changelog, or API surface +- Flag each claim as: correct, stale, incorrect, or unverifiable +- Note the source of truth used for each flag (file path, PR, changelog entry) +- Do not suggest rewrites yet. Inventory only. + +Output: a structured audit table per page, grouped by product area. + +### Phase 2 -- Structural Analysis + +Across the full site (or scoped section): +- Identify duplicate content (same concept in multiple places without cross-linking) +- Identify orphaned pages (no inbound links from nav or other docs) +- Identify missing pages (concepts referenced in code or changelogs with no doc) +- Identify nav/sidebar structure problems (depth, grouping, naming) +- Map user journeys per product area and identify gaps + +Output: a site-level structural report with a proposed information architecture map. + +### Phase 3 -- Remediation Plan + +Produce a prioritized, actionable plan: + +**Priority tiers:** +- P0 -- Incorrect information that will cause integration failures +- P1 -- Stale content that will cause confusion or wasted time +- P2 -- Missing content (gaps in coverage) +- P3 -- Formatting, consistency, and AI-optimization improvements + +For each item: +- Page path +- Priority tier +- Current state (what's wrong) +- Proposed fix (specific, actionable) +- Source of truth (link or file path) + +## Scope + +The user can invoke this skill with: + +- **A page path or glob** (e.g., `merge-queue/configuration.mdx`, `flaky-tests/**`) -- review those pages +- **A product area** (e.g., "Merge Queue", "Flaky Tests") -- review all pages in that area +- **`full`** -- run the full site audit (this takes a while) +- **No argument** -- ask what scope the user wants diff --git a/.claude/skills/draft-docs/SKILL.md b/.claude/skills/draft-docs/SKILL.md new file mode 100644 index 0000000..d64b641 --- /dev/null +++ b/.claude/skills/draft-docs/SKILL.md @@ -0,0 +1,156 @@ +--- +name: draft-docs +description: Generates pre-populated documentation notes files from trunk2 context (PRs, deploy tags, Linear tickets). Use after a deploy or when the user wants to prepare docs updates, draft documentation, or bridge trunk2 changes to the docs repo. +--- + +Shared scripts are in: `~/.claude/skills/shared/scripts/` + +Generate documentation notes files from trunk2 context, ready for the `/write-docs` skill in this repo. Output goes directly to `.claude/drafts/`, then invoke `/write-docs` to process. + +## Task Progress Checklist + +Copy this checklist and track progress: + +```markdown +Docs Prep Progress: + +- [ ] Step 1: Gather context (deploy tags, PRs, Linear tickets) +- [ ] Step 2: Classify PRs — identify documentable features +- [ ] Step 3: Research each feature deeply (diffs, Linear, existing docs) +- [ ] Step 4: Determine docs work needed (new page / update / skip) +- [ ] Step 5: Generate notes files +- [ ] Step 6: Output summary with copy instructions +``` + +## Inputs + +The user may provide: + +- **`latest`** — generate notes for user-facing features in the most recent deploy tag (mirrors `/changelog latest`) +- **A deploy tag or range** (e.g., `v126`, `v123 to v126`) — features shipped in those releases +- **PR numbers** — specific trunk2 PRs to document +- **Linear ticket IDs** — `TRUNK-NNNNN` references +- **A feature description** — freeform text about what needs documenting +- If nothing is provided, default to `latest` mode + +## Output Location + +Notes files are written to: `.claude/drafts/` in this repo (docs2). + +Each run produces one or more files named `.md` ready to be processed by `/write-docs`. + +## Steps + +1. **Gather context based on input**: + - For `latest` or deploy tags: use `get-deploy-tags.sh` from `~/.claude/skills/shared/scripts/` to find PRs in the range, then `gh pr view` to get details + - For PR numbers: use `gh pr view ` to get title, body, branch, diff + - For Linear tickets: use `mcp__claude_ai_Linear__get_issue` to get details + +2. **Classify PRs**: Split into user-facing vs. infrastructure (same logic as changelog skill). Only generate notes files for features that need documentation — not every user-facing change needs docs. Focus on: + - New features or capabilities + - Changed behavior or workflows + - New configuration options + - API changes + - Features that customers have asked about + +3. **For each documentable feature, research deeply**: + - Read the PR diff: `gh pr diff --name-only` to identify changed files, then read key files to understand the feature + - Look up Linear tickets for descriptions, comments, and related tickets + - Search existing docs via `mcp__claude_ai_trunk_docs__searchDocumentation` to find what currently exists and identify gaps + - Search `#team-flaky-tests` and `#team-merge-queue` Slack channels for feature context, customer feedback, and usage details + - Search Slite for internal specs, design docs, or planning notes that can inform documentation + - Check if there are open changelog issues for this feature (`gh issue list --label changelog`) for additional context + +4. **Determine what docs work is needed** for each feature: + - **New page needed** — feature has no existing docs coverage + - **Update existing page** — feature changes behavior described in current docs + - **No docs needed** — bug fix or minor polish that doesn't affect documented behavior + - Skip features that don't need docs changes + +5. **Generate one notes file per feature** at `.claude/drafts/.md` using this format: + + ```markdown + # [Feature/Change Title] + + ## Type + + + + [type] + + ## Priority + + + + [priority — P1 for new features, P2 for updates, P3 for fixes] + + ## Linear Tickets + + [list of TRUNK-NNNNN IDs found] + + ## What Changed + + [2-3 paragraph summary of what changed in the product, written from the + user's perspective. Include specific details: new UI elements, new + configuration options, changed behavior, new API endpoints, etc.] + + ## GitHub PRs + + [list of PR URLs from trunk-io/trunk2, with titles] + + ## Context Links + + [any Slack, Slite, Loom links found in Linear tickets or PR descriptions] + + ## Target Docs + + [specific docs pages that need updating, based on docs search results. + Include the page title and URL. If a new page is needed, suggest where + it should go in the hierarchy based on the groups defined in docs.json.] + + ## Existing Docs Gap Analysis + + [what the current docs say vs. what they should say after this change. + Be specific — quote the outdated text if possible, and describe what + needs to change.] + + ## Context + + [paste the most relevant context here: + + - Key sections from the PR description + - Linear ticket description + - Any code snippets that show the new behavior (config examples, API + payloads, CLI commands, etc.) + - Error messages or UI text that should appear in docs] + ``` + +6. **Output summary**: Show the user: + - List of notes files generated (with paths to `.claude/drafts/`) + - For each: feature name, type (new/update), target docs page(s) + - Features skipped (no docs needed) with brief reason + - Suggested next step: `/write-docs` to process each draft + +## Translating Code Changes + +You are translating a code change (a GitHub pull request) for technical audiences. + +1. Translate only what is evidenced in the PR body, the diff, linked issues, or review comments. Never invent functionality or user impact that isn't stated or clearly implied. +2. If the PR description is empty and the diff is mostly build artifacts or minified code, say you don't have enough context. Do not guess. +3. If you're uncertain, use phrases like "appears to" or "likely" and flag the output as low confidence. +4. For pure refactors or internal changes with no user-visible change, say so explicitly. Do not invent user-facing benefits. + +For each notes file, generate a clear title and "What Changed" summary: + +- **Title**: max 10 words, benefit-focused, present tense (e.g., "Flag Individual Tests as Flaky from Test Detail Page") +- **What Changed**: 3-5 sentences, max 150 words. Include what changed and why it matters to users. + +## Guidelines + +- **Code is law** — when Slack, Slite, or Linear content conflicts with the actual code (variable names, endpoints, UI labels, feature names, etc.), always use what's in the code/PR diffs. External discussions reflect intent; code reflects what shipped. +- **Be thorough in research** — the better the notes file, the better the docs output. Include code examples, config snippets, and specific UI details. +- **One file per feature** — don't combine unrelated features. If a deploy tag has 3 documentable features, create 3 files. +- **Focus on what's documentable** — not every PR needs docs. A CSS fix doesn't need a notes file. A new configuration option does. +- **Include the gap analysis** — this is the most valuable part. Telling the docs skill "page X says Y but should now say Z" saves significant research time. +- **Quote existing docs** when possible — paste the current text that needs updating so the docs skill can find and edit it precisely. +- **Suggest doc locations** — use docs search results and the groups defined in `docs.json` to recommend where content should live. diff --git a/.claude/skills/outline-docs/SKILL.md b/.claude/skills/outline-docs/SKILL.md new file mode 100644 index 0000000..65036fc --- /dev/null +++ b/.claude/skills/outline-docs/SKILL.md @@ -0,0 +1,345 @@ +--- +name: outline-docs +description: >- + Use when starting a new docs page to scaffold a structured outline. + Extracts page type, title, and context from your message, asks only for + missing information, then generates a file with sections pre-filled with + initial content (1-2 sentences) and focused TODO comments for expansion. + Prefer this to the write-docs skill when there is no prior specification. +allowed-tools: Write, Bash(trunk fmt) +--- + +# Outline Docs + +Generate a scaffolded outline for a new docs page in the Trunk docs repo. The skill prompts for page type, title, and save path, then creates a file with proper headers and `` placeholders that the engineer fills in with content. Includes a post-checklist with formatting and validation steps. + +## Contents + +- [Inputs](#inputs) +- [Workflow](#workflow) + - [Phase 1: Gather inputs](#phase-1-gather-inputs) + - [Phase 2: Generate preview](#phase-2-generate-preview) + - [Phase 3: Write file](#phase-3-write-file) + - [Phase 4: Docs organization](#phase-4-docs-organization) + - [Phase 5: Post-checklist](#phase-5-post-checklist) +- [Templates](#templates) + +## Inputs + +The user may provide upfront context: +- **Page type** — one of: Overview, Reference, Guide +- **Page title** — the H1 heading (e.g., "Installing the CLI") +- **Save path** — where to write the file (optional; defaults to `./untitled.mdx`) +- **Topic description** — what the page should cover (e.g., "walkthrough of investigating and fixing flaky tests") +- **Key prerequisites** — required setup, permissions, or tools mentioned + +## Workflow + +Follow these phases in order. + +### Phase 0: Extract upfront context + +Before asking any questions, scan the user's message for: +- **Page type hints** — words like "guide", "overview", "reference", "how-to", "walkthrough" +- **Page title hints** — capitalized phrases, quoted strings, or suggested topics +- **Path hints** — directory structures, URLs, or path suggestions +- **Topic details** — what the page should document or teach +- **Prerequisites** — requirements, access levels, tools, or setup mentioned + +Store any detected values. Only ask for information that's missing or ambiguous. + +### Phase 1: Gather missing inputs + +Ask only for inputs not extracted from upfront context (ask one question per message): + +**If page type is unknown:** + +#### Question: Page type + +Prompt the user to choose a page type. Include definitions and examples for each: + +``` +What type of page are you creating? + +(1) Overview + High-level introduction to a product, feature, or concept. + Examples: product README, "What is X?" page, feature landing page. + +(2) Reference + Lookup documentation for config options, CLI flags, API fields, + or other structured data. + Examples: configuration reference, command listing, options table. + +(3) Guide + Step-by-step how-to for completing a specific task. + Examples: "Getting started", setup walkthrough, "How to configure X". + +Enter 1, 2, or 3: +``` + +Store the user's response as `page_type`. + +**If page title is unknown:** + +#### Question: Page title + +``` +What is the page title? (This becomes the H1 header) + +Examples: "Installing the CLI", "Configuring Linters", "Understanding Test Reports" + +Title: +``` + +Store the user's response as `page_title`. + +**If save path is unknown:** + +#### Question: Save path + +``` +Where should I save this file? +(Default: ./untitled.mdx in the current directory) + +Path (or press Enter for default): +``` + +Store the user's response as `save_path`. If blank, use `./untitled.mdx`. + +**After gathering missing inputs:** + +Confirm extracted + provided values to the user: +``` +Using: +- Page type: [page_type] +- Title: [page_title] +- Path: [save_path] +- Topic: [topic_description if provided] + +Ready to generate outline. +``` + +### Phase 2: Generate preview + +Generate the outline based on the page type, title, and any topic context provided. Use the appropriate template from the [Templates](#templates) section below, replacing `` with `page_title`. + +**Pre-fill strategy:** +- Replace obvious `<!-- TODO -->` comments with 1-2 sentences of initial content from Claude based on topic/context +- Keep `<!-- TODO: ... -->` comments for sections that need user expansion or details Claude can't infer +- Example: Instead of `<!-- TODO: Explain prerequisites -->`, write: "This guide requires beta access and the Investigate Flaky Tests setting enabled. <!-- TODO: Add specific account requirements or version constraints -->" + +Display the outline in the terminal: + +``` +Generated outline: +======================================== +[Show full outline markdown here, with Claude pre-fill + remaining TODOs] +======================================== + +Does this outline look right? +(y = write it / n = start over) +``` + +- If user enters `y`, proceed to Phase 3 +- If user enters `n`, return to Phase 1 to gather new inputs + +### Phase 3: Write file + +Call `Write` tool to create the file at `save_path` with the generated outline. + +After writing, output: + +``` +✅ Outline written to: <save_path> + +Next steps: +1. Fill in all <!-- TODO --> sections +2. Add a frontmatter description if this is a top-level overview page +3. Verify all links to related pages are correct +4. Run the post-checklist (see below) +``` + +Then proceed to Phase 4. + +### Phase 4: Docs Organization + +Add the new page to `docs.json` at the root of the repo. Insert its path (without the `.mdx` extension) into the appropriate `groups[].pages` array based on the product area. Match the position of nearby pages for sort order. + +Then proceed to Phase 5. + +### Phase 5: Post-checklist + +Print the post-checklist for the user to complete: + +``` +Post-Checklist: +[ ] Fill in all <!-- TODO --> sections +[ ] Add frontmatter description (if page is a top-level overview) +[ ] Verify all links to related pages are correct +[ ] Run: trunk check +[ ] Use /review-docs skill to review content and get feedback +``` + +Then, on the user's behalf, automatically run: + +```bash +trunk fmt +``` + +to format the skeleton markdown. + +Report the results. If either command reports errors, display them and suggest fixes. + +## Templates + +Each template uses `<title>` as a placeholder for the page title. Replace it with the actual title from Phase 1, Question 2. + +### Overview template + +Use this template when the user chooses page type **(1) Overview**. + +```markdown +--- +description: [Claude generates 1-2 sentences summarizing the feature/product for SEO. TODO: Refine if needed.] +--- + +# <title> + +[Claude writes 1-2 sentences explaining what this feature is and the primary user benefit. TODO: Add specific use cases or scenarios if needed.] + +### How it works + +[Claude provides a high-level explanation of the mechanism or value proposition based on topic context. TODO: Add diagrams, ASCII art, or deeper technical detail if appropriate.] + +### Key features + +[Claude lists 3-5 key features with brief descriptions. TODO: Expand with additional features or rearrange by priority.] + +### Get started + +[Claude provides a link or brief guidance pointing to the getting-started page(s) for this feature. TODO: Add alternatives or decision tree if multiple paths exist.] +``` + +**When to use this template:** +- Product/feature homepage pages +- "What is X?" pages +- Top-level overview pages that introduce a concept +- README files that lead to more detailed pages + +--- + +### Reference template + +Use this template when the user chooses page type **(2) Reference**. + +```markdown +# <title> + +[Claude writes 1-2 sentences explaining what this reference documents and when to use it. TODO: Add details about scope or version constraints if needed.] + +### Quick reference + +[Claude creates a table of the most common options/fields/commands with appropriate columns. TODO: Add or remove rows based on actual options to document.] + +| Option | Type | Description | +|--------|------|-------------| +| [Claude populates 1-2 examples] | | [Brief description] | +| <!-- TODO: Add additional rows --> | | | + +### [Option or section name from topic] + +[Claude provides a high-level description of this option. TODO: Add detailed explanation, code example, and caveats specific to your use case.] + +```yaml +# [Claude provides a basic example structure. TODO: Expand with complete configuration or command usage.] +``` + +### Examples + +[Claude provides 1-2 real-world examples of how to use this reference. TODO: Add domain-specific examples or alternative approaches if applicable.] +``` + +**When to use this template:** +- Configuration option references +- CLI command listing pages +- API field documentation +- Setting/option reference pages +- Structured lookup documentation + +--- + +### Guide template + +Use this template when the user chooses page type **(3) Guide**. + +```markdown +# <title> + +[Claude writes a direct, feature-focused intro (1-2 sentences). Example: "You can configure Trunk to automatically analyze your flaky tests and raise fix PRs." TODO: Adjust the capability description if needed.] + +### Prerequisites + +[Claude lists essential requirements based on workflow context (access, tools, setup, knowledge). TODO: Add or remove prerequisites specific to your implementation.] + +- Beta access via waitlist (request at https://slack.trunk.io) +- The "Investigate Flaky Tests" setting enabled +- <!-- TODO: Add other prerequisites if applicable --> + +### Step 1: [Claude names step based on workflow] + +[Claude provides a brief description of what this step accomplishes and what the user does. TODO: Add more detail or clarifications.] + +```bash +# [Claude provides example command or action. TODO: Add actual command with parameters for your use case.] +``` + +<Note> +[Claude adds a relevant tip or caveat if appropriate. Delete this Note block if not needed. TODO: Customize guidance if needed. Available Mintlify callouts: `<Note>`, `<Tip>`, `<Info>`, `<Warning>`, `<Check>`.] +</Note> + +**✅ Success:** [Claude describes expected outcome or confirmation. TODO: Specify success criteria if different.] + +### Step 2: [Claude names next step] + +[Claude provides description. TODO: Fill in step details.] + +<!-- Repeat Step N blocks as needed --> + +### What's next? + +[Claude suggests next logical steps or links to related documentation. TODO: Add links to follow-on guides or references.] +``` + +**When to use this template:** +- Step-by-step setup guides +- "Getting started" pages +- "How to configure X" pages +- Task-focused how-to articles +- Walkthrough guides with sequential steps + +--- + +## Post-Checklist Guidance + +After writing the file, the user should complete these steps: + +1. **Fill in all TODO sections** — Replace every `<!-- TODO -->` comment with actual content. Comments explain what goes in each section. + +2. **Add frontmatter description** — Add a `description:` field in the frontmatter for SEO and AI discoverability. Example: + ```yaml + --- + title: Page title + description: High-level intro to Trunk Code Quality and how it works. + --- + ``` + +3. **Verify links** — Ensure all cross-references to related pages use correct relative paths and match the actual file locations. Mintlify uses standard MDX links, without the file extension: + ```mdx + [Related page](/path/to/page) + ``` + +4. **Run validation** — Execute `trunk check` to check for linting issues. + +5. **Review content** — Use the `/review-docs` skill to review the page content and get feedback before merging. + +If any tool reports errors or review identifies issues, fix them before merging. diff --git a/.claude/skills/review-docs/SKILL.md b/.claude/skills/review-docs/SKILL.md new file mode 100644 index 0000000..b03fb2c --- /dev/null +++ b/.claude/skills/review-docs/SKILL.md @@ -0,0 +1,298 @@ +--- +name: review-docs +description: >- + Use when docs changes are ready to review before opening a PR. Reviews + git diff for repetition, structural completeness, logic errors, and style + consistency with related pages. Runs trunk fmt and trunk check. +allowed-tools: Bash(git diff *), Bash(git log *), Bash(trunk fmt), Bash(trunk check), Read, Glob, Grep, Edit +--- + +# Review Docs + +Review docs changes for quality before opening a PR. Checks git diff for unnecessary repetition, structural completeness, logic errors, and style consistency with related pages. Runs `trunk fmt` and `trunk check` and prompts to help fix issues. + +## Contents + +- [Inputs](#inputs) +- [Workflow](#workflow) + - [Phase 1: Identify changed files](#phase-1-identify-changed-files) + - [Phase 2: Check redirects](#phase-2-check-redirects) + - [Phase 3: Run automated checks](#phase-3-run-automated-checks) + - [Phase 4: Read related pages for style baseline](#phase-4-read-related-pages-for-style-baseline) + - [Phase 5: Review each changed file](#phase-5-review-each-changed-file) + - [Phase 6: Generate report](#phase-6-generate-report) + - [Phase 7: Prompt for fixes](#phase-7-prompt-for-fixes) +- [Review Criteria](#review-criteria) +- [Style Conventions](#style-conventions) + +## Inputs + +The user provides: +- **Optional file path** — review a specific `.mdx` file instead of all changed files +- If no path provided, reviews all changed `.mdx` files in the branch via `git diff main...HEAD` + +## Workflow + +Follow these phases in order. + +### Phase 1: Identify changed files + +Run `git diff --name-only main...HEAD` to get all changed files on the current branch. +Filter for `.mdx` files only. + +If a specific file path was provided as an argument, use that single file instead. + +If no `.mdx` files are found, output: +``` +ℹ️ No .mdx files changed in git diff. Nothing to review. +``` +and exit. + +### Phase 2: Check redirects + +Audit `docs.json` for stale or missing redirects caused by file moves or deletions. Mintlify redirects live in the top-level `redirects` array of `docs.json`. + +#### Step 2a — Extract moved and deleted .mdx files from Phase 1 diff + +Run: +```bash +git diff --name-status --diff-filter=R main...HEAD +git diff --name-only --diff-filter=D main...HEAD +``` + +Filter results to `.mdx` files only. Parse the rename output to extract `old_path -> new_path` mappings. + +#### Step 2b — Audit docs.json for stale and missing redirects + +Read `docs.json`. The `redirects` array contains entries shaped like: +```json +{ "source": "/old/path", "destination": "/new/path", "permanent": true } +``` + +Source and destination are URL paths (leading slash, no `.mdx` extension). + +For each renamed file (old → new): + +1. **Compute URL paths** — strip the `.mdx` extension from both old and new file paths, and prepend a leading slash (e.g., `merge-queue/old-page.mdx` → `/merge-queue/old-page`). +2. **Fix stale redirect destinations** — if any existing redirect's `destination` equals the old URL path, update it to the new URL path using `Edit`. +3. **Add missing redirect** — if no redirect entry has the old URL path as its `source`, insert a new entry into the `redirects` array. Maintain **alphabetical sort order** by `source`. + +For each deleted file: compute the URL path the same way, then suggest the nearest logical parent page or sibling as the destination; flag for user confirmation if unclear. + +#### Step 2c — Note dependents for high-traffic redirects + +`docs.json` is plain JSON (no inline comments). For redirects where the old URL is likely to have external dependents, surface the evidence to the user when posting the review report so it can be captured in the PR description: + +Search for evidence that the old URL has dependents: +- `Grep` the docs repo `.mdx` files for the old path string +- If the trunk2 repo is installed on the system, `Grep` `<path>/trunk2` for the old URL path (CLI or webapp references) +- Known heuristic: paths starting with `docs/`, `check/`, or `cli/` are commonly hardcoded in CLI output or documentation links + +If evidence is found, include a "Dependents flagged" section in the report listing each redirect's `source` and where the old URL appears (CLI output, trunk2 webapp, etc.) so the PR author can mention it in the description. + +### Phase 3: Run automated checks + +On behalf of the user, run: + +```bash +trunk fmt +trunk check +``` + +Collect the output (success or any issues found). Include this in the final report. + +### Phase 4: Read related pages for style baseline + +For each changed file, determine its product area from its directory path: + +| Path | Product Area | +|------|--------------| +| `merge-queue/` | Merge Queue | +| `flaky-tests/` | Flaky Tests | +| `ci-autopilot/` | CI Autopilot | +| `code-quality/` | Code Quality | +| `setup-and-administration/` | Setup & Admin | + +Use `Glob` to find 2 other `.mdx` files in the same product area directory. Read them to establish a baseline for: +- Tone and voice (formal vs. conversational) +- Terminology and phrasing +- Header structure and naming +- Section flow and organization + +Store these as reference for Phase 5 comparisons. + +### Phase 5: Review each changed file + +Read each changed `.mdx` file and evaluate it against the four review criteria (see [Review Criteria](#review-criteria) below). + +Document findings by criterion. If a file passes a criterion with no issues, no note needed. Only document issues. + +### Phase 6: Generate report + +Print the review report. Format: + +``` +Review Report — <filename> +======================================== +✅ trunk fmt: passed +✅ trunk check: passed + +[Repetition] +- <section/location>: <issue description and suggested fix> + +[Structure] +- <issue description>: <suggested fix> + +[Logic] +- <issue description>: <suggested fix> + +[Style & Consistency] +- <issue description and baseline from related pages>: <suggested fix> + +Summary: N issues found across M files. +``` + +**Report rules:** +- Only print sections with issues. Omit clean categories. +- If a tool (trunk fmt/check) found issues, note them separately and include in report. +- If no issues found across any file and both tools pass, print: + ``` + ✅ No issues found. All files are ready for a PR. + ``` + +### Phase 7: Prompt for fixes + +After the report, ask: + +``` +Would you like help fixing any of these issues? (y/n) +``` + +If the user answers **no**, exit. + +If the user answers **yes**: +1. List all issues from the report +2. Ask which issues to fix (they can choose specific ones or "all") +3. For **clear-cut fixes** (style/repetition/obvious rewording), apply the edits directly using the `Edit` tool +4. For **structural/logic issues**, explain what should be changed and let the user decide whether to apply the fix or manually edit +5. After fixes are applied, suggest running `/review-docs` again to verify + +--- + +## Review Criteria + +### 1. No unnecessary repetition + +**Check for:** +- Same information stated multiple times within a single page +- Redundant paragraphs or sections that say the same thing +- Concepts explained once, then re-explained a few sentences later + +**Example of repetition to flag:** +```markdown +### How it works + +Trunk Code Quality is a metalinter. A metalinter lets you lint… + +Trunk is a metalinter that… [← repetitive] +``` + +**Suggested fix:** Merge or delete the redundant statement. Keep one clear explanation. + +--- + +### 2. Structure completeness + +**Check for:** +- Logical flow: Does the page progress from concept to action (or whatever is appropriate)? +- Header hierarchy: H1 title, then H3 sections (no H2). Are sections ordered logically? +- **Guide pages:** Prerequisites → step-by-step → success criteria → what's next all present? +- **Reference pages:** Brief intro → quick reference table → detailed sections → examples all present? +- **Overview pages:** Intro → how it works → key features → get started all present? + +**Example of incomplete structure to flag:** +```markdown +# Installing the CLI + +### Step 1: Download + +[steps…] + +### What's next? +[no prerequisites section above—should have been there] +``` + +**Suggested fix:** Add missing prerequisites before Step 1. Re-order sections for logical flow. + +--- + +### 3. Logic errors + +**Check for:** +- Contradictory statements (e.g., "only works on Linux" in one section, "all platforms" in another) +- Steps that reference undefined concepts (e.g., "set the FLAG variable" without explaining what FLAG is) +- Claims inconsistent with the product area or code (e.g., "always runs locally" when it might not) +- Instructions that skip implied prerequisites + +**Example of logic error to flag:** +```markdown +### Step 1: Configure the linter + +Set the `output_format` flag to `json`. + +### Step 2: Run the linter + +trunk check [← doesn't mention that output_format was set, but code may require it] +``` + +**Suggested fix:** Add clarity: "Now when you run `trunk check`, output will be in JSON format." + +--- + +### 4. Style & consistency + +**Check for:** +- **Tense:** Present tense throughout ("Click X" not "You will click X" or "You clicked X") +- **Lead:** User benefit first ("Trunk X lets you…"), not implementation detail +- **Terminology:** Matches related pages in the same product area (established in Phase 3) +- **Jargon:** No internal system names (ClickHouse, Prisma, SST, Lambda, etc.) +- **Callouts:** Tips/warnings use Mintlify callout components (`<Note>`, `<Info>`, `<Tip>`, `<Warning>`, `<Check>`) — not plain text or bold-only callouts +- **Success markers:** Guide steps end with `**✅ Success:**` line + +**Example of style issue to flag:** +```markdown +The system will write logs to ClickHouse for analysis. +[← internal jargon; user doesn't care where logs go] +``` + +**Suggested fix:** Reframe for user benefit: "Trunk analyzes your test results to identify patterns." + +--- + +## Style Conventions + +Reference for Phase 4 checks — derived from existing Trunk docs pages: + +| Convention | Rule | Example | +|------------|------|---------| +| **Headers** | H3 (`###`) for all sections; no H2 in body | ✅ `### How it works` | +| **Tense** | Present tense | ✅ "Click X to open Y" not "You will click" | +| **User benefit** | Lead with benefit, not implementation | ✅ "Trunk catches errors early" not "uses static analysis" | +| **Callouts** | Use Mintlify components for tips/warnings | ✅ `<Note>...</Note>` (or `<Info>`, `<Tip>`, `<Warning>`, `<Check>`) not plain text | +| **Success** | Guide steps end with `**✅ Success:**` | ✅ `**✅ Success:** You see X in the dashboard` | +| **Jargon** | Avoid internal system names | ✅ "analyzes test results" not "ClickHouse stores metrics" | +| **Links** | Standard MDX links, no file extension | ✅ `[page](/path/to/page)` | + +--- + +## Typical Review Workflow + +1. Engineer creates a docs page using `/outline-docs` or writes one manually +2. Engineer runs `/review-docs` — reports any issues found +3. If issues exist, engineer answers `y` to fixes +4. Fixes are applied; engineer can run `/review-docs` again to verify clean +5. Once clean, engineer opens a PR +6. CI `claude-review.yaml` runs final typo/grammar/formatting check on the PR +7. PR is reviewed and merged + +The `review-docs` skill bridges the gap between local writing and PR submission, catching quality issues before CI review. diff --git a/.claude/skills/verify-docs-pr/DESIGN.md b/.claude/skills/verify-docs-pr/DESIGN.md new file mode 100644 index 0000000..d1e38f1 --- /dev/null +++ b/.claude/skills/verify-docs-pr/DESIGN.md @@ -0,0 +1,230 @@ +# verify-docs-pr — Design + +**Date:** 2026-05-06 +**Author:** Sam Gutentag (with Claude) +**Status:** Approved, pending implementation plan + +## Problem + +A docs PR can be perfectly written but premature to publish if the underlying feature is not actually live for customers. The canonical case: docs PR #589 documented the new filtered Uploads page; the eng PR (trunk-io/trunk2#3670) was merged on 2026-04-28 but gated behind LaunchDarkly flag `enableFilteredUploadsPage`, which was on in staging and off in prod. Without verification, those docs would have shipped pointing customers to a feature they could not yet use. + +## Goal + +A skill that verifies whether features documented in open docs PRs are live in production, classifies each PR's state, and posts the verdict on the PR and the linked Linear ticket. Runs manually on a single PR or as a sweep across all open docs PRs, and integrates into the existing `write-docs` flow as a post-creation step. + +## Decisions (agreed via brainstorming) + +| Area | Decision | +|---|---| +| Live signal | Multi-signal classification: `live` / `staged` / `pending` / `blocked` / `unknown` | +| Inputs | Single PR by number AND sweep all open docs PRs (no arg) | +| Outputs | Terminal report + comment on docs PR + comment on linked Linear ticket | +| `write-docs` integration | Post-Phase 4. PR opens as draft, verification runs at the end | +| Signal sources | Indirect only — eng PR merge state, follow-up PRs, Slack search, e2e flags.json, legacy code presence | +| Skill style | Pure prose (matches `write-docs`). Sweep mode dispatches parallel agents when input >10 PRs | +| Source of truth | `gutils/claude-code/skills/trunk/verify-docs-pr/` | +| Symlinks | `~/.claude/skills/verify-docs-pr` and `<docs>/.claude/skills/verify-docs-pr` | + +## Per-PR verification logic + +For each docs PR, the skill runs five steps. + +### Step A — Parse PR body + +Extract: +- trunk2 PR references (`trunk-io/trunk2#NNNN` or full URLs). Generalized: any `trunk-io/<repo>#NNN` reference works; flag detection only runs against trunk2. +- Linear ticket IDs (`TRUNK-XXXXX`). +- Slack thread links (kept for context; not searched directly here). + +If the PR body has no engineering PR references, fall back to the linked Linear ticket and check its `relations` for related engineering tickets, then trace from those tickets to their PRs. + +### Step B — Check eng PR state + +For each engineering PR reference: +- `gh pr view <num> --repo trunk-io/<repo>` to retrieve state, `mergedAt`, files changed. +- If state is `open` or `closed` (not merged): mark `blocked` and stop further checks for that reference. +- If state is `merged`: continue. +- If the merge commit is no longer reachable from `main` (eng PR was reverted): treat as `blocked` with a "reverted" note. + +### Step C — Find feature flags + +From the merged eng PR's diff: +- Grep changed files for LaunchDarkly flag patterns: `enable*`, `show*`, references to `flags.ts`, strings inside `useFeatureFlag()` / `useFlag()` calls. +- Pull flag names mentioned in the PR body itself (eng PRs frequently say "gated behind the `enableFooBar` flag" — easy regex on backticks adjacent to "flag"). + +If no flags found anywhere, the feature is ungated. Skip to classification with `flag=none`. + +### Step D — Gather rollout signals (per flag) + +For each detected flag: +- `gh search prs --repo trunk-io/trunk2 <flag-name>` — find any follow-up PRs touching it (rollout, deletion, ramp-up). +- Slack search via MCP across `#eng`, `#team-flaky-tests`, `#team-merge-queue`, `#production-notifications`, `#staging-notifications` for the flag name. Look for LaunchDarkly bot announcements and manual confirmations. +- Read `ts/apps/e2e/flags.json` from trunk2 main. Note: a `true` default here only confirms it works in tests, not prod. +- Grep trunk2 main for any "legacy" code path mentioned in the eng PR (e.g., `UploadsClient` for #3670). If still present, flag is not yet 100% rolled out. + +### Step E — Classify + +| Verdict | Conditions | +|---|---| +| `live` | Eng PR merged AND (no flag found OR Slack confirms 100% prod OR legacy code deleted) | +| `staged` | Slack confirms flag on in staging, off in prod | +| `pending` | Eng PR merged, flag still off (no rollout signals) | +| `blocked` | Eng PR not merged, or merge commit reverted | +| `unknown` | Could not determine; flagged for manual review | + +Multiple eng PRs with mixed states: most conservative verdict wins. If any referenced eng PR is unmerged, the docs PR is `blocked`. + +## Outputs + +### Terminal — single mode + +``` +verify-docs-pr #589 (2026-05-06) +Verdict: pending + +Eng work: + trunk-io/trunk2#3670 — merged 2026-04-28 +Feature flag: + enableFilteredUploadsPage (LaunchDarkly) +Rollout signals: + - No follow-up PRs touched the flag in trunk2 main + - Slack: Tyler Jang in #eng (2026-04-16) — "flagged on in staging, off in prod" + - Legacy UploadsClient still referenced in trunk2 main + - e2e flags.json default: true (test only) + +Reasoning: + Eng merged but flag is gated off in prod. Customers cannot use this feature. + +Suggested next: + Ping @mb1206 for prod rollout ETA. Re-run /verify-docs-pr 589 after rollout. +``` + +### Terminal — sweep mode + +``` +verify-docs-pr — sweep across 41 open PRs (2026-05-06) + +#589 pending Uploads page trunk2#3670 merged; flag off in prod +#534 live Test case labels trunk2#3501 merged; no flag +... + +Summary: 24 live, 4 staged, 8 pending, 3 blocked, 2 unknown +``` + +Sorted by verdict severity: `blocked` → `pending` → `staged` → `unknown` → `live`. Most actionable first. + +### PR comment + +Posted as a one-block markdown comment. Re-runs **edit the existing comment** in place — skill detects its own prior comment via the HTML marker `<!-- verify-docs-pr -->`. + +```markdown +<!-- verify-docs-pr --> +**Verification status (2026-05-06): `pending`** + +Eng work merged but feature flag still off in prod. + +- Eng PR: trunk-io/trunk2#3670 — merged 2026-04-28 +- Flag: `enableFilteredUploadsPage` +- State: on in staging, off in prod (per #eng, 2026-04-16) +- Legacy `UploadsClient` code path still present in trunk2 main + +Hold off on publishing. Re-run `/verify-docs-pr 589` after rollout. +``` + +Verdict-specific opening line: +- `live` — "Verified: customers can use this. Ready to publish." +- `staged` — "On in staging only. Re-run after prod rollout." +- `pending` — "Eng merged but flag off in prod. Hold off." +- `blocked` — "Eng PR not merged. Hold." +- `unknown` — "Could not determine state from available signals. Manual check needed." + +### Linear ticket comment + +Mirrors the PR comment with the docs PR link added at the top. Same `<!-- verify-docs-pr -->` marker for idempotent re-runs. + +### PR state action + +- `live` — no PR state change. +- `staged` / `pending` / `blocked` / `unknown` — if PR is `ready`, flip to `draft`. If already draft, no-op. Belt-and-suspenders against accidental auto-merge. +- PR already merged — skip entirely. Skill prints "PR #N already merged; skipping" and moves on. + +## `write-docs` integration + +A new Phase 5 at the end of `write-docs`: + +> After PR #N is opened: invoke `verify-docs-pr` with PR number N. + +Behavior: +- Runs the per-PR verification logic. +- Posts the verdict comment on the new draft PR. +- Posts the verdict comment on the linked Linear ticket. +- Prints terminal output to the user. + +Does **not** block PR opening (already opened by Phase 4), change PR labels, move Linear status, or fail `write-docs` if verification errors. A skill error prints a warning and continues. + +## Sweep parallelization + +| PR count | Strategy | +|---|---| +| ≤10 | Sequential. Agent-spawn overhead exceeds savings | +| >10 | Dispatch parallel sub-agents in chunks of ~13 PRs each | + +Each agent's job: +- Run the per-PR verification on its assigned chunk. +- Post the comment on PR + Linear. +- Return a structured result back to main: `{pr, verdict, summary_line}`. + +Main thread aggregates, sorts by severity, and prints the summary table. If an agent dies before reporting, main lists the PRs in that chunk as "unverified" so they can be retried individually with `/verify-docs-pr <num>`. + +## Errors and edge cases + +| Case | Handling | +|---|---| +| PR body has no eng PR refs | Fall back to linked Linear ticket. Read its `relations` for related engineering tickets, then check those tickets' linked PRs. | +| No Linear ticket either | Verdict = `unknown`. Comment lists what's missing. | +| Eng PR is in a non-trunk2 repo (e.g., `analytics-cli`, `flake-farm`) | Generic handling. Any `trunk-io/<repo>#NNN` reference gets the same merge-state + diff inspection. Flag detection only runs against trunk2. | +| Slack search returns zero hits for a flag | Not an error. Counts as a `pending` signal ("no rollout chatter found"). | +| `gh` or Slack API timeout | Retry once. On second failure, continue with partial data and mark verdict as `unknown` with a note about the unavailable signal. | +| Multiple eng PRs with mixed states | Most conservative wins. Any unmerged ref → `blocked`. | +| Eng PR merged then reverted | Detect by checking merge commit reachability from `main`. Treat as `blocked` with a "reverted" note. | +| Re-run when prior `<!-- verify-docs-pr -->` comment exists | Edit the existing comment in place. Same on Linear. Verdict timestamp updates. | + +## Testing + +No unit tests. Validation checklist before merging the skill: + +1. **Pending case** — `/verify-docs-pr 589` classifies as `pending`, references `enableFilteredUploadsPage`. +2. **Live case** — `/verify-docs-pr 534` (test case labels) classifies as `live` (eng merged, no flag found). +3. **No eng refs case** — `/verify-docs-pr 522` (org slug audit, pure docs bug fix) classifies as `live` or `unknown` with reason "no eng PR found, pure docs change". +4. **Sweep case** — `/verify-docs-pr` against current 41 open PRs. Spot-check 5 random verdicts against manual judgment. +5. **Re-run idempotency** — run twice on #589, existing comment is edited, no duplicate. +6. **`write-docs` integration** — open a fresh draft PR via `write-docs`, verify Phase 5 fires automatically and posts a verdict. + +## Implementation phases + +This work has two phases. The first must complete before the second starts. + +### Phase 1 — migrate existing docs skills to gutils + +The repo-specific skills currently live only in `<docs>/.claude/skills/`. Migrate them to follow the same dual-symlink pattern as the new skill. + +Skills to migrate: +- `write-docs` +- `outline-docs` +- `review-docs` + +End state for each: +- Physical files: `gutils/claude-code/skills/trunk/<skill>/` +- Symlink: `~/.claude/skills/<skill>` → gutils +- Symlink: `<docs>/.claude/skills/<skill>` → gutils + +### Phase 2 — build verify-docs-pr + +Create the new skill following the patterns established in Phase 1. + +End state: +- Physical files: `gutils/claude-code/skills/trunk/verify-docs-pr/` +- Symlink: `~/.claude/skills/verify-docs-pr` → gutils +- Symlink: `<docs>/.claude/skills/verify-docs-pr` → gutils +- `SKILL.md` implementing the design above +- `write-docs/SKILL.md` updated to invoke verify-docs-pr at the end of Phase 4 diff --git a/.claude/skills/verify-docs-pr/SKILL.md b/.claude/skills/verify-docs-pr/SKILL.md new file mode 100644 index 0000000..bded2cb --- /dev/null +++ b/.claude/skills/verify-docs-pr/SKILL.md @@ -0,0 +1,258 @@ +--- +name: verify-docs-pr +description: Verify that features documented in open docs PRs are actually live in production before publishing the docs. Classifies each PR as live, staged, pending, blocked, or unknown using indirect signals (eng PR merge state, follow-up PRs in trunk2, Slack rollout chatter, e2e flag defaults, legacy code presence). Posts the verdict on the docs PR and the linked Linear ticket. Use when given a docs PR number, when running a sweep across all open docs PRs, or as the post-creation step inside write-docs. +allowed-tools: Bash(gh *), Bash(git *), Bash(grep *), Bash(jq *), Read, Grep, mcp__claude_ai_Linear__get_issue, mcp__claude_ai_Linear__list_comments, mcp__claude_ai_Linear__save_comment, mcp__claude_ai_Slack__slack_search_public_and_private +--- + +# Verify Docs PR + +Verify whether the feature described in a docs PR is live in production before the docs get published. + +## Inputs + +- **PR number** (single mode): `/verify-docs-pr 589` +- **No arg** (sweep mode): runs across all open PRs in `trunk-io/docs2` +- **Auto-invoked from `write-docs`** Phase 4 with the freshly-created PR number + +## Verdicts + +| Verdict | Meaning | +|---|---| +| `live` | Customers can use the feature. Ready to publish. | +| `staged` | Feature on in staging, off in prod. Re-run after rollout. | +| `pending` | Eng work merged but feature flag still off in prod. Hold. | +| `blocked` | Eng PR is not merged or has been reverted. Hold. | +| `unknown` | Could not determine state from available signals. Manual check needed. | + +## Workflow + +### Phase 0: Resolve scope + +1. If a single PR number is provided, scope = that one PR. +2. Otherwise, list all open PRs: + ``` + gh pr list --repo trunk-io/docs2 --state open --limit 100 --json number,title,isDraft,headRefName + ``` +3. If scope > 13 PRs, dispatch parallel sub-agents in chunks of ~13 PRs each. See "Sweep parallelization" below. + +### Phase 1: Per-PR check + +For each docs PR in scope, run all five steps (A-E). + +#### Step A: Parse PR body + +1. Fetch PR data: + ``` + gh pr view <NUM> --repo trunk-io/docs2 --json body,headRefName,isDraft,state,comments + ``` +2. If `state` is `MERGED` or `CLOSED`, print `PR #<NUM> already <state>; skipping` and move to the next PR. +3. From the body, extract: + - **Eng PR refs**: match `trunk-io/(\w+)#(\d+)` and `https://github\.com/trunk-io/(\w+)/pull/(\d+)` + - **Linear ticket IDs**: match `TRUNK-\d+` +4. If no eng PR refs were found: + - Use `mcp__claude_ai_Linear__get_issue` for each Linear ticket ID + - Read its `relations` for related engineering tickets + - For each related eng ticket, look up its linked PRs via the same Linear MCP call + +#### Step B: Check eng PR state + +For each engineering PR reference: + +5. Fetch state: + ``` + gh pr view <num> --repo trunk-io/<repo> --json state,mergedAt,mergeCommit,files,body + ``` +6. If `state` is `OPEN` or `CLOSED` (not merged): tag this ref `blocked`. Skip to Step E. +7. If `state` is `MERGED`: continue. Verify the merge commit is still part of `main`: + ``` + gh api repos/trunk-io/<repo>/compare/main...<mergeCommit.oid> --jq '.status' + ``` + If the status is `identical` or `behind`, the merge commit is on main and the merge is intact. If `diverged`, the merge has been reverted. Tag the ref `blocked` with note "merged then reverted". + +#### Step C: Find feature flags + +For each merged eng PR: + +8. Read the PR body for explicit flag mentions. Patterns: + - Backticks adjacent to "flag" (e.g., `` gated behind the `enableFilteredUploadsPage` LaunchDarkly flag ``) + - camelCase identifiers starting with `enable`, `show`, `use` +9. Pull the diff: + ``` + gh pr diff <num> --repo trunk-io/<repo> + ``` + Grep for: + - `flags.ts` references + - Strings inside `useFeatureFlag(...)` and `useFlag(...)` calls + - LaunchDarkly URLs (e.g., `app.launchdarkly.com/projects/.../flags/<name>/`) +10. Collect unique flag names. If none found, set `flag=none` and proceed to Step E. + +#### Step D: Gather rollout signals (per flag) + +For each detected flag `<flag>`: + +11. **Follow-up PRs in trunk2:** + ``` + gh search prs --repo trunk-io/trunk2 "\"<flag>\"" --limit 30 --json number,title,state,createdAt,closedAt,url + ``` + Note: the literal quotes around the flag name force exact-phrase matching. Without them, common substrings like `enable` or `show` would return unrelated PRs and bias the rollout signal count. + + Filter to PRs whose `createdAt` is after the original eng PR's `mergedAt`. Look in titles for keywords: "rollout", "100%", "delete legacy", "remove flag", "ramp up". + +12. **Slack search:** + Use `mcp__claude_ai_Slack__slack_search_public_and_private` with: + - Query: `<flag>` + - Sort: `timestamp` (newest first) + - Look for messages from the LaunchDarkly bot, eng confirmations of flag state, rollout dates + - Recommended channels to scan in results: `#eng`, `#team-flaky-tests`, `#team-merge-queue`, `#production-notifications`, `#staging-notifications` + + A 0-result Slack search is itself a signal. It counts toward `pending`. + +13. **e2e flag default:** + ``` + gh api repos/trunk-io/trunk2/contents/ts/apps/e2e/flags.json --jq '.content' | base64 -d | jq '.flagValues["<flag>"]' + ``` + A `true` here only confirms it works in tests, not prod. + +14. **Legacy code presence:** + Eng PR bodies often mention a legacy component being preserved (e.g., "When the flag is off, the legacy `<UploadsClient>` renders unchanged"). Extract the legacy name from the eng PR body (regex: `legacy \x60(\w+)\x60` and similar) and search trunk2: + ``` + gh api 'search/code?q=<legacy-name>+repo:trunk-io/trunk2' --jq '.items[].path' + ``` + Presence of the legacy code path in `main` = flag not yet 100% rolled out. + +#### Step E: Classify + +Apply rules in order. First match wins. + +| Condition | Verdict | +|---|---| +| Any referenced eng PR is unmerged or reverted | `blocked` | +| Eng PR state itself was unavailable after retry (state unknown) | `unknown` | +| All eng PRs merged AND no flag found | `live` | +| Slack message confirms flag at 100% prod, OR a follow-up "delete legacy" PR is merged | `live` | +| Slack message dated AFTER the eng PR's `mergedAt` confirms flag on in staging, off in prod | `staged` | +| Eng PR merged, flag exists, no Slack rollout signals, legacy code still present | `pending` | +| Eng PR merged, flag exists, mixed or insufficient signals | `unknown` | + +**Recency rule.** Slack messages from before the eng PR's `mergedAt` describe a state the eng PR may have changed. Treat pre-merge messages as background context only, not as current-state signals. A Slack message must be timestamped after `mergedAt` to count as a positive `live` or `staged` signal. + +### Phase 2: Output + +For each PR with a verdict: + +15. **Console output.** + - Single mode: print full reasoning (eng work, flag, signals checked, suggested next action). + - Sweep mode: one line per PR, sorted by severity (`blocked` → `pending` → `staged` → `unknown` → `live`). End with a summary line. + +16. **PR comment.** + Body template (replace `<...>` placeholders): + ``` + <!-- verify-docs-pr --> + **Verification status (<DATE>): `<verdict>`** + + <verdict-opening-line, see "Verdict messages" below> + + - Eng PR: <links> + - Flag: `<flag-name>` (or "none" if ungated) + - Signals: <bulleted list of rollout signals checked> + + <suggested next action> + ``` + Check for an existing `<!-- verify-docs-pr -->` comment in the PR's `comments` array. If present, edit it via `gh api` (`PATCH /repos/.../issues/comments/<id>` with the new body). Otherwise post a new comment via `gh pr comment <num> --body "<body>" --repo trunk-io/docs2`. + + Do not use em dashes (U+2014) in the comment body. Use periods, commas, or parentheses instead. + +17. **Linear comment.** + Find the linked Linear ticket from the docs PR body. Use `mcp__claude_ai_Linear__list_comments` with the issue ID to find any existing `<!-- verify-docs-pr -->` comment. + - If present, update via `mcp__claude_ai_Linear__save_comment` with the comment ID. + - Otherwise create a new comment with `mcp__claude_ai_Linear__save_comment`. + + Body template: + ``` + <!-- verify-docs-pr --> + Verification status (<DATE>): `<verdict>` + + Docs PR: https://github.com/trunk-io/docs2/pull/<NUM> + + <same body as PR comment, minus the marker> + ``` + + Do not use em dashes (U+2014) in the comment body. Use periods, commas, or parentheses instead. + +18. **PR state action.** + + **Draft flag.** + - `live`: no draft change. + - Anything else: if `isDraft` is `false`, flip to draft via `gh pr ready <num> --undo --repo trunk-io/docs2`. If already draft, no-op. + + **Title prefix.** Adds a visible queue signal so anyone scanning open PRs can see the verdict without opening the PR. `live` PRs get a positive cue, non-`live` PRs get a hold cue. + + Known prefixes managed by this skill: `[ready to merge]`, `[staged]`, `[feature not live]`, `[blocked]`. Treat them as case-sensitive and bracket-anchored. + + Per verdict: + + | Verdict | Title prefix | + |---|---| + | `live` | `[ready to merge]` | + | `staged` | `[staged]` | + | `pending` | `[feature not live]` | + | `blocked` | `[blocked]` | + | `unknown` | none (the verdict is already non-actionable; an extra prefix would be noise) | + + Algorithm: + 1. Read the current title from the PR data fetched in Step A. + 2. Strip any leading known prefix to derive the base title. Match the regex `^\[(ready to merge|staged|feature not live|blocked)\] ` (anchored, single trailing space). + 3. Compose the new title: if the verdict has a prefix, `<prefix> <base>`; otherwise just `<base>`. + 4. If the new title differs from the current title, update via: + ``` + gh pr edit <num> --repo trunk-io/docs2 --title "<new title>" + ``` + If they match, no-op. + + This keeps the title in sync with the verdict on every run. The prefix lifecycle is fully automatic in both directions: a PR that was `pending` and flips to `live` swaps `[feature not live]` for `[ready to merge]`; a PR whose eng work gets reverted swaps `[ready to merge]` for `[blocked]`. + + Do not stack prefixes. If you ever see a title with multiple known prefixes (e.g., a manual edit that added `[blocked][staged]`), treat the leftmost match as the only one to strip and let the next verification settle the rest. + +## Verdict messages + +Per verdict, use this opening line in the comment body: + +| Verdict | Opening line | +|---|---| +| `live` | "Verified: customers can use this. Ready to publish." | +| `staged` | "On in staging only. Re-run after prod rollout." | +| `pending` | "Eng merged but flag off in prod. Hold off." | +| `blocked` | "Eng PR not merged. Hold." | +| `unknown` | "Could not determine state from available signals. Manual check needed." | + +## Sweep parallelization + +When scope > 13 PRs: + +1. Split the PR list into chunks of up to 13 each (4 chunks for 41 PRs). +2. For each chunk, dispatch a sub-agent. Brief the agent to run Phase 1 and Phase 2 for each PR in its chunk and return a structured result: `{pr, verdict, summary_line, comment_posted, linear_updated}`. +3. Run all agents concurrently. +4. Collect results. If an agent fails, list its PRs as "unverified" and suggest re-running them individually with `/verify-docs-pr <num>`. +5. Sort the combined results by severity and print the summary table. + +## Edge cases + +- **PR has no eng refs and no Linear ticket:** verdict = `unknown`. Comment lists what's missing. +- **Eng PR is in a non-trunk2 repo (e.g., analytics-cli, flake-farm):** treat the merge state check the same way. Skip flag detection (only trunk2 has the LD flag patterns). +- **Multiple flags from the same eng PR:** gather signals for all; classify on the most conservative result. +- **Multiple eng PRs with mixed states:** `blocked` wins if any is unmerged. +- **Stacked merges:** If the merged eng PR's body mentions "stacked PRs", "Trunk Merge Queue", or lists multiple `trunk-io/<repo>#NNN` references in its body, recursively run Step C (find feature flags) on each child PR. The merge commit's flat diff may not surface flag definitions added in earlier child PRs of the stack. Real-world example: trunk2#3583 (Test Collections) merged children #3545-#3550 and the docs PR for it (docs#554) was incorrectly classified as `live` because the child PR contents weren't inspected. +- **API timeouts (`gh` or Slack):** retry once. On second failure, set verdict = `unknown` with a note about the unavailable signal. +- **PR already merged:** print "PR #N already merged; skipping" and skip entirely. Do not comment. +- **Docs PR body has no `TRUNK-XXX` reference:** The skill cannot find the linked Linear ticket reliably. Print a warning ("No Linear ticket found in PR body; skipping Linear comment") and proceed without posting to Linear. Do NOT guess at which ticket to post to. + +## Manual validation cases + +When changes ship to this skill, verify all six cases pass: + +1. `/verify-docs-pr 589` → verdict `pending`, references `enableFilteredUploadsPage`. +2. `/verify-docs-pr 534` → verdict `live` (no flag found). +3. `/verify-docs-pr 522` → verdict `live` (pure docs change, no eng PR found). +4. `/verify-docs-pr` → all open PRs classified, summary printed sorted by severity. +5. Run `/verify-docs-pr 589` twice → existing comment is edited, no duplicate posted. +6. Title prefix lifecycle: run on a `pending` PR with no prefix → title gets `[feature not live]` prepended. Re-run with the verdict still `pending` → no further change. Simulate the verdict flipping to `live` → `[feature not live]` is replaced with `[ready to merge]`. Simulate the eng PR being reverted (verdict flips to `blocked`) → `[ready to merge]` is replaced with `[blocked]`. diff --git a/.claude/skills/write-docs/OUTPUTS.md b/.claude/skills/write-docs/OUTPUTS.md new file mode 100644 index 0000000..33e42c1 --- /dev/null +++ b/.claude/skills/write-docs/OUTPUTS.md @@ -0,0 +1,59 @@ +# Output Formats + +Reference for all outputs produced by the write-docs skill. + +## Contents + +- [PR body format](#pr-body-format) +- [Slack post format](#slack-post-format) +- [Report card format](#report-card-format) + +## PR Body Format + +PR title: `[TRUNK-XXXXX] Short descriptive title` (prefix with Linear ticket ID if one exists). + +PR body sections: +- **Summary** — bullet list of changes +- **Linear tickets** — clickable links to all related tickets +- **Engineering authors** — GitHub handles of engineers who built the feature (from trunk2 PRs), for technical accuracy review +- **Context links** — all Slack, Slite, Loom links from the notes +- **Files changed** — list of files created/modified +- **Open questions** — things that could not be confirmed from available context +- **Test plan** — checklist for reviewer (e.g., "check GitBook preview", "verify code example works") + +## Slack Post Format + +Write to `.claude/tmp/<draft-name>/slack.md`. Must be directly copy-pasteable into Slack. + +**MUST use Slack mrkdwn syntax, NOT Markdown:** +- Bold: `*text*` (single asterisks, not double) +- Links: `<URL|display text>` +- Bullets: use the `*` character on a new line (Slack list style) +- No Markdown headers (`##`), links (`[text](url)`), or bold (`**text**`) + +Template: +``` +*[Feature Name] — docs update ready for review* + +[1-2 sentence summary of what changed in the docs.] + +* PR: <GitHub PR URL|#NNN> +* Linear: <Linear ticket URL|TRUNK-XXXXX> + +*Open questions for the team:* +* [list any items needing eng confirmation] +``` + +## Report Card Format + +Append an HTML card to `.claude/tmp/report.html`. If the file does not exist, create it with basic HTML styling. + +Each card includes: +- PR link +- Linear link +- Change type badge +- Changes summary +- Context links +- Related tickets +- Review focus areas +- Open questions diff --git a/.claude/skills/write-docs/OVERLAP-CHECK.md b/.claude/skills/write-docs/OVERLAP-CHECK.md new file mode 100644 index 0000000..b0c7a05 --- /dev/null +++ b/.claude/skills/write-docs/OVERLAP-CHECK.md @@ -0,0 +1,51 @@ +# Duplicate & Overlap Check + +Run these checks before starting any work. Stop and ask the user if any match is found. + +## Step 1: Check for existing PRs/branches from this draft + +1. Derive the expected branch topic from the draft filename (e.g., `flag-as-flaky.md` -> `flag-as-flaky`). Get the username prefix from `git config user.name` (kebab-cased). + +2. Search for open PRs matching the branch: + ```bash + gh pr list --repo trunk-io/docs2 --state open --head "<username>/<topic>" --json number,title,url,headRefName + ``` + Also search by topic keyword: + ```bash + gh pr list --repo trunk-io/docs2 --state open --json number,title,url,headRefName | jq '.[] | select(.headRefName | contains("<topic>"))' + ``` + +3. Check local branches: + ```bash + git branch --list "*<topic>*" + ``` + +4. **If a match is found**: Show the user the existing PR/branch and ask: + - (a) Update the existing PR + - (b) Close it and start fresh + - (c) Skip this draft + + Do NOT proceed until the user responds. + +## Step 2: Check for overlapping PRs from other authors + +1. Read the draft to identify target docs files/product area. + +2. List all open PRs: + ```bash + gh pr list --repo trunk-io/docs2 --state open --json number,title,headRefName,url --limit 50 + ``` + +3. For any PR that looks related (by title or branch name matching the same product area), check file overlap: + ```bash + gh pr view <number> --repo trunk-io/docs2 --json files --jq '[.files[].path]' + ``` + +4. **If overlapping PRs are found**: Show the user the overlapping PR and affected files, then ask: + - (a) Proceed anyway (changes will likely conflict) + - (b) Wait for that PR to merge first + - (c) Skip this draft + + Do NOT proceed until the user responds. + +5. **If no overlaps found**: Continue to Phase 1. diff --git a/.claude/skills/write-docs/README.md b/.claude/skills/write-docs/README.md new file mode 100644 index 0000000..08e3aec --- /dev/null +++ b/.claude/skills/write-docs/README.md @@ -0,0 +1,278 @@ +# write-docs skill + +End-to-end documentation pipeline: raw notes -> reviewed docs PR with Linear tracking. + +Invoked as `/write-docs <input>`. + +## Directory Structure + +``` +write-docs/ +├── SKILL.md # Main instructions (~130 lines, loaded when skill triggers) +├── OVERLAP-CHECK.md # Detailed dupe/overlap check procedure (loaded in Phase 0) +├── OUTPUTS.md # PR body, Slack post, report format specs (loaded in Phases 4-6) +└── README.md # This file (human reference only) +``` + +## How It Works + +The skill uses progressive disclosure: Claude loads SKILL.md when triggered, then reads OVERLAP-CHECK.md and OUTPUTS.md only when it reaches the relevant phase. This keeps the context window lean. + +### Pipeline + +``` +Phase 0 Duplicate & overlap check Stops if a PR/branch already covers this draft +Phase 1 Parse input Read notes file, extract tickets, PRs, context links +Phase 2 Research Linear tickets, Slite PRDs, Slack channels, + published docs, trunk2 PR diffs, gap analysis +Phase 2.5 Sources Write audit trail to .claude/tmp/<draft>/sources.md +Phase 3 Draft Edit existing pages or create new docs files +Phase 4 Branch & PR Stash → branch → commit → push → gh pr create +Phase 5 Linear Create/update ticket, attach links, add relations +Phase 6 Stage Write slack.md, append to report.html +Phase 7 Clean up Restore original branch and stashed changes +``` + +### Research Sources (Phase 2) + +The skill cross-references five sources to build context before writing anything: + +| Source | What it finds | MCP Server | +| ------------------ | ---------------------------------------------------------------------------------------- | --------------------- | +| **Linear** | Ticket descriptions, status, assignees, related tickets | `claude.ai Linear` | +| **Slite** | PRDs, specs, roadmap items, knowledge base articles | `claude.ai Trunk Slite` | +| **Slack** | Team discussions, changelogs, feature context from `#team-flaky-tests`, `#team-merge-queue` | `claude.ai Slack` | +| **Published docs** | Existing documentation pages, hierarchy from `docs.json` | `claude.ai trunk docs` | +| **GitHub** | trunk2 PR diffs, code changes, implementation details | `gh` CLI | + +### Inputs + +| Input type | Example | What happens | +| ----------------- | --------------------------------- | --------------------------------------------------------------- | +| Notes file | `.claude/drafts/flag-as-flaky.md` | Primary mode. Reads file, extracts metadata, researches, writes | +| trunk2 PR numbers | `3187 3177` | Reads PR details via `gh`, extracts Linear IDs | +| Linear ticket IDs | `TRUNK-17633` | Looks up ticket, finds related PRs and context | +| Deploy tag | `v126` | Documents features shipped in a specific release | + +Notes files follow the template at `.claude/drafts/TEMPLATE.md`. + +### Outputs + +**Committed (the PR):** + +- Branch: `<git-username>/<kebab-case-topic>` +- PR title: `[TRUNK-XXXXX] Short descriptive title` +- PR body: summary, Linear links, context links, files changed, open questions, test plan + +**Staged (gitignored, under `.claude/tmp/<draft-name>/`):** + +- `sources.md` — audit trail for reviewers (every source consulted) +- `slack.md` — copy-pasteable Slack announcement (uses Slack mrkdwn, not Markdown) + +**Cumulative (`.claude/tmp/report.html`):** + +- HTML card appended per run — PR link, Linear link, changes summary, open questions + +**Linear:** + +- Ticket created/updated in Docs Maintenance project with `docs` label +- Context links attached (Slack, Slite, Loom, etc.) +- Related engineering tickets linked via `relatedTo` +- Status set to "In Review" + +### Prerequisites + +The skill requires these MCP servers to be connected. Verify with `claude mcp list`: + +- `claude.ai Slack` — for searching team channels +- `claude.ai Trunk Slite` — for PRDs and specs +- `claude.ai trunk docs` — for searching published docs +- `claude.ai Linear` — for ticket management +- GitHub via `gh` CLI (authenticated with access to `trunk-io/trunk2`) + +--- + +## Demo Script + +Walkthrough for demoing `/write-docs` at an all-hands or team meeting. Total runtime: ~5 minutes. + +### Pre-Demo Setup (10 min before) + +#### 1. Clean terminal state + +```bash +cd ~/TRUNK/docs +git checkout main +git pull origin main +git status # should be clean +``` + +#### 2. Verify no leftover demo artifacts + +```bash +git branch --list "*indefinite-monitor-muting*" +gh pr list --repo trunk-io/docs2 --state open --json number,title,headRefName | jq '.[] | select(.headRefName | contains("indefinite-monitor-muting"))' + +# If either returns results, clean up: +# gh pr close <number> --repo trunk-io/docs2 --delete-branch +# git branch -D sam-gutentag/indefinite-monitor-muting +``` + +#### 3. Verify draft and clear previous outputs + +```bash +cat .claude/drafts/indefinite-monitor-muting.md +rm -rf .claude/tmp/indefinite-monitor-muting/ +``` + +#### 4. Verify MCP servers + +```bash +claude mcp list +``` + +All five should show connected (see [Prerequisites](#prerequisites) above). + +#### 5. Start Claude Code + +```bash +claude +``` + +Wait for it to load. Verify `/write-docs` is recognized. + +#### 6. Terminal setup + +- Font size: 18-20pt for readability +- Terminal width: full screen, max 120 chars +- Dark theme for projector visibility + +### The Demo + +#### ACT 1: The Problem (30 seconds) + +**[Speaking to audience, terminal visible but idle]** + +> "Quick show of hands — who's had to write documentation for a feature someone else built? Yeah. It's not the writing that's painful, it's the context-gathering. You're reading Slack threads, digging through PRs, searching Linear, figuring out which docs pages need updating. Then you do the actual writing. Then you create a branch, open a PR, update the ticket, post in Slack. That's 30 minutes for something that should take 5." + +> "We built a Claude Code skill that does all of that from a single command. Let me show you." + +#### ACT 2: Show the Input (30 seconds) + +**[In Claude Code, type:]** + +``` +cat .claude/drafts/indefinite-monitor-muting.md +``` + +**[Let the file scroll. Point out key sections:]** + +> "This is a notes file — it's what an engineer or PM drops into our drafts folder. It has the feature name, links to the PRs that shipped it, and which docs pages need updating. It's rough and that's fine — it's input, not output." + +> "Notice there's no Linear ticket yet, no branch, no PR. Just notes." + +#### ACT 3: Run the Skill (2-3 minutes) + +**[Type the command:]** + +``` +/write-docs .claude/drafts/indefinite-monitor-muting.md +``` + +**[Narrate each phase as it runs. Don't rush — let the audience watch the tool calls.]** + +When you see `gh pr list` calls: +> "Phase zero — it's checking whether someone already opened a PR for this, or if another PR touches the same docs files." + +When you see `mcp__claude_ai_Linear` calls: +> "Now it's researching. Starts with Linear to get ticket context..." + +When you see Slite or Slack MCP calls: +> "It's searching our Slite knowledge base for PRDs and checking the #team-flaky-tests Slack channel for recent discussion. This is context that used to take 10 minutes of manual digging." + +When you see `gh pr view` or `gh pr diff` calls: +> "Reading the trunk2 PR to see what code actually shipped." + +When you see a file being written to `.claude/tmp/`: +> "That's the sources file — an audit trail so reviewers can trace any claim back to its source." + +When you see `Edit` tool calls on docs files: +> "Now it's writing the actual docs. It read the existing pages first to match our tone and structure." + +When you see `git` commands: +> "Creating a branch, committing, pushing..." + +When you see `gh pr create`: +> "And there's the PR." + +When you see Linear calls: +> "Creating a Linear ticket, attaching the PR link, context URLs, and linking related engineering tickets." + +#### ACT 4: Show the Outputs (1 minute) + +**[Claude will output a summary with links. Click through each one:]** + +**GitHub PR** — open in browser: +> "The title has the Linear ticket ID. The body has open questions — things it couldn't confirm from the available context. It flags what needs human verification instead of guessing." + +**Linear ticket** — open in browser: +> "Docs Maintenance project, assigned to me, status In Review. It attached the PR link, the Slack threads it found, and any Slite docs it consulted." + +**[Show staged outputs:]** + +``` +cat .claude/tmp/indefinite-monitor-muting/slack.md +cat .claude/tmp/indefinite-monitor-muting/sources.md +``` + +> "A copy-pasteable Slack post and a full sources audit trail." + +#### ACT 5: Wrap Up (30 seconds) + +> "From one command and a rough notes file: a docs PR, a Linear ticket, an audit trail, and a team notification. About 3 minutes." + +> "The skill searches five systems — Linear, Slite, Slack, our published docs, and GitHub PRs — to build context before writing a single line. Every PR still gets human review." + +### Q&A + +**"What if the docs it writes are wrong?"** +> "Every PR gets human review. The skill flags open questions explicitly. It's doing the 80%, a human does the last 20%." + +**"Can this work for other repos?"** +> "The skill is specific to our docs workflow, but the pattern is portable. You write a SKILL.md describing the pipeline and Claude Code follows it." + +**"How long did it take to build?"** +> "About 130 lines of instructions plus two reference files. The iteration was the slow part — maybe 2-3 sessions." + +**"What if it creates a PR that conflicts with someone else's work?"** +> "Phase 0 catches that. It checks every open PR for file-level overlap before doing any work." + +**"Does it handle new pages or just updates?"** +> "Both. New pages get created and added to docs.json. Updates edit existing files in place." + +**"How does it know what's in Slite and Slack?"** +> "We connected them as MCP servers — same protocol as Linear. The skill searches them during research, and knows to check #team-flaky-tests and #team-merge-queue for product context." + +### Post-Demo Cleanup + +```bash +gh pr close <PR_NUMBER> --repo trunk-io/docs2 --delete-branch +git checkout main +git branch -D sam-gutentag/indefinite-monitor-muting 2>/dev/null +rm -rf .claude/tmp/indefinite-monitor-muting/ +``` + +### Dry Run Checklist + +Do a full dry run the day before. Clean up everything so the live demo starts fresh. + +- [ ] Draft file exists at `.claude/drafts/indefinite-monitor-muting.md` +- [ ] No existing branch `sam-gutentag/indefinite-monitor-muting` +- [ ] No existing PR for indefinite-monitor-muting +- [ ] No existing `.claude/tmp/indefinite-monitor-muting/` directory +- [ ] Claude Code starts cleanly and recognizes the skill +- [ ] MCP servers are responding (Linear, GitBook, Slack, Slite) +- [ ] Terminal font size is readable from the back of the room +- [ ] You've timed the run — should be 2-4 minutes +- [ ] You know where to find the PR and Linear links in the output +- [ ] Linear "Docs Maintenance" project view is open in a browser tab diff --git a/.claude/skills/write-docs/SKILL.md b/.claude/skills/write-docs/SKILL.md new file mode 100644 index 0000000..7ac1018 --- /dev/null +++ b/.claude/skills/write-docs/SKILL.md @@ -0,0 +1,150 @@ +--- +name: write-docs +description: Process notes, Slack threads, Slite docs, or trunk2 deploy context into documentation changes. Creates a branch, edits docs, opens a PR, and updates Linear. Use when given a notes file from .claude/drafts/, trunk2 PR numbers, Linear ticket IDs, a deploy tag, Slite doc links, or when the user says "write docs", "document this", or "process drafts". +allowed-tools: Bash(git *), Bash(gh pr *), Bash(gh issue *), Bash(gh api *), Bash(trunk fmt *), Bash(trunk check *), Read, Write, Edit, Glob, Grep, mcp__claude_ai_Linear__get_issue, mcp__claude_ai_Linear__list_issues, mcp__claude_ai_Linear__list_projects, mcp__claude_ai_Linear__save_issue, mcp__claude_ai_Linear__save_comment, mcp__claude_ai_Linear__create_attachment, mcp__claude_ai_trunk_docs__searchDocumentation, mcp__claude_ai_Trunk_Slite__*, mcp__claude_ai_Slack__*, WebFetch +--- + +# Writing Docs + +Turn raw notes, Slack pastes, and PR references into reviewed docs PRs with full Linear tracking. + +## Contents + +- [Inputs](#inputs) +- [Products](#products) +- [Workflow](#workflow) +- [Guidelines](#guidelines) +- [Batch processing](#batch-processing) + +## Inputs + +The user provides any combination of: +- **Notes file** (e.g., `.claude/drafts/my-feature.md`) — primary input. Template: `.claude/drafts/TEMPLATE.md` +- **trunk2 PR numbers** — PRs from `trunk-io/trunk2` +- **Linear ticket IDs** — `TRUNK-NNNNN` references +- **Deploy tag** (e.g., `v126`) — features shipped in a release +- **Slite doc links** — PRDs, specs, or knowledge base articles from Slite +- **Context links** — Slack threads, Loom videos, Google Docs, etc. +- **Specific docs page** — if the user knows what needs changing + +## Products + +Docs are organized by product area: +- **Merge Queue** — `/merge-queue/` +- **Flaky Tests** — `/flaky-tests/` +- **CI Autopilot** — `/ci-autopilot/` +- **Code Quality** — `/code-quality/` +- **Setup & Administration** — `/setup-and-administration/` + +## Workflow + +Copy this checklist and track progress: + +``` +Progress: +- [ ] Phase 0: Duplicate & overlap check +- [ ] Phase 1: Parse input +- [ ] Phase 2: Research (Linear, Slite, Slack, docs, PRs) +- [ ] Phase 2.5: Write sources audit file +- [ ] Phase 3: Draft documentation +- [ ] Phase 4: Branch, commit, PR +- [ ] Phase 5: Update Linear +- [ ] Phase 6: Stage outputs (Slack post, report) +- [ ] Phase 7: Clean up and summarize +``` + +### Phase 0: Duplicate & Overlap Check + +Before doing any work, verify no existing PR already covers this draft. See [OVERLAP-CHECK.md](OVERLAP-CHECK.md) for the detailed check procedure. + +If a match or overlap is found, stop and ask the user how to proceed before continuing. + +### Phase 1: Parse and Understand + +1. If a notes file was provided, read it and extract: feature name, Linear ticket IDs, GitHub PR URLs, context links, product area, change type, and key details. +2. If trunk2 PR numbers or a deploy tag were provided instead, use `gh pr view <number> --repo trunk-io/trunk2` to get details and extract Linear ticket IDs. + +### Phase 2: Research + +**Code is law.** Actual source code and PR diffs are the authoritative source of truth. Slite specs, PRDs, Slack threads, and other planning docs provide context, intent, and examples — but when they conflict with what the code does, the code wins. Only document what is actually implemented. + +3. **Linear tickets**: Use `mcp__claude_ai_Linear__get_issue` for each ticket ID. Search by feature name for related engineering tickets. +4. **Slite docs**: Search Slite for PRDs, specs, roadmap items, and knowledge base articles related to the feature. Use feature name and product area as search terms. Retrieve relevant docs for product intent, requirements, and decisions. +5. **Slack channels**: Search relevant Slack channels for recent discussion, changelogs, and context: + - `#team-flaky-tests` — Flaky Tests product discussions + - `#team-merge-queue` — Merge Queue product discussions + - Search by feature name, ticket ID, or key terms from the notes +6. **Existing docs**: Use `mcp__claude_ai_trunk_docs__searchDocumentation`, Glob, and Grep. Read `docs.json` for the site navigation and group structure. +7. **trunk2 PR diffs** (if available): `gh pr diff <number> --repo trunk-io/trunk2 --name-only`, then read key files. When possible, also read the current source on `main` via `gh api` to confirm the latest state. +8. **Gap analysis**: Compare existing docs vs. what the code implements. Cross-reference planning docs (Slite, Slack) against code to identify discrepancies — features described in specs but not yet implemented should be flagged as open questions, not documented as existing functionality. + +### Phase 2.5: Generate Sources File + +9. Write `.claude/tmp/<draft-name>/sources.md` with all Linear tickets, GitHub PRs, Slite docs, Slack threads, existing docs, code references, and context links found during research. This is the reviewer audit trail. Must include: + - A **"Code-Confirmed Details"** section listing metric names, endpoint paths, auth mechanisms, etc. as they exist in the actual codebase + - A **"Differences: Code vs. Planning Docs"** table highlighting any discrepancies between what planning docs describe and what the code implements + - An **"Open Questions"** section for anything that could not be confirmed from code + +### Phase 3: Draft Documentation + +10. Write or edit documentation: + - **Match tone and structure** of existing Trunk docs — read nearby files first + - **New pages**: write full content; **Updates**: edit in place; **Explainers**: add to relevant existing page + - Update `docs.json` if adding new pages (insert the page path, without the `.mdx` extension, into the relevant `groups[].pages` array) + - Lead with user benefit, not implementation details + - Use present tense, include practical examples + - Don't mention internal systems (ClickHouse, Prisma, SST, Lambda) + +### Phase 4: Branch, Commit, and PR + +11. **Branch**: From `main`. Name: `<git-username>/<kebab-case-topic>` (username from `git config user.name`, kebab-cased). Stash unrelated changes first. +12. **Commit**: Stage changed files. Include `Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>`. +13. **Identify engineering authors**: Before creating the PR, look up who built the feature. For every `trunk-io/trunk2` PR linked in the Linear ticket or found during research: + - Fetch the PR via `gh pr view <number> --repo trunk-io/trunk2 --json author` + - Extract the author's GitHub handle + - Collect all unique authors across all linked PRs — list every one, do not filter by contribution size + - Include all authors in the PR body under an "Engineering authors" section so Sam can tag them for technical accuracy review + + **Known Trunk team GitHub handle reference** (fallback if API is unavailable): + + | Name | GitHub handle | + |------|--------------| + | Phil Vendola | @pvendola | + | Tyler Jang | @tyler-jang | + | Ben Cook | @bwcook | + | Alexander Graebe | @alexgraebe | + | Ventsi Tsachev | @ventsislavtsachev | + + If you cannot determine a handle confidently, write `[unknown — check trunk2 PR: <url>]`. + +14. **Push and PR**: `gh pr create --draft` with structured body. **Always create as a draft** — Sam reviews every PR manually before marking it ready. See [OUTPUTS.md](OUTPUTS.md) for PR body format. Include the engineering authors list in the PR body. +15. **Request reviewers**: After creating the PR, add engineering authors as reviewers using `gh pr edit <number> --add-reviewer <handle1>,<handle2>`. This works even on draft PRs — reviewers see the PR and can leave early feedback. If a handle lookup fails, note it in the PR body for Sam to add manually. + +### Phase 5: Update Linear + +16. Create or update the docs ticket — add PR link, context links, change summary. Set status to "In Review". If no ticket exists, create one in Trunk Engineering with `docs` label. +17. Attach all context links (Slack, Slite, Loom) as attachments with descriptive titles. +18. Add `relatedTo` relations for every related engineering ticket found during research. + +### Phase 6: Stage Outputs + +19. **Slack post**: Write to `.claude/tmp/<draft-name>/slack.md`. See [OUTPUTS.md](OUTPUTS.md) for Slack formatting rules. +20. **Report**: Append an HTML card to `.claude/tmp/report.html`. See [OUTPUTS.md](OUTPUTS.md) for report format. + +### Phase 7: Clean Up + +21. Return to original branch, restore stashed changes. +22. Show the user: branch name, PR link, Linear ticket link, files changed, open questions, staged output file paths. + +## Guidelines + +- **One notes file = one PR.** Flag multi-topic drafts and ask how to split. +- **Preserve the notes file** — never delete or modify it. +- **Ask before guessing** — list specific questions rather than making assumptions. +- **Always include full PR URLs** in Linear comments and descriptions. +- **Match existing style** — read adjacent docs before writing. +- **Prioritize accuracy** — flag inferred vs. confirmed items in open questions. + +## Batch Processing + +Process each notes file in `.claude/drafts/` one at a time. Each gets its own branch, PR, Linear ticket, and `.claude/tmp/<draft-name>/` directory. The skill handles git stash/restore between runs. diff --git a/.claude/tmp/.gitkeep b/.claude/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 From 24838bc94ad64f76d18caef4cd9c56405112e07d Mon Sep 17 00:00:00 2001 From: Sam Gutentag <1404219+samgutentag@users.noreply.github.com> Date: Wed, 13 May 2026 00:16:59 -0700 Subject: [PATCH 2/4] chore(claude): consolidate to 4 skills (drop draft-docs, merge review/docs-review -> docs-research) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes: - skills/draft-docs/ — write-docs already accepts raw trunk2 refs - skills/review-docs/ — drop pre-PR local-diff review (rely on trunk check + PR review instead) - skills/docs-review/ — fold into the new docs-research skill - skills/verify-docs-pr/ DESIGN.md — historical migration artifact Adds: - skills/docs-research/ — upstream audit skill that runs before /outline-docs or /write-docs. Identifies gaps in existing coverage, recommends placement for new content, prevents duplicated effort. Updates: - .claude/README.md — new mental model (research / write / verify), skill reference rewritten, drafts section clarified as optional batch scaffolding - .claude/settings.json — Skill() permissions match new skill set - skills/outline-docs/SKILL.md — post-checklist no longer references /review-docs; points to trunk check and normal PR review flow Final skill set: docs-research, outline-docs, write-docs, verify-docs-pr, plus the doc-researcher subagent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --- .claude/README.md | 96 +++----- .claude/settings.json | 4 +- .claude/skills/docs-research/SKILL.md | 119 ++++++++++ .claude/skills/docs-review/SKILL.md | 133 ----------- .claude/skills/draft-docs/SKILL.md | 156 ------------- .claude/skills/outline-docs/SKILL.md | 4 +- .claude/skills/review-docs/SKILL.md | 298 ------------------------ .claude/skills/verify-docs-pr/DESIGN.md | 230 ------------------ 8 files changed, 160 insertions(+), 880 deletions(-) create mode 100644 .claude/skills/docs-research/SKILL.md delete mode 100644 .claude/skills/docs-review/SKILL.md delete mode 100644 .claude/skills/draft-docs/SKILL.md delete mode 100644 .claude/skills/review-docs/SKILL.md delete mode 100644 .claude/skills/verify-docs-pr/DESIGN.md diff --git a/.claude/README.md b/.claude/README.md index 595f632..67ffb40 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -9,19 +9,27 @@ Claude Code configuration for the Trunk docs (Mintlify) repo. Skills are documen ├── agents/ # Subagent definitions (spawned via the Agent tool) │ └── doc-researcher.md # Gathers Linear/PR/Slite/docs context before writing ├── skills/ # User-invoked workflows (triggered via /skill-name) +│ ├── docs-research/ # Audit existing docs to find gaps and placement │ ├── outline-docs/ # Scaffold a new docs page from scratch -│ ├── write-docs/ # Notes → PR pipeline (9 phases) -│ ├── review-docs/ # Pre-PR review of local changes -│ ├── draft-docs/ # Generate notes files from trunk2 context -│ ├── verify-docs-pr/ # Verify a docs PR's feature is live in prod -│ └── docs-review/ # Audit live docs.trunk.io pages -├── drafts/ # Input notes files for write-docs +│ ├── write-docs/ # Trunk2 context → PR pipeline (9 phases) +│ └── verify-docs-pr/ # Verify a docs PR's feature is live in prod +├── drafts/ # Optional input notes files for write-docs │ └── TEMPLATE.md # Scaffold for new draft notes ├── tmp/ # Scratch outputs (gitignored) ├── settings.json # Shared permissions (committed) └── settings.local.json # Per-machine overrides (gitignored) ``` +## Mental model + +The flow is **research → write → verify**: + +| Phase | Skill | +|---|---| +| Research | `/docs-research` (existing-coverage audit) + `doc-researcher` agent (Linear/PR/Slite context) | +| Write | `/outline-docs` (blank-page scaffold) or `/write-docs` (full PR pipeline) | +| Verify | `/verify-docs-pr` (is the feature actually live in prod?) | + ## Agents vs. Skills **Agents** (`agents/`) are autonomous subprocesses spawned by the `Agent` tool. They run with a specific model and limited toolset, do their work, and return results to the parent conversation. Use agents for parallelizable research tasks. @@ -35,6 +43,23 @@ Claude Code configuration for the Trunk docs (Mintlify) repo. Skills are documen ## Skill reference +### `/docs-research` + +**When:** Before writing a new doc (or right after a deploy) to audit existing coverage, find gaps, and decide where new content should live. + +**What it does:** Five-phase audit: +1. Maps the relevant product-area group in `docs.json` and lists candidate `.mdx` files +2. Searches existing docs (hosted search + local grep) for the topic and synonyms +3. Classifies each hit as `covered`, `partial`, or `adjacent` +4. Recommends placement for new content — defaulting to extending an existing page over creating a new one +5. Generates a structured report with existing coverage, gaps, suggested placement, and cross-links to add + +**Inputs:** A topic / feature name / product area. Optional: a feature description, PR body, or Linear ticket. `full` for a site-wide audit. + +**Outputs:** A research report that feeds directly into `/outline-docs` or `/write-docs`. + +--- + ### `/outline-docs` **When:** Starting a brand-new docs page from scratch with no prior spec. @@ -59,40 +84,11 @@ Claude Code configuration for the Trunk docs (Mintlify) repo. Skills are documen 5. Updates Linear and writes a Slack post draft 6. Invokes `/verify-docs-pr` to check whether the feature is actually live in prod -**Inputs:** A `.claude/drafts/<topic>.md` file (preferred), or raw PR/ticket references. +**Inputs:** Trunk2 PR refs, Linear ticket IDs, a deploy tag, Slite links, or an optional `.claude/drafts/<topic>.md` file for batch workflows. **Outputs:** Branch, draft PR, Linear update, Slack post in `tmp/<topic>/slack.md`. -**Discipline:** One draft = one PR. Always opens as draft for human review. - ---- - -### `/review-docs` - -**When:** Docs changes are ready locally and you want a structural review before opening a PR. - -**What it does:** Five-phase review: -1. Identifies changed `.mdx` files via `git diff main...HEAD` -2. **Audits `docs.json` redirects** for stale or missing entries when files have been moved or deleted -3. Runs `trunk fmt` and `trunk check` -4. Reads sibling pages in the same product area to establish a style baseline -5. Reviews each file for repetition, structural completeness, logic errors, and Trunk style consistency - -**Inputs:** Optional file path (single-file mode), otherwise the full branch diff. - -**Outputs:** A structured report. Offers to apply mechanical fixes directly. - ---- - -### `/draft-docs` - -**When:** After a trunk2 deploy, or anytime you want to pre-populate drafts from PR / Linear / deploy-tag context. - -**What it does:** Reads trunk2 PRs, Linear tickets, and Slack/Slite context. Classifies which PRs need docs. For each, generates a `<featurename>.md` notes file in `.claude/drafts/` containing: feature summary, target pages, gap analysis, code references, and "what changed" prose. - -**Inputs:** A deploy tag, `latest`, PR numbers, Linear IDs, or a freeform feature description. - -**Outputs:** One notes file per documentable feature, written directly to `.claude/drafts/` for processing by `/write-docs`. +**Discipline:** One feature = one PR. Always opens as draft for human review. --- @@ -110,20 +106,6 @@ Claude Code configuration for the Trunk docs (Mintlify) repo. Skills are documen --- -### `/docs-review` - -**When:** Auditing existing docs.trunk.io pages for accuracy, naming consistency, AI-readability, or running a site-wide quality pass. - -**What it does:** Reads local `.mdx` files and reviews them for: factual accuracy against the code, naming consistency, structural issues, and AI-friendliness (clear hierarchy, scannable headers, no ambiguous pronouns). Reports findings. - -**Inputs:** A page path or glob, a product area name, `full` for a site-wide pass, or no arg to ask. - -**Outputs:** A structured audit report. - -**vs. `/review-docs`:** `/review-docs` is pre-PR diff review on your local branch. `/docs-review` audits already-published pages for ongoing quality. - ---- - ## Agent reference ### `doc-researcher` @@ -142,12 +124,12 @@ Subagent (not a slash command). Spawned via the `Agent` tool with `subagent_type ## Drafts -`drafts/` holds input notes files that feed into `/write-docs`. Each file represents one documentable feature; processing it produces one PR. +`drafts/` is optional scratch space for batch workflows. `/write-docs` accepts trunk2 PR / Linear / deploy-tag refs directly, so most invocations skip drafts entirely. Use a draft file when you want to curate notes by hand before processing — for example, post-deploy when several features ship and you want to triage which ones need docs first. -- **`TEMPLATE.md`** — scaffold for new drafts. Don't edit this; copy it. -- **`<featurename>.md`** — one per feature. Generated either manually or by `/draft-docs`. +- **`TEMPLATE.md`** — scaffold for new drafts. Don't edit; copy it. +- **`<featurename>.md`** — one per feature. Author manually, then run `/write-docs <featurename>`. -Drafts are inputs, not outputs. Never modify or delete a draft mid-pipeline — it's the source of truth for what was requested. +If you do use a draft file, treat it as input: never modify or delete it mid-pipeline. ## Tmp @@ -168,11 +150,9 @@ Everything under `tmp/` is gitignored except `.gitkeep`. Generic versions of these skills live in `~/Developer/gutils/claude-code/skills/trunk/` (the canonical personal-use copy). The versions in this directory are **project-tuned for Mintlify**: +- `docs-research` — audits `docs.json` groups, reads `.mdx` files, defaults to extending existing pages over creating new ones - `outline-docs` — uses `.mdx`, updates `docs.json` nav, generates Mintlify callouts -- `review-docs` — audits `docs.json` redirects, expects Mintlify callout components, no GitBook syntax - `write-docs` — updates `docs.json` instead of `summary.md` - `verify-docs-pr` — hardcoded to `trunk-io/docs2` -- `draft-docs` — outputs directly to `.claude/drafts/` (no copy step needed) -- `docs-review` — reviews `.mdx` files When updating one of these skills, decide whether the change is generic (also update gutils) or project-specific (only update here). Intentional drift between the two is fine and expected. diff --git a/.claude/settings.json b/.claude/settings.json index 68856a3..b087a85 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -87,10 +87,8 @@ "Skill(write-docs)", "Skill(outline-docs)", - "Skill(review-docs)", - "Skill(draft-docs)", + "Skill(docs-research)", "Skill(verify-docs-pr)", - "Skill(docs-review)", "Skill(schedule)" ] } diff --git a/.claude/skills/docs-research/SKILL.md b/.claude/skills/docs-research/SKILL.md new file mode 100644 index 0000000..613fda6 --- /dev/null +++ b/.claude/skills/docs-research/SKILL.md @@ -0,0 +1,119 @@ +--- +name: docs-research +description: >- + Audit the existing Mintlify docs site to inform new documentation work. + Run before /outline-docs or /write-docs to (1) identify gaps in coverage, + (2) recommend placement for new content, and (3) prevent duplicated effort + by surfacing existing pages that already touch the topic. +allowed-tools: Read, Glob, Grep, Bash(rg *), Bash(jq *), mcp__claude_ai_trunk_docs__searchDocumentation +--- + +# Docs Research + +Survey the existing Trunk docs site before writing new content. Produces a structured report covering existing coverage, gaps, recommended placement, and cross-link opportunities. + +## Inputs + +The user provides: + +- **A topic, feature name, or product area** — required scope (e.g., "auto-quarantine override", "Merge Queue health page", "flaky test history timeline") +- **Optional context** — a feature description, PR body, Linear ticket, or notes file that describes what's about to be documented +- **`full`** — site-wide audit mode (slower; reads every page in every product area) + +If no scope is provided, ask what to research before proceeding. + +## Workflow + +Follow these phases in order. + +### Phase 1: Map the relevant area + +1. Read `docs.json`. Identify which top-level group(s) the topic belongs to (e.g., `flaky-tests`, `merge-queue`, `setup-and-administration`). +2. List all `.mdx` files in the relevant group directories via `Glob`. +3. For each candidate page, read its frontmatter (`title`, `description`) and section headers (lines starting with `##` or `###`) to build a lightweight coverage map. Don't read full bodies yet. + +In `full` mode, skip the topic-narrowing and walk every group. + +### Phase 2: Search for topical overlap + +For the given topic and its likely synonyms: + +1. **Hosted docs search** — `mcp__claude_ai_trunk_docs__searchDocumentation` for the topic, the feature name, and 2-3 likely synonyms. +2. **Local grep** — `Grep` across the relevant `.mdx` files for the same terms. +3. **Hit list** — for each match, capture: + - File path + - Section header containing the match + - One-line summary of what that section currently says about the topic (read just enough of the section body to summarize) + +### Phase 3: Classify each hit + +For every existing page that touches the topic, label it: + +| Label | Meaning | +|---|---| +| `covered` | Topic is fully documented here; new content would duplicate | +| `partial` | Topic is mentioned briefly; could be expanded in-place rather than creating a new page | +| `adjacent` | Related but distinct topic; new content should cross-link, not merge | + +Anything mentioned in the input scope but absent from the hit list is a **gap**. + +### Phase 4: Recommend placement + +For each gap or partial-coverage finding, propose where the new content should live: + +1. Identify the most relevant product-area group in `docs.json`. +2. Decide between **extending an existing page** (default) and **creating a new page** (only if the topic clearly doesn't fit any existing page's scope). +3. If creating new, suggest 2-3 specific placement options with rationale (e.g., "new file under `flaky-tests/configuration/auto-quarantine-overrides.mdx`, between `auto-quarantine.mdx` and `quarantine-history.mdx` to maintain the configuration → behavior → audit ordering"). +4. Note natural cross-links — pages that should reference the new content once it exists. + +**Bias:** extending an existing page beats creating a new one. Only recommend a new page when the topic warrants its own scope and the existing pages would feel bloated if extended. + +### Phase 5: Generate report + +Print a structured report. Format: + +``` +Docs Research — <topic> +======================================== + +## Existing coverage + +| Page | Coverage | What it says | +|---|---|---| +| <path>.mdx | covered | <one-line summary> | +| <path>.mdx | partial | <one-line summary; what's missing> | +| <path>.mdx | adjacent | <one-line summary; how it relates> | + +## Gaps +- <specific aspect> — not covered anywhere +- <specific aspect> — not covered anywhere + +## Recommended placement +1. **Extend** `<existing-page>.mdx` — <rationale> +2. **New page** at `<path>.mdx` — <rationale, including where it sits in the nav> + +## Cross-links to add +- `<existing-page>` → `<new-content>` +- `<existing-page>` → `<new-content>` + +## Suggested next step +- /outline-docs to scaffold <X> at <path> +- /write-docs <PR-or-ticket-ref> targeting <path> +- (no action — topic is already well covered) +``` + +In `full` mode, replace the per-topic structure with a per-group coverage matrix. + +## When to use + +- **Before `/outline-docs`** — confirm a new page is needed and identify the right path +- **Before `/write-docs`** — surface existing content the new docs should reference or replace +- **After a deploy** — spot gaps where shipped features are undocumented +- **For periodic audits** — run in `full` mode to find stale, duplicated, or thin coverage across product areas + +## Guidelines + +- **Code is law.** Prefer canonical sources (trunk2 PRs, code, official Mintlify docs) over Slack speculation when classifying coverage. +- **Be specific.** "Auto-quarantine is mentioned" is useless. "Auto-quarantine is mentioned in `flaky-tests/configuration.mdx` Phase 3 as a one-liner; behavior details and override flow are missing" is what we want. +- **Default to extending, not creating.** Three thin pages on adjacent topics is worse than one well-organized page. Only recommend a new page when the topic deserves its own scope. +- **Surface dependents.** When a page changes location or scope (in `full` mode audits), note any cross-links from other pages that would need updating. diff --git a/.claude/skills/docs-review/SKILL.md b/.claude/skills/docs-review/SKILL.md deleted file mode 100644 index 2dfdd46..0000000 --- a/.claude/skills/docs-review/SKILL.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -name: docs-review -description: Review and audit docs.trunk.io pages for accuracy, naming consistency, structural issues, and AI-readability. Use when auditing existing docs, reviewing docs PRs, or running a site-wide quality pass. ---- - -Review Trunk documentation for accuracy, consistency, and structure. Operates as a senior technical writer auditing docs.trunk.io. - -The audience is two groups simultaneously: human developers integrating Trunk into their workflows, and AI coding agents (Claude Code, Cursor, Copilot) that ingest documentation to surface tools autonomously. Both are first-class readers. - -## Core Beliefs - -- **Accuracy over completeness.** A shorter, correct doc beats a longer, stale one. -- **The reader is mid-task.** Lead with what they need to do, not background. -- **AI-readability is not dumbed-down.** Consistent structure, explicit vocabulary, zero ambiguity. This also makes docs better for humans. -- **Naming conventions are load-bearing.** Inconsistent names create bugs in AI reasoning and human mental models. -- **Obsolete content is actively harmful.** A doc that was accurate 18 months ago and hasn't been updated is worse than no doc. - -## Canonical Naming Conventions - -Enforce without exception. Flag any deviation. - -| Concept | Correct | Never Use | -|---|---|---| -| Product name | Trunk | trunk (lowercase in prose) | -| CLI tool | Trunk CLI | `trunk` CLI, the CLI | -| Merge Queue product | Trunk Merge Queue | merge queue (unbranded), MQ | -| Flaky test detection | Trunk Flaky Tests | flaky test detection, CI Autopilot (DEPRECATED) | -| Code quality / linter tool | Trunk Code Quality | Trunk Check, trunk check | -| Config file | `.trunk/trunk.yaml` | trunk.yaml (without path), `.trunk/config` | -| MCP server | Trunk MCP | trunk MCP server (lowercase T) | -| Docs site | docs.trunk.io | Trunk docs, the docs | -| GitHub Actions integration | Trunk's GitHub Actions | Trunk GHA | - -Product feature names are Title Case as proper nouns. Lowercase when used generically ("the merge queue held 12 PRs"). - -## Formatting Standards - -### Page Structure (every doc page must follow this order) - -1. **H1 title** -- noun phrase, not a sentence. "Merge Queue Configuration" not "How to Configure the Merge Queue" -2. **One-sentence summary** -- plain text, no bold, immediately after H1. Answers: what is this and why does it exist? -3. **Prerequisites block** (if applicable) -- bulleted, linked, before procedural content -4. **Body** -- organized by H2s describing tasks or concepts, not document structure ("## Connect Your Repo" not "## Setup Section") -5. **Troubleshooting** (if applicable) -- H2, at the bottom, before reference tables -6. **Reference tables** (if applicable) -- last section, clearly labeled - -### Writing Rules - -- Second person ("you", "your repo"). Never first person plural ("we recommend", "our system") -- Active voice only. "Trunk detects the conflict" not "the conflict is detected" -- One idea per sentence. Max 25 words per sentence in procedural steps -- Imperative mood for steps: "Run `trunk merge enable`" not "You should run..." -- No filler openers: never start a section with "In this guide", "Overview", or "Introduction" -- Avoid: leverage, utilize, seamlessly, robust, powerful, streamline, cutting-edge, delve, nuanced, ensure (use "make sure"), simply (delete it) -- Oxford comma always - -### Code Blocks - -- Every CLI command in its own fenced code block with language tag (`bash`, `yaml`, `json`, etc.) -- Realistic, non-placeholder values in examples where possible -- Unavoidable placeholders use `<YOUR_VALUE>` format (angle brackets, screaming snake case) -- YAML examples must be complete enough to copy-paste -- Show minimum viable config first, extended options separately - -### Admonitions - -- `:::note` -- neutral extra context -- `:::tip` -- genuine shortcut or best practice (max 1 per page) -- `:::warning` -- gotcha that will cause a real failure -- `:::danger` -- destructive or irreversible action -- Never use admonitions as a substitute for writing the thing in prose - -## AI Agent Optimization Rules - -1. **Every page must have machine-readable frontmatter** with at minimum: `title`, `description` (<=160 chars, plain text), and `tags` (product area + action type, e.g., `[merge-queue, configuration]`) -2. **First 150 words of every page must be self-contained.** An AI reading only the first chunk should understand what the tool does and what problem it solves -3. **Avoid pronouns with ambiguous referents.** Every "it", "this", "they" must have an unambiguous antecedent in the same paragraph -4. **All configuration keys must be documented as a table**, not prose, with columns: `key`, `type`, `default`, `description` -5. **Code examples must be syntactically complete.** No `...` ellipsis in YAML/JSON unless explicitly labeled as a partial snippet -6. **Cross-references use absolute doc paths**, not "see the section above" or "as mentioned earlier" -7. **Tool capabilities must be stated explicitly.** Don't imply what Trunk can do. "Trunk Merge Queue supports parallel queues across multiple branches" is parseable. "You can also do more complex setups" is not. - -## Review Workflow - -When invoked, proceed in this order. Complete each phase before starting the next. - -### Phase 1 -- Inventory & Accuracy Audit - -For each page in scope: -- Read the current doc -- Cross-reference against source code, changelog, or API surface -- Flag each claim as: correct, stale, incorrect, or unverifiable -- Note the source of truth used for each flag (file path, PR, changelog entry) -- Do not suggest rewrites yet. Inventory only. - -Output: a structured audit table per page, grouped by product area. - -### Phase 2 -- Structural Analysis - -Across the full site (or scoped section): -- Identify duplicate content (same concept in multiple places without cross-linking) -- Identify orphaned pages (no inbound links from nav or other docs) -- Identify missing pages (concepts referenced in code or changelogs with no doc) -- Identify nav/sidebar structure problems (depth, grouping, naming) -- Map user journeys per product area and identify gaps - -Output: a site-level structural report with a proposed information architecture map. - -### Phase 3 -- Remediation Plan - -Produce a prioritized, actionable plan: - -**Priority tiers:** -- P0 -- Incorrect information that will cause integration failures -- P1 -- Stale content that will cause confusion or wasted time -- P2 -- Missing content (gaps in coverage) -- P3 -- Formatting, consistency, and AI-optimization improvements - -For each item: -- Page path -- Priority tier -- Current state (what's wrong) -- Proposed fix (specific, actionable) -- Source of truth (link or file path) - -## Scope - -The user can invoke this skill with: - -- **A page path or glob** (e.g., `merge-queue/configuration.mdx`, `flaky-tests/**`) -- review those pages -- **A product area** (e.g., "Merge Queue", "Flaky Tests") -- review all pages in that area -- **`full`** -- run the full site audit (this takes a while) -- **No argument** -- ask what scope the user wants diff --git a/.claude/skills/draft-docs/SKILL.md b/.claude/skills/draft-docs/SKILL.md deleted file mode 100644 index d64b641..0000000 --- a/.claude/skills/draft-docs/SKILL.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -name: draft-docs -description: Generates pre-populated documentation notes files from trunk2 context (PRs, deploy tags, Linear tickets). Use after a deploy or when the user wants to prepare docs updates, draft documentation, or bridge trunk2 changes to the docs repo. ---- - -Shared scripts are in: `~/.claude/skills/shared/scripts/` - -Generate documentation notes files from trunk2 context, ready for the `/write-docs` skill in this repo. Output goes directly to `.claude/drafts/`, then invoke `/write-docs` to process. - -## Task Progress Checklist - -Copy this checklist and track progress: - -```markdown -Docs Prep Progress: - -- [ ] Step 1: Gather context (deploy tags, PRs, Linear tickets) -- [ ] Step 2: Classify PRs — identify documentable features -- [ ] Step 3: Research each feature deeply (diffs, Linear, existing docs) -- [ ] Step 4: Determine docs work needed (new page / update / skip) -- [ ] Step 5: Generate notes files -- [ ] Step 6: Output summary with copy instructions -``` - -## Inputs - -The user may provide: - -- **`latest`** — generate notes for user-facing features in the most recent deploy tag (mirrors `/changelog latest`) -- **A deploy tag or range** (e.g., `v126`, `v123 to v126`) — features shipped in those releases -- **PR numbers** — specific trunk2 PRs to document -- **Linear ticket IDs** — `TRUNK-NNNNN` references -- **A feature description** — freeform text about what needs documenting -- If nothing is provided, default to `latest` mode - -## Output Location - -Notes files are written to: `.claude/drafts/` in this repo (docs2). - -Each run produces one or more files named `<featurename>.md` ready to be processed by `/write-docs`. - -## Steps - -1. **Gather context based on input**: - - For `latest` or deploy tags: use `get-deploy-tags.sh` from `~/.claude/skills/shared/scripts/` to find PRs in the range, then `gh pr view` to get details - - For PR numbers: use `gh pr view <number>` to get title, body, branch, diff - - For Linear tickets: use `mcp__claude_ai_Linear__get_issue` to get details - -2. **Classify PRs**: Split into user-facing vs. infrastructure (same logic as changelog skill). Only generate notes files for features that need documentation — not every user-facing change needs docs. Focus on: - - New features or capabilities - - Changed behavior or workflows - - New configuration options - - API changes - - Features that customers have asked about - -3. **For each documentable feature, research deeply**: - - Read the PR diff: `gh pr diff <number> --name-only` to identify changed files, then read key files to understand the feature - - Look up Linear tickets for descriptions, comments, and related tickets - - Search existing docs via `mcp__claude_ai_trunk_docs__searchDocumentation` to find what currently exists and identify gaps - - Search `#team-flaky-tests` and `#team-merge-queue` Slack channels for feature context, customer feedback, and usage details - - Search Slite for internal specs, design docs, or planning notes that can inform documentation - - Check if there are open changelog issues for this feature (`gh issue list --label changelog`) for additional context - -4. **Determine what docs work is needed** for each feature: - - **New page needed** — feature has no existing docs coverage - - **Update existing page** — feature changes behavior described in current docs - - **No docs needed** — bug fix or minor polish that doesn't affect documented behavior - - Skip features that don't need docs changes - -5. **Generate one notes file per feature** at `.claude/drafts/<featurename>.md` using this format: - - ```markdown - # [Feature/Change Title] - - ## Type - - <!-- new-feature | update | fix | deprecation | explainer --> - - [type] - - ## Priority - - <!-- P1 | P2 | P3 | P4 --> - - [priority — P1 for new features, P2 for updates, P3 for fixes] - - ## Linear Tickets - - [list of TRUNK-NNNNN IDs found] - - ## What Changed - - [2-3 paragraph summary of what changed in the product, written from the - user's perspective. Include specific details: new UI elements, new - configuration options, changed behavior, new API endpoints, etc.] - - ## GitHub PRs - - [list of PR URLs from trunk-io/trunk2, with titles] - - ## Context Links - - [any Slack, Slite, Loom links found in Linear tickets or PR descriptions] - - ## Target Docs - - [specific docs pages that need updating, based on docs search results. - Include the page title and URL. If a new page is needed, suggest where - it should go in the hierarchy based on the groups defined in docs.json.] - - ## Existing Docs Gap Analysis - - [what the current docs say vs. what they should say after this change. - Be specific — quote the outdated text if possible, and describe what - needs to change.] - - ## Context - - [paste the most relevant context here: - - - Key sections from the PR description - - Linear ticket description - - Any code snippets that show the new behavior (config examples, API - payloads, CLI commands, etc.) - - Error messages or UI text that should appear in docs] - ``` - -6. **Output summary**: Show the user: - - List of notes files generated (with paths to `.claude/drafts/`) - - For each: feature name, type (new/update), target docs page(s) - - Features skipped (no docs needed) with brief reason - - Suggested next step: `/write-docs` to process each draft - -## Translating Code Changes - -You are translating a code change (a GitHub pull request) for technical audiences. - -1. Translate only what is evidenced in the PR body, the diff, linked issues, or review comments. Never invent functionality or user impact that isn't stated or clearly implied. -2. If the PR description is empty and the diff is mostly build artifacts or minified code, say you don't have enough context. Do not guess. -3. If you're uncertain, use phrases like "appears to" or "likely" and flag the output as low confidence. -4. For pure refactors or internal changes with no user-visible change, say so explicitly. Do not invent user-facing benefits. - -For each notes file, generate a clear title and "What Changed" summary: - -- **Title**: max 10 words, benefit-focused, present tense (e.g., "Flag Individual Tests as Flaky from Test Detail Page") -- **What Changed**: 3-5 sentences, max 150 words. Include what changed and why it matters to users. - -## Guidelines - -- **Code is law** — when Slack, Slite, or Linear content conflicts with the actual code (variable names, endpoints, UI labels, feature names, etc.), always use what's in the code/PR diffs. External discussions reflect intent; code reflects what shipped. -- **Be thorough in research** — the better the notes file, the better the docs output. Include code examples, config snippets, and specific UI details. -- **One file per feature** — don't combine unrelated features. If a deploy tag has 3 documentable features, create 3 files. -- **Focus on what's documentable** — not every PR needs docs. A CSS fix doesn't need a notes file. A new configuration option does. -- **Include the gap analysis** — this is the most valuable part. Telling the docs skill "page X says Y but should now say Z" saves significant research time. -- **Quote existing docs** when possible — paste the current text that needs updating so the docs skill can find and edit it precisely. -- **Suggest doc locations** — use docs search results and the groups defined in `docs.json` to recommend where content should live. diff --git a/.claude/skills/outline-docs/SKILL.md b/.claude/skills/outline-docs/SKILL.md index 65036fc..6adcd8d 100644 --- a/.claude/skills/outline-docs/SKILL.md +++ b/.claude/skills/outline-docs/SKILL.md @@ -177,7 +177,7 @@ Post-Checklist: [ ] Add frontmatter description (if page is a top-level overview) [ ] Verify all links to related pages are correct [ ] Run: trunk check -[ ] Use /review-docs skill to review content and get feedback +[ ] Fill in TODOs, run `trunk check` for linting, and request review on the PR ``` Then, on the user's behalf, automatically run: @@ -340,6 +340,6 @@ After writing the file, the user should complete these steps: 4. **Run validation** — Execute `trunk check` to check for linting issues. -5. **Review content** — Use the `/review-docs` skill to review the page content and get feedback before merging. +5. **Review content** — Fill in every `<!-- TODO -->` block, then rely on `trunk check` for linting and the normal PR review flow for editorial feedback. Run `/docs-research` if you discover the topic overlaps an existing page and you need to reconsider placement. If any tool reports errors or review identifies issues, fix them before merging. diff --git a/.claude/skills/review-docs/SKILL.md b/.claude/skills/review-docs/SKILL.md deleted file mode 100644 index b03fb2c..0000000 --- a/.claude/skills/review-docs/SKILL.md +++ /dev/null @@ -1,298 +0,0 @@ ---- -name: review-docs -description: >- - Use when docs changes are ready to review before opening a PR. Reviews - git diff for repetition, structural completeness, logic errors, and style - consistency with related pages. Runs trunk fmt and trunk check. -allowed-tools: Bash(git diff *), Bash(git log *), Bash(trunk fmt), Bash(trunk check), Read, Glob, Grep, Edit ---- - -# Review Docs - -Review docs changes for quality before opening a PR. Checks git diff for unnecessary repetition, structural completeness, logic errors, and style consistency with related pages. Runs `trunk fmt` and `trunk check` and prompts to help fix issues. - -## Contents - -- [Inputs](#inputs) -- [Workflow](#workflow) - - [Phase 1: Identify changed files](#phase-1-identify-changed-files) - - [Phase 2: Check redirects](#phase-2-check-redirects) - - [Phase 3: Run automated checks](#phase-3-run-automated-checks) - - [Phase 4: Read related pages for style baseline](#phase-4-read-related-pages-for-style-baseline) - - [Phase 5: Review each changed file](#phase-5-review-each-changed-file) - - [Phase 6: Generate report](#phase-6-generate-report) - - [Phase 7: Prompt for fixes](#phase-7-prompt-for-fixes) -- [Review Criteria](#review-criteria) -- [Style Conventions](#style-conventions) - -## Inputs - -The user provides: -- **Optional file path** — review a specific `.mdx` file instead of all changed files -- If no path provided, reviews all changed `.mdx` files in the branch via `git diff main...HEAD` - -## Workflow - -Follow these phases in order. - -### Phase 1: Identify changed files - -Run `git diff --name-only main...HEAD` to get all changed files on the current branch. -Filter for `.mdx` files only. - -If a specific file path was provided as an argument, use that single file instead. - -If no `.mdx` files are found, output: -``` -ℹ️ No .mdx files changed in git diff. Nothing to review. -``` -and exit. - -### Phase 2: Check redirects - -Audit `docs.json` for stale or missing redirects caused by file moves or deletions. Mintlify redirects live in the top-level `redirects` array of `docs.json`. - -#### Step 2a — Extract moved and deleted .mdx files from Phase 1 diff - -Run: -```bash -git diff --name-status --diff-filter=R main...HEAD -git diff --name-only --diff-filter=D main...HEAD -``` - -Filter results to `.mdx` files only. Parse the rename output to extract `old_path -> new_path` mappings. - -#### Step 2b — Audit docs.json for stale and missing redirects - -Read `docs.json`. The `redirects` array contains entries shaped like: -```json -{ "source": "/old/path", "destination": "/new/path", "permanent": true } -``` - -Source and destination are URL paths (leading slash, no `.mdx` extension). - -For each renamed file (old → new): - -1. **Compute URL paths** — strip the `.mdx` extension from both old and new file paths, and prepend a leading slash (e.g., `merge-queue/old-page.mdx` → `/merge-queue/old-page`). -2. **Fix stale redirect destinations** — if any existing redirect's `destination` equals the old URL path, update it to the new URL path using `Edit`. -3. **Add missing redirect** — if no redirect entry has the old URL path as its `source`, insert a new entry into the `redirects` array. Maintain **alphabetical sort order** by `source`. - -For each deleted file: compute the URL path the same way, then suggest the nearest logical parent page or sibling as the destination; flag for user confirmation if unclear. - -#### Step 2c — Note dependents for high-traffic redirects - -`docs.json` is plain JSON (no inline comments). For redirects where the old URL is likely to have external dependents, surface the evidence to the user when posting the review report so it can be captured in the PR description: - -Search for evidence that the old URL has dependents: -- `Grep` the docs repo `.mdx` files for the old path string -- If the trunk2 repo is installed on the system, `Grep` `<path>/trunk2` for the old URL path (CLI or webapp references) -- Known heuristic: paths starting with `docs/`, `check/`, or `cli/` are commonly hardcoded in CLI output or documentation links - -If evidence is found, include a "Dependents flagged" section in the report listing each redirect's `source` and where the old URL appears (CLI output, trunk2 webapp, etc.) so the PR author can mention it in the description. - -### Phase 3: Run automated checks - -On behalf of the user, run: - -```bash -trunk fmt -trunk check -``` - -Collect the output (success or any issues found). Include this in the final report. - -### Phase 4: Read related pages for style baseline - -For each changed file, determine its product area from its directory path: - -| Path | Product Area | -|------|--------------| -| `merge-queue/` | Merge Queue | -| `flaky-tests/` | Flaky Tests | -| `ci-autopilot/` | CI Autopilot | -| `code-quality/` | Code Quality | -| `setup-and-administration/` | Setup & Admin | - -Use `Glob` to find 2 other `.mdx` files in the same product area directory. Read them to establish a baseline for: -- Tone and voice (formal vs. conversational) -- Terminology and phrasing -- Header structure and naming -- Section flow and organization - -Store these as reference for Phase 5 comparisons. - -### Phase 5: Review each changed file - -Read each changed `.mdx` file and evaluate it against the four review criteria (see [Review Criteria](#review-criteria) below). - -Document findings by criterion. If a file passes a criterion with no issues, no note needed. Only document issues. - -### Phase 6: Generate report - -Print the review report. Format: - -``` -Review Report — <filename> -======================================== -✅ trunk fmt: passed -✅ trunk check: passed - -[Repetition] -- <section/location>: <issue description and suggested fix> - -[Structure] -- <issue description>: <suggested fix> - -[Logic] -- <issue description>: <suggested fix> - -[Style & Consistency] -- <issue description and baseline from related pages>: <suggested fix> - -Summary: N issues found across M files. -``` - -**Report rules:** -- Only print sections with issues. Omit clean categories. -- If a tool (trunk fmt/check) found issues, note them separately and include in report. -- If no issues found across any file and both tools pass, print: - ``` - ✅ No issues found. All files are ready for a PR. - ``` - -### Phase 7: Prompt for fixes - -After the report, ask: - -``` -Would you like help fixing any of these issues? (y/n) -``` - -If the user answers **no**, exit. - -If the user answers **yes**: -1. List all issues from the report -2. Ask which issues to fix (they can choose specific ones or "all") -3. For **clear-cut fixes** (style/repetition/obvious rewording), apply the edits directly using the `Edit` tool -4. For **structural/logic issues**, explain what should be changed and let the user decide whether to apply the fix or manually edit -5. After fixes are applied, suggest running `/review-docs` again to verify - ---- - -## Review Criteria - -### 1. No unnecessary repetition - -**Check for:** -- Same information stated multiple times within a single page -- Redundant paragraphs or sections that say the same thing -- Concepts explained once, then re-explained a few sentences later - -**Example of repetition to flag:** -```markdown -### How it works - -Trunk Code Quality is a metalinter. A metalinter lets you lint… - -Trunk is a metalinter that… [← repetitive] -``` - -**Suggested fix:** Merge or delete the redundant statement. Keep one clear explanation. - ---- - -### 2. Structure completeness - -**Check for:** -- Logical flow: Does the page progress from concept to action (or whatever is appropriate)? -- Header hierarchy: H1 title, then H3 sections (no H2). Are sections ordered logically? -- **Guide pages:** Prerequisites → step-by-step → success criteria → what's next all present? -- **Reference pages:** Brief intro → quick reference table → detailed sections → examples all present? -- **Overview pages:** Intro → how it works → key features → get started all present? - -**Example of incomplete structure to flag:** -```markdown -# Installing the CLI - -### Step 1: Download - -[steps…] - -### What's next? -[no prerequisites section above—should have been there] -``` - -**Suggested fix:** Add missing prerequisites before Step 1. Re-order sections for logical flow. - ---- - -### 3. Logic errors - -**Check for:** -- Contradictory statements (e.g., "only works on Linux" in one section, "all platforms" in another) -- Steps that reference undefined concepts (e.g., "set the FLAG variable" without explaining what FLAG is) -- Claims inconsistent with the product area or code (e.g., "always runs locally" when it might not) -- Instructions that skip implied prerequisites - -**Example of logic error to flag:** -```markdown -### Step 1: Configure the linter - -Set the `output_format` flag to `json`. - -### Step 2: Run the linter - -trunk check [← doesn't mention that output_format was set, but code may require it] -``` - -**Suggested fix:** Add clarity: "Now when you run `trunk check`, output will be in JSON format." - ---- - -### 4. Style & consistency - -**Check for:** -- **Tense:** Present tense throughout ("Click X" not "You will click X" or "You clicked X") -- **Lead:** User benefit first ("Trunk X lets you…"), not implementation detail -- **Terminology:** Matches related pages in the same product area (established in Phase 3) -- **Jargon:** No internal system names (ClickHouse, Prisma, SST, Lambda, etc.) -- **Callouts:** Tips/warnings use Mintlify callout components (`<Note>`, `<Info>`, `<Tip>`, `<Warning>`, `<Check>`) — not plain text or bold-only callouts -- **Success markers:** Guide steps end with `**✅ Success:**` line - -**Example of style issue to flag:** -```markdown -The system will write logs to ClickHouse for analysis. -[← internal jargon; user doesn't care where logs go] -``` - -**Suggested fix:** Reframe for user benefit: "Trunk analyzes your test results to identify patterns." - ---- - -## Style Conventions - -Reference for Phase 4 checks — derived from existing Trunk docs pages: - -| Convention | Rule | Example | -|------------|------|---------| -| **Headers** | H3 (`###`) for all sections; no H2 in body | ✅ `### How it works` | -| **Tense** | Present tense | ✅ "Click X to open Y" not "You will click" | -| **User benefit** | Lead with benefit, not implementation | ✅ "Trunk catches errors early" not "uses static analysis" | -| **Callouts** | Use Mintlify components for tips/warnings | ✅ `<Note>...</Note>` (or `<Info>`, `<Tip>`, `<Warning>`, `<Check>`) not plain text | -| **Success** | Guide steps end with `**✅ Success:**` | ✅ `**✅ Success:** You see X in the dashboard` | -| **Jargon** | Avoid internal system names | ✅ "analyzes test results" not "ClickHouse stores metrics" | -| **Links** | Standard MDX links, no file extension | ✅ `[page](/path/to/page)` | - ---- - -## Typical Review Workflow - -1. Engineer creates a docs page using `/outline-docs` or writes one manually -2. Engineer runs `/review-docs` — reports any issues found -3. If issues exist, engineer answers `y` to fixes -4. Fixes are applied; engineer can run `/review-docs` again to verify clean -5. Once clean, engineer opens a PR -6. CI `claude-review.yaml` runs final typo/grammar/formatting check on the PR -7. PR is reviewed and merged - -The `review-docs` skill bridges the gap between local writing and PR submission, catching quality issues before CI review. diff --git a/.claude/skills/verify-docs-pr/DESIGN.md b/.claude/skills/verify-docs-pr/DESIGN.md deleted file mode 100644 index d1e38f1..0000000 --- a/.claude/skills/verify-docs-pr/DESIGN.md +++ /dev/null @@ -1,230 +0,0 @@ -# verify-docs-pr — Design - -**Date:** 2026-05-06 -**Author:** Sam Gutentag (with Claude) -**Status:** Approved, pending implementation plan - -## Problem - -A docs PR can be perfectly written but premature to publish if the underlying feature is not actually live for customers. The canonical case: docs PR #589 documented the new filtered Uploads page; the eng PR (trunk-io/trunk2#3670) was merged on 2026-04-28 but gated behind LaunchDarkly flag `enableFilteredUploadsPage`, which was on in staging and off in prod. Without verification, those docs would have shipped pointing customers to a feature they could not yet use. - -## Goal - -A skill that verifies whether features documented in open docs PRs are live in production, classifies each PR's state, and posts the verdict on the PR and the linked Linear ticket. Runs manually on a single PR or as a sweep across all open docs PRs, and integrates into the existing `write-docs` flow as a post-creation step. - -## Decisions (agreed via brainstorming) - -| Area | Decision | -|---|---| -| Live signal | Multi-signal classification: `live` / `staged` / `pending` / `blocked` / `unknown` | -| Inputs | Single PR by number AND sweep all open docs PRs (no arg) | -| Outputs | Terminal report + comment on docs PR + comment on linked Linear ticket | -| `write-docs` integration | Post-Phase 4. PR opens as draft, verification runs at the end | -| Signal sources | Indirect only — eng PR merge state, follow-up PRs, Slack search, e2e flags.json, legacy code presence | -| Skill style | Pure prose (matches `write-docs`). Sweep mode dispatches parallel agents when input >10 PRs | -| Source of truth | `gutils/claude-code/skills/trunk/verify-docs-pr/` | -| Symlinks | `~/.claude/skills/verify-docs-pr` and `<docs>/.claude/skills/verify-docs-pr` | - -## Per-PR verification logic - -For each docs PR, the skill runs five steps. - -### Step A — Parse PR body - -Extract: -- trunk2 PR references (`trunk-io/trunk2#NNNN` or full URLs). Generalized: any `trunk-io/<repo>#NNN` reference works; flag detection only runs against trunk2. -- Linear ticket IDs (`TRUNK-XXXXX`). -- Slack thread links (kept for context; not searched directly here). - -If the PR body has no engineering PR references, fall back to the linked Linear ticket and check its `relations` for related engineering tickets, then trace from those tickets to their PRs. - -### Step B — Check eng PR state - -For each engineering PR reference: -- `gh pr view <num> --repo trunk-io/<repo>` to retrieve state, `mergedAt`, files changed. -- If state is `open` or `closed` (not merged): mark `blocked` and stop further checks for that reference. -- If state is `merged`: continue. -- If the merge commit is no longer reachable from `main` (eng PR was reverted): treat as `blocked` with a "reverted" note. - -### Step C — Find feature flags - -From the merged eng PR's diff: -- Grep changed files for LaunchDarkly flag patterns: `enable*`, `show*`, references to `flags.ts`, strings inside `useFeatureFlag()` / `useFlag()` calls. -- Pull flag names mentioned in the PR body itself (eng PRs frequently say "gated behind the `enableFooBar` flag" — easy regex on backticks adjacent to "flag"). - -If no flags found anywhere, the feature is ungated. Skip to classification with `flag=none`. - -### Step D — Gather rollout signals (per flag) - -For each detected flag: -- `gh search prs --repo trunk-io/trunk2 <flag-name>` — find any follow-up PRs touching it (rollout, deletion, ramp-up). -- Slack search via MCP across `#eng`, `#team-flaky-tests`, `#team-merge-queue`, `#production-notifications`, `#staging-notifications` for the flag name. Look for LaunchDarkly bot announcements and manual confirmations. -- Read `ts/apps/e2e/flags.json` from trunk2 main. Note: a `true` default here only confirms it works in tests, not prod. -- Grep trunk2 main for any "legacy" code path mentioned in the eng PR (e.g., `UploadsClient` for #3670). If still present, flag is not yet 100% rolled out. - -### Step E — Classify - -| Verdict | Conditions | -|---|---| -| `live` | Eng PR merged AND (no flag found OR Slack confirms 100% prod OR legacy code deleted) | -| `staged` | Slack confirms flag on in staging, off in prod | -| `pending` | Eng PR merged, flag still off (no rollout signals) | -| `blocked` | Eng PR not merged, or merge commit reverted | -| `unknown` | Could not determine; flagged for manual review | - -Multiple eng PRs with mixed states: most conservative verdict wins. If any referenced eng PR is unmerged, the docs PR is `blocked`. - -## Outputs - -### Terminal — single mode - -``` -verify-docs-pr #589 (2026-05-06) -Verdict: pending - -Eng work: - trunk-io/trunk2#3670 — merged 2026-04-28 -Feature flag: - enableFilteredUploadsPage (LaunchDarkly) -Rollout signals: - - No follow-up PRs touched the flag in trunk2 main - - Slack: Tyler Jang in #eng (2026-04-16) — "flagged on in staging, off in prod" - - Legacy UploadsClient still referenced in trunk2 main - - e2e flags.json default: true (test only) - -Reasoning: - Eng merged but flag is gated off in prod. Customers cannot use this feature. - -Suggested next: - Ping @mb1206 for prod rollout ETA. Re-run /verify-docs-pr 589 after rollout. -``` - -### Terminal — sweep mode - -``` -verify-docs-pr — sweep across 41 open PRs (2026-05-06) - -#589 pending Uploads page trunk2#3670 merged; flag off in prod -#534 live Test case labels trunk2#3501 merged; no flag -... - -Summary: 24 live, 4 staged, 8 pending, 3 blocked, 2 unknown -``` - -Sorted by verdict severity: `blocked` → `pending` → `staged` → `unknown` → `live`. Most actionable first. - -### PR comment - -Posted as a one-block markdown comment. Re-runs **edit the existing comment** in place — skill detects its own prior comment via the HTML marker `<!-- verify-docs-pr -->`. - -```markdown -<!-- verify-docs-pr --> -**Verification status (2026-05-06): `pending`** - -Eng work merged but feature flag still off in prod. - -- Eng PR: trunk-io/trunk2#3670 — merged 2026-04-28 -- Flag: `enableFilteredUploadsPage` -- State: on in staging, off in prod (per #eng, 2026-04-16) -- Legacy `UploadsClient` code path still present in trunk2 main - -Hold off on publishing. Re-run `/verify-docs-pr 589` after rollout. -``` - -Verdict-specific opening line: -- `live` — "Verified: customers can use this. Ready to publish." -- `staged` — "On in staging only. Re-run after prod rollout." -- `pending` — "Eng merged but flag off in prod. Hold off." -- `blocked` — "Eng PR not merged. Hold." -- `unknown` — "Could not determine state from available signals. Manual check needed." - -### Linear ticket comment - -Mirrors the PR comment with the docs PR link added at the top. Same `<!-- verify-docs-pr -->` marker for idempotent re-runs. - -### PR state action - -- `live` — no PR state change. -- `staged` / `pending` / `blocked` / `unknown` — if PR is `ready`, flip to `draft`. If already draft, no-op. Belt-and-suspenders against accidental auto-merge. -- PR already merged — skip entirely. Skill prints "PR #N already merged; skipping" and moves on. - -## `write-docs` integration - -A new Phase 5 at the end of `write-docs`: - -> After PR #N is opened: invoke `verify-docs-pr` with PR number N. - -Behavior: -- Runs the per-PR verification logic. -- Posts the verdict comment on the new draft PR. -- Posts the verdict comment on the linked Linear ticket. -- Prints terminal output to the user. - -Does **not** block PR opening (already opened by Phase 4), change PR labels, move Linear status, or fail `write-docs` if verification errors. A skill error prints a warning and continues. - -## Sweep parallelization - -| PR count | Strategy | -|---|---| -| ≤10 | Sequential. Agent-spawn overhead exceeds savings | -| >10 | Dispatch parallel sub-agents in chunks of ~13 PRs each | - -Each agent's job: -- Run the per-PR verification on its assigned chunk. -- Post the comment on PR + Linear. -- Return a structured result back to main: `{pr, verdict, summary_line}`. - -Main thread aggregates, sorts by severity, and prints the summary table. If an agent dies before reporting, main lists the PRs in that chunk as "unverified" so they can be retried individually with `/verify-docs-pr <num>`. - -## Errors and edge cases - -| Case | Handling | -|---|---| -| PR body has no eng PR refs | Fall back to linked Linear ticket. Read its `relations` for related engineering tickets, then check those tickets' linked PRs. | -| No Linear ticket either | Verdict = `unknown`. Comment lists what's missing. | -| Eng PR is in a non-trunk2 repo (e.g., `analytics-cli`, `flake-farm`) | Generic handling. Any `trunk-io/<repo>#NNN` reference gets the same merge-state + diff inspection. Flag detection only runs against trunk2. | -| Slack search returns zero hits for a flag | Not an error. Counts as a `pending` signal ("no rollout chatter found"). | -| `gh` or Slack API timeout | Retry once. On second failure, continue with partial data and mark verdict as `unknown` with a note about the unavailable signal. | -| Multiple eng PRs with mixed states | Most conservative wins. Any unmerged ref → `blocked`. | -| Eng PR merged then reverted | Detect by checking merge commit reachability from `main`. Treat as `blocked` with a "reverted" note. | -| Re-run when prior `<!-- verify-docs-pr -->` comment exists | Edit the existing comment in place. Same on Linear. Verdict timestamp updates. | - -## Testing - -No unit tests. Validation checklist before merging the skill: - -1. **Pending case** — `/verify-docs-pr 589` classifies as `pending`, references `enableFilteredUploadsPage`. -2. **Live case** — `/verify-docs-pr 534` (test case labels) classifies as `live` (eng merged, no flag found). -3. **No eng refs case** — `/verify-docs-pr 522` (org slug audit, pure docs bug fix) classifies as `live` or `unknown` with reason "no eng PR found, pure docs change". -4. **Sweep case** — `/verify-docs-pr` against current 41 open PRs. Spot-check 5 random verdicts against manual judgment. -5. **Re-run idempotency** — run twice on #589, existing comment is edited, no duplicate. -6. **`write-docs` integration** — open a fresh draft PR via `write-docs`, verify Phase 5 fires automatically and posts a verdict. - -## Implementation phases - -This work has two phases. The first must complete before the second starts. - -### Phase 1 — migrate existing docs skills to gutils - -The repo-specific skills currently live only in `<docs>/.claude/skills/`. Migrate them to follow the same dual-symlink pattern as the new skill. - -Skills to migrate: -- `write-docs` -- `outline-docs` -- `review-docs` - -End state for each: -- Physical files: `gutils/claude-code/skills/trunk/<skill>/` -- Symlink: `~/.claude/skills/<skill>` → gutils -- Symlink: `<docs>/.claude/skills/<skill>` → gutils - -### Phase 2 — build verify-docs-pr - -Create the new skill following the patterns established in Phase 1. - -End state: -- Physical files: `gutils/claude-code/skills/trunk/verify-docs-pr/` -- Symlink: `~/.claude/skills/verify-docs-pr` → gutils -- Symlink: `<docs>/.claude/skills/verify-docs-pr` → gutils -- `SKILL.md` implementing the design above -- `write-docs/SKILL.md` updated to invoke verify-docs-pr at the end of Phase 4 From 4302521736f20e42d024a934cad0d9e7f8283b10 Mon Sep 17 00:00:00 2001 From: Sam Gutentag <1404219+samgutentag@users.noreply.github.com> Date: Wed, 13 May 2026 00:29:45 -0700 Subject: [PATCH 3/4] chore(claude): add Linear-ticket dedup to write-docs OVERLAP-CHECK The OVERLAP-CHECK Phase 0 in write-docs only checked GitHub for existing/conflicting docs PRs. Adds Step 3: search Linear for an existing planning ticket using the same Trunk Engineering team ID the DevRel scanner uses, ask the user before duplicating. Closes the gap surfaced by mapping the daily DevRel scanner against the consolidated docs2 skill set: scheduled automation already dedups against Linear in its own pipeline, but a manual /write-docs invocation could still file a duplicate ticket. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --- .claude/skills/write-docs/OVERLAP-CHECK.md | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.claude/skills/write-docs/OVERLAP-CHECK.md b/.claude/skills/write-docs/OVERLAP-CHECK.md index b0c7a05..a114cea 100644 --- a/.claude/skills/write-docs/OVERLAP-CHECK.md +++ b/.claude/skills/write-docs/OVERLAP-CHECK.md @@ -48,4 +48,26 @@ Run these checks before starting any work. Stop and ask the user if any match is Do NOT proceed until the user responds. -5. **If no overlaps found**: Continue to Phase 1. +5. **If no overlaps found**: Continue to Step 3. + +## Step 3: Check for existing Linear tickets + +Automation (the daily DevRel scanner, the `/changelog` skill, others) may have already filed a planning ticket for this feature. Find it before creating a duplicate. + +1. Extract feature keywords from the draft — topic name, product area, and any `TRUNK-NNNNN` refs already in the notes file. + +2. **If the draft already references a `TRUNK-NNNNN` ticket**: fetch it directly via `mcp__claude_ai_Linear__get_issue` to confirm it's the right one and capture its current state. Proceed to Phase 1. + +3. **Otherwise, search Linear** for matching tickets: + - Tool: `mcp__claude_ai_Linear__list_issues` + - Team ID: `16f26d2e-3c38-4c56-869d-9fea8f33321e` (Trunk Engineering) + - Filter by feature keywords; look for ticket titles or descriptions mentioning the same feature name, product area, or carrying a `changelog` / `docs` label + +4. **If matching tickets are found**: show the user the ticket links and titles, then ask: + - (a) Link the new docs PR to this ticket and pull its context into the draft (no new ticket) + - (b) Create a new ticket anyway and document why (rare — usually only if the existing ticket is closed or scoped to something unrelated) + - (c) Skip this draft (someone else owns the planning) + + Do NOT proceed until the user responds. + +5. **If no matching tickets are found**: continue to Phase 1. A new Linear ticket will be created in Phase 5 of `/write-docs`. From c14e755334836e8c30ad4b13e45a6dbefba2e662 Mon Sep 17 00:00:00 2001 From: Sam Gutentag <1404219+samgutentag@users.noreply.github.com> Date: Wed, 13 May 2026 00:34:55 -0700 Subject: [PATCH 4/4] docs(claude): note Linear-ticket dedup in write-docs description Reflects the OVERLAP-CHECK Phase 0 addition from commit 4302521. Phase 5 also mentions linking the existing ticket from Phase 0 rather than always creating a new one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --- .claude/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude/README.md b/.claude/README.md index 67ffb40..ba3f2fe 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -77,11 +77,11 @@ The flow is **research → write → verify**: **When:** Given a draft notes file, trunk2 PR numbers, Linear ticket IDs, a deploy tag, or Slite links — anything that says "document this feature." **What it does:** Full 9-phase pipeline: -1. Overlap detection (Phase 0) — refuses to proceed if another PR covers the same topic +1. Overlap detection (Phase 0) — checks GitHub for existing or conflicting docs PRs **and** Linear for existing planning tickets before starting work; asks the user if anything matches 2. Research across Linear, Slite, Slack, trunk2 PR diffs, and existing docs 3. Drafts new content or in-place edits, updating `docs.json` if adding pages 4. Creates a branch, commits, opens a **draft** PR with author tags -5. Updates Linear and writes a Slack post draft +5. Updates Linear (links the existing ticket from Phase 0 or creates a new one) and writes a Slack post draft 6. Invokes `/verify-docs-pr` to check whether the feature is actually live in prod **Inputs:** Trunk2 PR refs, Linear ticket IDs, a deploy tag, Slite links, or an optional `.claude/drafts/<topic>.md` file for batch workflows.