Imported from warpdotdev/docs (
.agents/skills/missing_docs/SKILL.md). Install upstream withnpx skills add warpdotdev/docs --skill missing_docs. Copyright stays with the author.
Missing Docs
Find documentation gaps, detect doc-impacting code changes, and draft missing pages.
Agent-doc quality contract
Any PR this skill opens or updates follows the shared v1 agent-doc quality
contract in .agents/references/doc-quality-policy.md: apply the
warpy-factory label, add the ## Documentation risk block
(.agents/skills/doc_quality_policy/finalize_pr_contract.py build), and keep
## Unverified claims (step 9.5 of draft_docs) current. A newly-drafted
feature page is engineering-review-required by default per the allowlist.
Requirements
The audit compares docs against code, so both source repos must be available:
- the public warp client repo (warpdotdev/warp)
and
warp-server, auto-detected as siblings of the docs repo root (e.g./workspace/docsnext to/workspace/warpand/workspace/warp-server; a sibling namedwarp-internalis accepted as a fallback), or passed explicitly via--warp PATH/--warp-server PATH(--warp-internalis a deprecated alias).
The script FAILS LOUD when a repo is missing OR when an extraction sanity guard
trips (a parser returning implausibly few surfaces means the source layout
changed and the parser needs fixing): it exits with code 2 and lists the skipped
audits in the report's audits_skipped field (extraction:* entries identify
broken parsers). Never treat an exit-2 run as a clean audit — fix the problem
and re-run. Exit 0 means all requested audits ran (findings may still exist).
Run every command from the docs repo root
Every path in this skill — scripts, references, doc pages — is relative to the docs
repo root, and nothing resolves them for you. A sandbox commonly starts a run one level
up (/workspace, with the checkout at /workspace/docs), so cd before anything else:
cd "$(git rev-parse --show-toplevel)"
A wrong working directory fails in a way that reads like a real failure: python3 exits
2 with can't open file, the same exit code audit_docs.py uses to fail loud on a
broken environment. Read the message before concluding a sanity guard tripped.
Install Node dependencies before the first build
npm run build is the only validation this repo has, and it needs node_modules, which
a fresh sandbox does not have. Install once per sandbox:
npm ci
Regenerate the snapshot only from current checkouts
--update-snapshot rewrites references/surface_snapshot.json from whatever the sibling
repos hold at that moment, and it cannot tell a current checkout from a stale one. Running
it against an old or feature-branched warp / warp-server writes a baseline describing
surfaces that are not on the default branch, and the next --diff then reports the gap
between two wrong baselines as real drift. Confirm both repos are current and on their
default branch — master for warp, develop for warp-server — before regenerating:
for repo in ../warp ../warp-server; do
git -C "$repo" fetch --quiet origin
echo "$repo $(git -C "$repo" rev-parse --abbrev-ref HEAD) \
$(git -C "$repo" log -1 --format=%cd --date=short)"
done
A cloud sandbox provisions fresh checkouts and satisfies this by construction; a developer's machine usually does not. When you cannot confirm it, leave the regen to a scheduled run rather than committing a snapshot you cannot vouch for.
Public vs. private surfaces (what you may document)
Only document surfaces that are publicly released. This is the most important guardrail in this skill: do not reveal private or unreleased surfaces in public docs. Two independent gates, both required:
- Source / exposure. The OSS warp client repo (warpdotdev/warp; locally
warp, or thewarp-internalfallback) is public — its feature flags, CLI commands, settings, and slash commands are documentable.warp-serveris a private repo and is not public until released; its source and most of its surfaces must NOT be documented. The one exception is the public Oz Agent API, whose released surface is exactly the set of endpoints already present in the OpenAPI spec (developers/agent-api-openapi.yaml). - Rollout status. Even for public-repo surfaces, only document GA features. Never document dogfood, preview, or research-preview surfaces (for example, Agent Memory is research preview, so its
oz memory*CLI and/memory_storesAPI must not be documented yet).
Rules of thumb:
- A
warp-serverAPI endpoint that is not already in the OpenAPI spec is treated as not-yet-public: do NOT hand-write docs for it. Either confirm it has been publicly released and let thesync-openapi-specskill bring it into the spec, or map it-> internal/ defer it. When unsure, defer — never expose an unreleased endpoint or feature in public docs. - A CLI command or API route gated by a non-GA feature flag should be mapped
-> gated:<Flag>(for example,gated:AIMemories) rather than-> internal: the audit auto-defers it while the flag is non-GA and auto-surfaces it for docs once the flag goes GA. (Feature flags and settings already auto-defer by rollout status;gated:extends that to CLI/API.) - A published docs page is not evidence that an API is released. Early Access features routinely have public pages describing what the product does while their REST routes stay out of the released spec, so "we already document this feature" does not clear Gate 0 for an endpoint. For API surfaces the released OpenAPI spec is the only test. The Factory pages describe dispatching a run, yet no
/factorypath appears indevelopers/agent-api-openapi.yaml— those routes are Gate 0 deferrals despite the prose. - The audit still detects these as gaps (useful signal), but detection is not permission to document. Every resolution must respect this boundary.
This section is the source of truth for Gate 0 in
.agents/references/docs-worthiness-criteria.md. Passing Gate 0 only establishes that a
surface may be documented — it does not establish that it should be. Work through the
remaining gates before treating any finding as actionable.
Workflows
Phase 1: Audit (coverage)
Run the audit script to identify gaps:
python3 .agents/skills/missing_docs/scripts/audit_docs.py
Options:
--category features|cli|api|slash|settings|structure|staleness|map— run a single audit category--severity high|medium|low— filter by minimum severity--weak-coverage— also flag GA features whose mapped doc exists but doesn't mention feature keywords (low-severity, noisy)--output report.json— save JSON report to file--warp PATH/--warp-server PATH— explicit repo paths (--warp-internalis a deprecated alias)--diff— change detection against the committed snapshot (see Phase 2)--update-snapshot— regeneratereferences/surface_snapshot.json(full runs only)
The script resolves doc paths from the docs repo root and accepts .md and .mdx
interchangeably (and README.md ↔ index.mdx), so surface-map entries can use the
canonical filename even when the on-disk extension differs.
The script performs these coverage audits:
-
Feature flag coverage — classifies every
FeatureFlagby rollout status using the cargo-feature→flag bridge in the warp client repo'sapp/src/features.rsplusRELEASE_FLAGS/PREVIEW_FLAGS/DOGFOOD_FLAGSincrates/warp_features/src/lib.rs. GA flags must be mapped in the surface map or covered in docs; Preview flags produce low-severity "docs needed soon" findings; dogfood/other flags are tracked by the snapshot only. -
CLI command coverage — parses the full
ozcommand tree fromcrates/warp_cli/src/(recursive subcommands likeoz run message send, skippinghide = true) and checks the CLI reference docs. Per-module--longflags are additionally tracked in the snapshot for change detection. -
API endpoint coverage — extracts public routes from warp-server
router/handlers/public_api/*.go(nested gin groups resolved, caller-passed group prefixes matched positionally) and checks them againstdevelopers/agent-api-openapi.yaml(param-name-insensitive:{runId}matches{run_id}) and the API reference docs. For spec drift, run the docssync-openapi-specskill (or warp-server'supdate-open-api-spec) instead of hand-editing the YAML. warp-server is private (see Public vs. private surfaces): a flagged endpoint is documentable only once it is part of the released public Oz Agent API. Never hand-draft API docs or reveal an unreleased endpoint — resolve released endpoints viasync-openapi-spec, and-> internal/ defer the rest. -
Slash command coverage — parses the static registry in the warp client repo's
app/src/search/slash_command_menu/static_commands/and checks each/commandis mentioned in docs.Known limitation: this check is repo-wide, not surface-scoped. A command counts as covered when any page mentions it (
audit_slash_commandsinscripts/audit_docs.pysearches every docs page), so a command documented on the GUI slash-commands page reads as covered even when the CLI reference omits it. That is how/usagereached a release undocumented for CLI users — only the changelog cross-check caught it. The CLI and settings audits scope their search to the pages that own those surfaces; this one does not. Until that is fixed, do not read a slash command'sdoc_coveredbucket as "documented in the right place." -
Settings coverage — parses every
toml_path: "section.key"setting registration in the warp client repo (the same registry the JSON-schema generator uses) and checks the all-settings reference page documents it. Private and dogfood/other-flagged settings are exempt; object-typed settings documented as their own[section]count as covered. -
Docs staleness — flags renamed/removed-feature terminology in prose (code spans stripped; historical changelog pages excluded). Broader terminology and style enforcement is owned by the
style_lintskill — delegate pure wording issues there. -
Stale doc references — reverse checks: settings keys documented in all-settings.mdx that no longer exist in code (catches renames like
agents.oz.*→agents.warp_agent.*), and keybinding actions (scope:action) on the keyboard-shortcuts page that no longer exist anywhere in the warp client repo. -
Docs structure — pages on disk that are missing from
src/sidebar.ts(built but unreachable through navigation). Intentionally unlisted pages go in the surface map's "Unlisted docs pages" section. -
Surface map hygiene — flags map entries whose flag/command/route/setting no longer exists in code, and mapped doc targets that no longer exist. Verify the doc page is still accurate, then prune or update the entry.
Snapshot-only surfaces (no standing coverage audit, but added/removed/changed items
are reported by --diff): Oz web app routes (AgentsApp.tsx), server-side agent
tools (multi_agent tool registries), bundled + channel-gated skills
(resources/bundled/skills, resources/channel-gated-skills), and per-module CLI
flags.
Present the report to the user, grouped by category and sorted by severity.
Adjacent checks owned by other skills (do not duplicate them here):
- UI menu paths and Command Palette names, including detecting when a documented control has relocated to a different Settings page →
validate_ui_refs - Platform error-code pages →
sync-error-docs - Broken links and 404s/redirects →
check_for_broken_links/weekly-404-monitor - Terminology/style sweeps →
style_lint
Completeness accounting (the no-slip guarantee)
Every full run computes a completeness accounting and embeds it in the report
(summary.accounting in JSON, a COMPLETENESS ACCOUNTING block in the printed
output). It partitions every extracted surface item into exactly one
accountability bucket and proves totality:
- Feature flags: every GA/Preview flag is
mapped(surface map verified),ignored(curated internal list), or a visiblefinding; every dogfood/other flag istracked_non_ga(snapshot diff fires on promotion or removal). - CLI commands:
mapped,doc_covered,gated_non_ga(deferred viagated:<Flag>while its gating flag is non-GA),finding,parent_flagged(suppressed because the parent command is already flagged), orhidden. - API routes:
mapped,spec_covered,docs_covered,gated_non_ga(deferred viagated:<Flag>while its gating flag is non-GA), orfinding. - Slash commands:
mapped,doc_covered, orfinding. - Settings:
private,tracked_non_ga,mapped,doc_covered, orfinding.
If any item escapes every bucket, the run reports integrity:accounting in
audits_skipped and exits 2 — an unaccounted item means the audit logic itself
regressed, never that the item is fine. Map hygiene additionally rejects
integrity bugs in the surface map: entries that are both mapped and ignored
(the ignore silently wins) and duplicate keys within a section.
How every change path is caught, end to end:
- New surface item appears (flag, command, route, slash, setting, web
route, tool, bundled skill) → the snapshot
--diffreports it AND, once GA/user-facing, the coverage audit produces a standing finding until it is documented + mapped or ignored with a comment. - Item is promoted (dogfood→preview→ga, setting status change, skill
channel change) →
--diffstatus-change finding + coverage finding appears. - Item is removed/renamed →
--diffremoval finding + map hygiene flags the dead map entry + stale-doc-reference checks flag docs still naming it. - Launch with no client-code change (server-side experiment flips to 100%, Oz web app backend feature) → the changelog cross-check is the net: every "New features"/"Improvements"/"Oz updates" bullet newer than the snapshot becomes a verification finding.
- The audit itself rots (source layout moves, parser breaks) → extraction sanity guards trip, dependent audits skip, exit 2.
- The map rots (dead entries, conflicts, duplicates, missing doc targets, unmapped-but-mentioned features) → map hygiene + fallback-transparency findings keep pressure until fixed.
The mapping is updated through three enforced paths: Phase 3 step 8 makes the map+snapshot update a mandatory part of drafting; the drift-watch triage step requires a mapping/ignore/allowlist decision for every finding; and map hygiene findings force pruning when code moves underneath the map.
Phase 2: Change detection (diff mode)
The snapshot at references/surface_snapshot.json records all extracted surfaces
(flags + rollout status, CLI commands and per-module flags, API routes, slash
commands, settings + status, Oz web app routes, server-side agent tools, bundled
skills) plus the last-seen docs-changelog version. It makes change detection
possible: a feature flag that is deleted after stabilizing (per the warp repo's
remove-feature-flag policy) would otherwise vanish from the audit's universe
silently. When a new surface type is introduced, diffing against an older snapshot
emits a one-time "surface type newly tracked" note instead of false positives.
python3 .agents/skills/missing_docs/scripts/audit_docs.py --diff
Diff mode reports, since the snapshot was last updated:
-
Added / removed / promoted surfaces — e.g. a new GA flag (high), a flag promoted dogfood→ga (high), a removed flag ("feature stabilized or killed — verify docs and map entry"), new/removed CLI commands and
--flags, API routes, slash commands, settings (with status promotions), Oz web app routes, server-side agent tools, and bundled skills. -
Changelog items to verify — "New features", "Improvements", and "Oz updates" bullets from
src/content/docs/changelog/<year>.mdxentries newer than the snapshot's last-seen version. This is the best signal for launches no static code parse can see (server-side features, Oz web app, experiment rollouts). A changelog mention is NOT documentation — verify each item has real doc coverage. ("Bug fixes" bullets are deliberately untracked to keep weekly triage volume manageable.)A changelog item is a candidate, not a work item. Detection is not permission to document. Every item must pass
.agents/references/docs-worthiness-criteria.mdbefore it becomes actionable — most will not. Readreferences/changelog_decisions.mdfirst and skip any PR number already decided.
Sources beyond the client changelog
The client changelog only covers warpdotdev/warp. Server and platform features ship
continuously and never appear in it, which is how they used to reach docs through the
retired spec-scan path — and why that path produced most of the unvetted drafts. Cover
them through these three layers instead, in order of preference:
-
oz_updates— the separate array in the sameclient_versionpayload the release gate already fetches. Release-gated, low-noise, and currently the most direct signal for platform-side changes. Triage these bullets exactly like changelog bullets: same gates, same ledger, same evidence requirement.check_new_release.pyprints them; the audit never sees them, because it is offline by design and they are not part of the markdown changelog it parses. Read them from the gate's output, or--jsonfor the full array.oz_updatesis the API's field name and stays as-is regardless of product naming. -
The public Agent API surface — already covered by audit category 3 and
sync-openapi-spec. No new machinery; just confirm the release run actually triages these findings rather than deferring them by habit. A released endpoint reaches docs through the spec, never through hand-drafting. -
warp-serverproduct specs — the last resort, and the most conservative layer. Apply a hard rollout check before the worthiness gates: only consider a spec whose feature is verifiably enabled for users. A merged spec is not a shipped feature. If you cannot confirm the rollout from code or the changelog, defer it and record the blocking condition — do not draft against the spec text.
Layer 3 is where the old pipeline went wrong: it treated spec merge as the trigger, so it drafted for features that had not shipped and sometimes never would. Reach for it only when layers 1 and 2 cannot see a user-visible change you have independent evidence has shipped.
After triaging and addressing diff findings, refresh the snapshot and commit it with your PR so the next run diffs against the new baseline:
python3 .agents/skills/missing_docs/scripts/audit_docs.py --update-snapshot
Phase 3: Draft
Preconditions — do not draft without both:
- A recorded pass verdict. The finding must have passed
.agents/references/docs-worthiness-criteria.md, with the gate and its concrete evidence written down. No recorded verdict means no drafting. For changelog-derived findings the verdict also belongs inreferences/changelog_decisions.md. - A content design plan. Route it with the rule in
.agents/references/content-design-plan.md: a new page gets the full form in.agents/templates/content-design-plan.md, an update that adds a concept gets the three-line short form, and a correction gets no plan. Write it before opening a page template — the plan decides the content type; the template does not. Carry it into the PR body verbatim: a scheduled run has no one to present it to, so the PR body is the only place a human will see the reasoning.
A finding that passes the gate with the update an existing page outcome is still a drafting task — it just edits a page instead of creating one. Prefer it; new pages need to be justified against the existing information architecture, not just against the change.
For each gap to address (prioritize high → medium → low):
-
Read
references/feature_surface_map.mdto determine the target doc section -
Read
AGENTS.mdin the docs repo root for the complete style guide -
Read 2-3 strong examples in the target section to match formatting patterns
-
Research the relevant source code:
- Feature gaps → read the implementation in the warp client repo's
app/src/, check UI code, settings, user-facing strings - CLI gaps → read command definition in
crates/warp_cli/src/, extract flags, arguments, help text - API gaps → read handler in warp-server
router/handlers/public_api/, route definition, request/response types; prefer fixing the OpenAPI spec via thesync-openapi-specskill. Only act on endpoints already publicly released (see Public vs. private surfaces); never draft docs for unreleased warp-server endpoints. - Slash command gaps → read the registry entry and gating flags in
app/src/search/slash_command_menu/
Then look for a product spec. Code tells you what a surface does; it never tells you who it is for or what problem it solves — and those are the content design plan's first three fields. Check warp-server for
specs/<id>/PRODUCT.md. Only some specs have one, andTECH.mdis the implementation plan, not a substitute. Where it exists, itsProblem,Goals,Non-goals, andUser experiencesections map onto the plan's Problem, Goals, Excludes, and high-impact scenarios almost directly.Three limits, all load-bearing:
- Framing only, never behavior. Labels, flags, and defaults drift between spec and ship, so every concrete claim is still verified against code. A spec is context, not a source of truth.
- Never evidence that something shipped. A merged spec is not a release. Gate 0 is settled before this step, and a spec cannot reopen it.
- Never quoted into a public page. warp-server is private and specs routinely describe unshipped plans. Use one to understand the reader, then write the page from scratch.
If no spec exists, proceed without one and record that in the content design plan. An acknowledged gap is reviewable; an invented audience is not.
- Feature gaps → read the implementation in the warp client repo's
-
Draft the doc following style guide conventions:
- YAML frontmatter with description
- All headings (H1–H4) must use sentence case — capitalize only the first word and proper feature names (e.g., "Agent Mode", "Warp Drive"). ✅
## How it works❌## How It Works - Opening paragraph with user benefit
- Key features, how it works, detailed sections, cross-references
- Correct terminology (Agent, Agent Mode, Warp Drive, Oz, etc.)
- Bold + dash format for list items:
* **Term** - Description
-
Create the markdown file at the suggested path
-
Add new pages to the sidebar in
src/sidebar.ts(only a brand-new top-level topic also needs anastro.config.mjschange) -
Update
references/feature_surface_map.mdfor every feature you document: add aFlag -> src/content/docs/...mapping (or an ignore-list entry with a comment if you confirmed it is internal-only). This step is NOT optional — unmapped features become repeat findings, and an unmaintained map is how gaps get lost. Per the PR strategy below, collect all map edits into the single companion audit-bookkeeping PR (only fold them into a feature PR when the run documents exactly one feature).Edit map entries individually; never find-and-replace across the file. The left-hand side of every entry is a literal code identifier — a flag name, command, route, setting key, or doc slug — and it only matches code because it matches exactly. A rename sweep applied to the whole map (say, replacing
costwithusagewhile renaming a feature) rewrites unrelated keys into surfaces that do not exist. Map hygiene catches the corruption on the next audit, but only after it has shipped in a PR. Change the entries you mean to change, then re-run--category mapto confirm nothing else moved. -
Run
--update-snapshotand commit the refreshedsurface_snapshot.jsonin that same bookkeeping PR. Never split the snapshot across multiple PRs.
Resolution patterns
Not every finding needs a new doc page — pick the lightest correct fix and verify it against source before applying:
- No docs needed — the finding failed every worthiness gate, or a disqualifier applied.
This is a first-class resolution, not a silent skip: record the verdict, the
disqualifier or failed gates, and a one-line reason. Changelog items go in
references/changelog_decisions.md; code surfaces go in the surface map as an ignore entry with a comment. An unrecorded rejection is re-proposed next run and has to be rejected again by the same reviewer. - Deferred (Gate 0) — real user-facing surface, but not yet GA or not yet public. Record it with the blocking condition so it re-surfaces when the flag goes GA or the endpoint reaches the released OpenAPI spec. Never draft ahead of the release; a page written for an unshipped feature is stale before it merges.
- User-facing setting — document it in
terminal/settings/all-settings.mdxunder its TOML section (type/default/options come from thetoml_pathregistration). - Internal or state-only setting (one-time banners, migration flags, telemetry-modeled state) — map
section.key -> internalin the surface map instead of documenting it. - Feature flag with a dedicated doc page — map the flag to that page.
- Feature flag whose only user-facing surface is an already-documented setting — map the flag to that setting's doc page rather than writing a new page (for example, a tab-bar visibility flag maps to the all-settings reference).
- Preview or pre-launch feature with no docs yet — add it to the surface-map ignore list with a comment; the snapshot diff re-flags it when it promotes to GA.
- Stale map entry or doc reference (map hygiene) — confirm the surface is gone from code, then prune the dead entry.
- warp-server API endpoint not in the released OpenAPI spec — do not hand-document it (warp-server is private). If it is part of the released public Oz Agent API, hand it to the
sync-openapi-specskill; if it is unreleased or internal, map it-> internalwith a comment. Never expose an unreleased endpoint or feature in public docs. - CLI command or API route gated by a non-GA feature flag — map it
-> gated:<Flag>so it auto-defers while the flag is non-GA and auto-surfaces for docs when it GAs (e.g. Agent Memory'soz memory*and/memory_stores/*usegated:AIMemories). Prefer this over-> internal, which never re-surfaces.
Reviewer routing
Assign the engineer who owns the code behind each change, so a human with real context reviews the PR. Every finding traces to a source surface; map that surface's defining file to its owner using the ownership files that already live in the code repos (CODEOWNERS format, last-match-wins):
- warp client repo:
.github/STAKEHOLDERS - warp-server:
.github/STAKEHOLDERS(advisory) +.github/CODEOWNERS(enforced)
These are the source of truth (warp-server keeps STAKEHOLDERS fresh via the sync-stakeholders skill), so never hardcode owner lists here.
For each addressed finding, note the defining source file you already consulted in Phase 3 step 4:
- Setting → the file holding its
toml_pathregistration (usually underapp/src/settings/). - Slash command →
app/src/search/slash_command_menu/static_commands/. - Feature flag → the flag's primary usage site in
app/src/(grep the flag name); fall back tocrates/warp_features/src/lib.rs. - CLI command →
crates/warp_cli/src/. - API route → warp-server
router/handlers/public_api/(API gaps usually go tosync-openapi-spec).
Resolve owners (the script prints a ready-to-run command only when a single owner resolves):
python3 .agents/skills/missing_docs/scripts/suggest_reviewers.py \
--warp ../warp --warp-server ../warp-server \
warp:app/src/settings/ssh.rs \
warp:app/src/search/slash_command_menu/static_commands/commands.rs
Add --reviewers-only to get just the comma-joined --add-reviewer argument (empty output when nothing resolved), which is the form the create_pr request snippet consumes.
Then apply the create_pr skill's reviewer policy ("Request a reviewer (at most one, only with conviction)"): request at most one human per PR — never a team — and only when the resolution names exactly one owning engineer. When you do request, make it a real GitHub request with gh pr edit <PR> --add-reviewer <login>; a /cc @engineer line in the PR body puts nothing in the engineer's review queue (PRs #414–#417 named reviewers in prose and got zero reviews).
An individual unresolved path is non-fatal — other paths usually resolve the same owner. An empty or multi-owner result means no request at all: open the PR with no requested reviewer and record why in the run output. There is no fallback reviewer, and never re-add a reviewer a human removed from the PR.
Expect warp-server-only findings to resolve to nothing. The warp client repo's ownership file has a root rule, so nearly any path in it resolves. warp-server's does not, and whole areas — the /factory handlers among them — carry no entry, so a finding whose only source file is a warp-server handler resolves to no owner and its PR opens with no requested reviewer. Report it that way rather than as a resolution failure. Do not hardcode an owner here to paper over it — the fix belongs in warp-server's ownership file, and once an entry exists resolution starts working with no change to this skill.
PR strategy: one PR per feature
Ship each documented feature as its own focused PR so the owning engineer reviews only their area. Do NOT bundle unrelated features into a single mega PR.
-
One feature → one PR. Each documented feature (or standalone correction, e.g. a duplicate-heading fix) gets its own branch and PR, titled and scoped to that item, with the owning reviewer assigned. This is the default; the rules below are the only exceptions.
-
Group only when features share a doc file or owner. Two features that edit the same page (e.g. tab groups and drag-a-tab-to-another-window, both in
terminal/windows/tabs.mdx) go in one PR — separate PRs touching the same file would conflict, and they usually share an owner anyway. Prefer grouping by owning engineer when the same file is involved. -
Collect all surface-map + snapshot edits into a single companion "audit bookkeeping" PR.
references/feature_surface_map.mdandreferences/surface_snapshot.jsonare shared files; editing them across many feature PRs causes merge conflicts, and the snapshot is a wholesale regen. Put everyFlag -> pagemapping, ignore/internal/gated:entry, and the--update-snapshotregen into one bookkeeping PR. Its mappings may point at pages that land in the sibling feature PRs — map hygiene only requires the target page to exist on the base branch, so the bookkeeping PR merges independently of those feature PRs, in any order. If a run documents exactly one feature, fold its map + snapshot changes into that single PR and skip the companion. -
Keep at most one bookkeeping PR open. Extend the open one rather than opening a second. The independence above holds against feature PRs; it does not hold against another bookkeeping PR. Two of those edit the same shared files, and
surface_snapshot.jsonis regenerated wholesale, so a conflict between them cannot be resolved by hand — it has to be regenerated on the merged tree. Look for an existing one before opening yours:gh pr list --state open --search '"bookkeeping for" in:title' \ --json number,headRefName,title,reviewDecisionMatch on the quoted phrase, not the two words separately. An unquoted
missing_docs bookkeeping in:titlesearch ANDs the two words anywhere in the title, so it also matches this rule-only PR's own title ("...keep at most one bookkeeping PR open") — the next run would then check out and extend this PR's branch instead of a real bookkeeping PR.If one exists and
reviewDecisionis notAPPROVED, check out its branch and add this run's map entries and ledger rows on top, then re-runcheck_new_release.py --commitand--update-snapshotthere so the marker and snapshot stay a single regen covering every release the PR now carries. Update its title and body to name them all. The one exception: ifreviewDecisionis alreadyAPPROVED(it's about to merge), wait for the merge and branch from the result instead of extending it.For the search to find it, title every bookkeeping PR
chore(missing_docs): bookkeeping for <version>and name its branchmissing-docs/bookkeeping-<version>. The title keeps the repo's existing prefix style (seecreate_pr→ Best Practices) while carrying thebookkeeping forphrase the search matches on. #614 and #624 used two different naming schemes and neither run looked for the other's PR, which is how both ended up adding the same two/factorymap entries. -
API spec gaps stay separate — released endpoints go through the
sync-openapi-specskill as their own change, never bundled into a feature PR. -
Validate once, then split. Run
npm run buildon the combined working tree (all features together) to confirm everything compiles —npm cifirst if the sandbox has nonode_modules— then peel each feature onto its own branch offmain(e.g.git checkout <base> -b <branch>thengit checkout <combined-ref> -- <files>). Each feature branch is then a strict subset of the already-validated tree. -
List any deferred findings in the most relevant PR body (or the bookkeeping PR) so nothing is silently dropped.
Drift-watch mode (recurring scheduled agent)
This is the end-to-end workflow for the scheduled cloud agent that keeps docs in sync with the product. Each run:
-
Release gate: check whether a new stable release has shipped since the last processed run. The schedule runs daily so it can catch a release whenever it lands, but the work only happens once per release:
python3 .agents/skills/missing_docs/scripts/check_new_release.pyExit
0means a new stable release is available — continue. Exit10means no new release; record the no-op outcome in run output and stop. Exit1is a fetch or parse failure; report it and stop rather than proceeding as if nothing shipped. Exit2withcan't open fileis not a gate outcome at all — it ispython3reporting the wrong working directory.cdto the docs repo root and re-run.The gate also prints any
oz_updatesbullets for the release. Keep them — they are platform-side changes the audit cannot see, and this is the only place they surface.Do not update the state file yet. It is written in step 5, after triage, so a run that crashes mid-triage retries the same release instead of skipping it.
-
Audit: run both modes and save reports. Pass explicit repo paths; verify exit code 0 — if the script exits 2, STOP and report the environment problem instead of concluding "no gaps":
python3 .agents/skills/missing_docs/scripts/audit_docs.py \ --warp ../warp --warp-server ../warp-server \ --diff --output /tmp/docs_audit.json -
Triage: read
references/changelog_decisions.mdfirst and drop any changelog item already decided. Read the ledger and surface map as they stand on any open bookkeeping PR too, not only the copies onmain— a verdict recorded in an unmerged PR is still a verdict, and re-triaging it burns the run and produces duplicate map entries. Then work throughsurface_changesandchangelog_review(what changed since last run), then standing coverage findings (high → medium → low) across all categories: features, CLI, API, slash commands, settings, stale doc references, unlisted pages, map hygiene, staleness.Apply
.agents/references/docs-worthiness-criteria.mdto every remaining item before deciding anything else. The default is no docs; the burden is on the change to earn a page. Record a verdict for each item with the gate it passed (or the disqualifier that stopped it) and the concrete evidence — a setting key, CLI flag, quoted error string, changed default, or API field. Restating the changelog entry is not evidence. Expect most items to resolve to "no docs needed"; a run that passes everything it looked at has not applied the gate.For each item that passes, decide: update an existing page (preferred), draft a new page, or update the OpenAPI spec via
sync-openapi-spec. For each item that does not, decide: no docs needed, deferred with a blocking condition, a surface-map entry (documented elsewhere), or an ignore/internal/allowlist entry with a comment. -
Draft: follow Phase 3 for every item that needs docs. Every drafted page or substantive page update needs a content design plan first, carried into its PR body.
-
Update references: append every verdict from step 3 to
references/changelog_decisions.md(rejections included), record the processed release withcheck_new_release.py --commit, apply surface-map edits, then regenerate the snapshot:python3 .agents/skills/missing_docs/scripts/check_new_release.py --commit python3 .agents/skills/missing_docs/scripts/audit_docs.py --update-snapshot -
Validate: if doc pages changed, run
npm ci && npm run build— a fresh sandbox has nonode_modules, and the build is the only validation this repo has. Then re-run the audit and confirm the addressed findings are gone. -
Route the reviewer (at most one, only with conviction): resolve the owning engineer with
scripts/suggest_reviewers.py(see Reviewer routing), passing the source files behind the addressed findings. Request a review only when exactly one owner resolves — one human per PR, never a team, never a substitute. When nothing resolves (or several distinct owners do), open the PR with no requested reviewer and record why in the run output; that is a valid outcome, not a run failure.Use the snippet in the
create_prskill under "Request a reviewer (at most one, only with conviction)" — it is the canonical copy; do not paste a second version here. It distills the resolution to a single human, skips the request when the PR already has a reviewer, and never re-adds a reviewer a human removed. Feed it the reviewers fromsuggest_reviewers.py --reviewers-only, using the source files behind the addressed findings.When you do request an owner, a real request (
gh pr edit --add-reviewer) is what counts — naming the engineer in the body is not a request; that is exactly how #414–#417 ended up with zero reviews. Mention and request the same single engineer together, or do neither. -
Open one PR per feature following the PR strategy above (not a single mega PR): one focused PR per documented feature (grouping only features that share a doc file or owner), each carrying its content design plan as a section in the PR body, plus the run's bookkeeping changes to
feature_surface_map.md,changelog_decisions.md,last_release_processed.json, andsurface_snapshot.json. Extend the open bookkeeping PR if there is one; open a new one titledchore(missing_docs): bookkeeping for <version>only if there is not. Use thecreate_prskill: every drafting PR body opens with the required## What this feature doessummary, and every PR goes through the reviewer routing in step 7 before the run is done (which may legitimately end with no requested reviewer). Summarize remaining (deferred) findings in the relevant PR body so nothing is silently dropped.
A run that gates out every candidate is a successful run. It opens no feature PRs and only the bookkeeping PR recording the verdicts. Do not manufacture work to justify the run.
Schedule setup
Two configuration choices decide whether the schedule behaves, and neither is visible from the prompt below:
- Select a cloud agent, not Quick run. Quick run executes as the calling user, so its pull requests are authored by that person — which for a schedule means whoever created it. Selecting a cloud agent runs as that agent instead, and with team GitHub authorization configured its pull requests are authored by the Warp Factories GitHub App. Any schedule that opens PRs wants the agent. See Cloud agent accounts.
- Give that agent this skill and nothing else. A run inherits every skill attached to the agent it runs as, and the schedule form will not let you detach an agent-level skill. Pointing drift-watch at a general-purpose docs agent therefore pulls that agent's other skills into every run — and a weekly release-updates skill that defaults to running all of its tasks will do exactly that, daily. Create a dedicated agent rather than reusing one that already carries other work.
Recommended scheduled-agent prompt (copy when setting up the agent):
Run the missing_docs skill in drift-watch mode. Work from the docs repo root — every path below is relative to it, and a python exit code of 2 with "can't open file" means you are in the wrong directory, not that a check failed. First run .agents/skills/missing_docs/scripts/check_new_release.py; if it reports no new stable release, record the no-op outcome and stop. Otherwise use the audit script with explicit --warp (public warpdotdev/warp checkout) and --warp-server paths and --diff. If the script exits non-zero with skipped audits, report the environment problem and stop. Otherwise read references/changelog_decisions.md and drop already-decided items, then triage the remaining surface_changes and changelog_review findings plus high/medium coverage findings against .agents/references/docs-worthiness-criteria.md. The default is no docs: record a verdict and concrete evidence for every item, and expect most to fail. For items that pass, write a content design plan per .agents/references/content-design-plan.md before drafting, prefer updating an existing page over creating a new one, and use the sync-openapi-spec skill for API spec gaps. Update the surface map for every triaged flag, append every verdict to changelog_decisions.md, and regenerate the surface snapshot with --update-snapshot. Resolve reviewers by running .agents/skills/missing_docs/scripts/suggest_reviewers.py --reviewers-only against the source files behind each addressed finding. Open one focused PR per documented feature (grouping only features that share a doc file or owner), each opening with the required "## What this feature does" summary and carrying the content design plan as a section in its body, plus the bookkeeping changes to feature_surface_map.md, changelog_decisions.md, last_release_processed.json, and surface_snapshot.json. Before opening a bookkeeping PR, search for an open one with gh pr list --state open --search '"bookkeeping for" in:title' --json number,headRefName,title,reviewDecision — the quoted phrase, not the two words separately, so the search doesn't also match this rule-only PR's own title. Extend that branch instead of opening a second, unless reviewDecision is already APPROVED, in which case wait for the merge and branch from the result; two open bookkeeping PRs conflict on a snapshot that is regenerated wholesale. Title a new one "chore(missing_docs): bookkeeping for ". Request at most one reviewer per PR with gh pr edit --add-reviewer, and only when suggest_reviewers.py resolves exactly one owning engineer — never request a team, never substitute a fallback person when nothing resolves, and never re-add a reviewer a human removed from a PR. A PR with no requested reviewer plus a note explaining why is a valid outcome. List any findings you deferred in the relevant PR body.
Invocation modes
The user can trigger any subset:
- "Run a docs audit" or "Check docs coverage" → Phase 1 only
- "What changed since the last audit?" → Phase 1 + 2 (
--diff) - "Draft docs for [specific gap]" → Phase 3 only (skip audit)
- "Find and fix missing docs" → Phases 1–3 end-to-end
- Scheduled/recurring run → Drift-watch mode
Drafting standards
- Produce complete, ready-to-commit markdown — not outlines or stubs
- For CLI docs: include command syntax, all flags with descriptions, practical examples
- For feature docs: lead with user benefit, include how-to, cross-reference related features
- For API docs: include request/response schemas, auth requirements, curl examples
- Use
codebase_semantic_searchandgrepon source repos for technical accuracy
Tests
The skill's scripts have a stdlib-only test suite (no third-party dependencies):
python3 .agents/skills/missing_docs/scripts/test_suggest_reviewers.py
python3 .agents/skills/missing_docs/scripts/test_audit_docs.py
python3 .agents/skills/missing_docs/scripts/test_check_new_release.py
test_check_new_release.pyunit-tests the release gate with the network stubbed: exit-code contract (0 new / 10 no-op / 1 fetch failure), that a fetch failure is never reported as "no new release", that a plain check never writes state, and the full check → commit → no-op → next-release cycle.test_suggest_reviewers.pyunit-tests reviewer resolution (CODEOWNERS matching, last-match-wins, user/team split, dedup, unresolved paths).test_audit_docs.pyruns behavioral checks against the sibling code repos — clean exit, completeness accounting (unaccountedempty), category/severity scoping, fail-loud (exit 2) on a missing repo, snapshot round-trip, and research-preview deferral (the public/private boundary) — and skips gracefully when those repos aren't checked out.
References
references/feature_surface_map.md— curated mapping of flags/commands/routes/slash commands/settings to doc pages, ignore list for internal flags, allowlist for intentionally unlisted pages, theinternalsentinel for surfaces that intentionally have no public docs, and thegated:<Flag>sentinel for CLI/API surfaces deferred until their gating flag goes GA. Update it with every docs PR that ships a feature.references/surface_snapshot.json— generated snapshot of all code surfaces used by--diff. Regenerate with--update-snapshot; never hand-edit.references/last_release_processed.json— the release gate's state: which stable version was last triaged. Written bycheck_new_release.py --commit, never by hand. Deliberately separate fromsurface_snapshot.json, which is regenerated wholesale and would lose the marker. Delete it to force a re-run of the current release.references/changelog_decisions.md— append-only ledger of docs-worthiness verdicts on changelog items. Read before triage to skip already-decided items; append a row for every item evaluated, rejections included. Commit it in the companion bookkeeping PR.references/stale_terms.md— renamed/removed-feature terms to flag during staleness audits. Pure terminology/style policing belongs to thestyle_lintskill..agents/references/docs-worthiness-criteria.md— the gate that decides whether a finding should produce docs at all. Applied during triage, before any drafting..agents/references/content-design-plan.md— the audience, problem, goals, and content type decisions required before drafting a page that passed the gate.scripts/check_new_release.py— the release gate. Compares the current stable version fromapp.warp.dev/client_versionagainstlast_release_processed.jsonso a daily schedule does per-release work. Run it first in drift-watch mode; run it again with--commitonly after triage succeeds.scripts/suggest_reviewers.py— resolves PR reviewers from the warp and warp-server.github/STAKEHOLDERSandCODEOWNERSfiles (CODEOWNERS-format, last-match-wins), given the source files behind each finding. Used by the drift-watch reviewer-routing step.AGENTS.md(docs repo root) — full documentation style guide