Skip to content

feat(docs-audit): anchor a docs page on the CLI command phrase a change implements - #9290

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-9230-docs-drift-command-anchors
Aug 17, 2026
Merged

feat(docs-audit): anchor a docs page on the CLI command phrase a change implements#9290
os-zhuang merged 1 commit into
mainfrom
claude/issue-9230-docs-drift-command-anchors

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes#9230

Adds a fourth anchor kind — command — to scripts/docs-audit/affected-docs.mjs, so a
change to a CLI command file anchors on the command phrase it implements
(packages/cli/src/commands/meta/resync.ts ⇒ the phrase os meta resync) instead of on
symbol spellings no page uses.

⛔ Not urgent, and the PR body should say so

The card measures this at 1 of 10 PRs in the #9192 sample and states plainly:
"Not claimed: that this class matters often." This is a bounded precision
improvement
, not a defect fix. It is worth landing because the miss is silent from the
reader's side, not because it is frequent.

The registry already existed — it is the filesystem

The card's own first question was "whether that registry exists in a machine-readable
form is not checked."
It does: the CLI is oclif, and oclif resolves command ids from
paths — topic = directory, command = filename. So the derivation reads the path. No
curated table is added
; a hand-kept ledger beside the convention would be a second
source of truth with nothing keeping the two honest.

Gated on the package declaringoclif in its manifest rather than on a hardcoded
packages/cli path, so a second CLI package would be covered on the day it lands, and a
src/commands/ directory in a package that is not a CLI is correctly ignored. Binary
names come from what the package declares (oclif.bin plus each bin key), so
os meta resync, objectstack meta resync and npx objectstack meta resync all match
behind one anchor token.

⭐ The shape guard is untouched for bare tokens

The existing lowercase shape guard is load-bearing — neutralising it takes one measured
PR from 19 pages to 49, and label / object match 82 / 113 of the 178 hand-written
pages. resync on its own is exactly that shape and stays dropped.

os meta resync is not that shape. The new kind is admitted beside the guard through
a named PHRASE_ANCHOR_KINDS set (route was already exempt for the same reason: a
multi-segment token distinctive by construction), never by loosening isCodeShaped.
Command phrases still face the corpus-share guard unchanged.

Two self-test pins hold that pair together: isCodeShaped('resync') must stay false,
and symbol / literal / sdk must stay outside PHRASE_ANCHOR_KINDS. If they ever
disagree, recall was bought by neutralising the guard — the one fix this card rules out.

Measured

Reverse verification, both legs predicted red in advance, run from a detached worktree
at each commit against both the origin/main script and this one.

Leg 1 — a CLI-command change reaches the page documenting it (07ad42463,
fix(cli): explain os meta resync's skip count):

beforeafter
anchorsMetaResync, resyncSkipExplanationLine+ os meta resync (command)
pages matched01content/docs/data-modeling/drivers.mdx, via os meta resync (command)

Leg 2 — the bare-token guard still drops lowercase single words. On 445ae4deb
(the auth-email change whose locals are the calibration specimen), both scripts produce
byte-identical output: 3 docs, 35 anchors, and
weakAnchorsDropped = ["label (symbol)","locale (symbol)","start (symbol)","subject (symbol)"].
Recall was bought without buying back the false positives.

No-regression sweep — 20 consecutive commits touching packages/, none of them touching
a command file: every one byte-identical between the two scripts.

Corpus precision of the new kind, over all 58 command files and 178 hand-written pages
(hub limit = 26 pages):

command id shapenmedian pagesmeanmaxover the hub limit
topic + command (os meta resync)3712.08150
top-level (os serve)21410.14381 — os validate, dropped and published in overbroadAnchors

Summed over all 58 commands, the phrase matches 290 page-hits where the bare filename
token would match 2365 — 8.2x more selective, and every hit is a literal command
mention rather than prose.

⚠️The reviewable trade, stated rather than buried. Top-level single-word commands
are the broad end. On ba2e3c172 — a comment-only edit to serve.ts — the list goes
from 2 pages to 19, all of which literally write os serve. They are true positives by
this tool's own definition ("a doc is affected when it NAMES SOMETHING THE CHANGE
TOUCHED"), and the corpus-share guard already catches the extreme case. No second
tunable was added for this; if a reviewer wants top-level commands held to a tighter cap
than the repo's one calibrated line, that is a deliberate follow-up, not something this
PR should decide silently.

A correction to the card's worked example

The card records the 07ad42463 change as documented on content/docs/deployment/cli.mdx.
Re-derived at that exact commit and on current origin/main: that page never names
os meta or resync at all. The page that documents the command is
content/docs/data-modeling/drivers.mdx (line 619). The class the card records is
real and reproduced exactly — 2 anchors, 0 pages, only the coarse 22-page package-mention
set — only the page attribution was wrong.

Shapes the derivation declines, reported rather than guessed

Handled mechanically: top-level commands, topic + command, hyphenated ids, a topic
index.ts (which is the topic itself, not migrate index), and arbitrarily deeper
nesting — depth is read off the path, so a deeper topic tree needs no change here.
Measured on this tree: 45 commands at the root, 43 under 11 topics, one level deep.

Declined paths are collected in a new unmappedCommandFiles JSON field and named in the
summary line, in the same spirit as anchorlessChanges — the derivation declining a
shape must be readable, never inferred from a gap. Measured: all 58 real command files
derive a phrase, and the 30 declined paths are all test files (already excluded upstream).

One shape is knowingly out of reach and documented rather than detected: a command that
overrides its id in code (static id / static topic) can disagree with its path.
Measured on this tree — zero commands declare static topic, and the single static id
(init.ts) agrees with its path. The failure direction if that ever changes is a phrase
matching no page, which the published anchor list makes visible; not a false positive.

Gates

Re-derived from the actual changed path with
node scripts/pm/dispatch-gates.mjs scripts/docs-audit/affected-docs.mjs — it named the
same two families the dispatch did, no additions.

All run at 19cd67b84 (the final commit):

  • pnpm check:docs-audit-scope — green; affected-docs --self-test155 cases pass
    (115 on origin/main; 40 new pins), scope in sync with 178 hand-written docs
  • node scripts/docs-audit/check-affected-docs.mjs — green, 155 cases
  • pnpm check:nul-bytes — green, 6066 files; plus a direct control-byte grep over the
    changed file

No changeset: the diff is one root scripts/ file and publishes nothing — skip-changeset.

Out of scope: #9282 targets this same file (docs-drift being blind to anchorless files)
and is deliberately held back so there is one writer at a time; it is untouched here.


Generated by Claude Code

…ge implements
Adds a fourth anchor kind, `command`, to the docs-drift derivation: when a changed
file sits under an oclif package's `src/commands/**`, the phrase `os <topic> <name>`
is derived from its path and matched against the hand-written corpus.
The registry this needs already exists as the oclif filesystem convention (topic =
directory, command = filename), so the derivation reads the path — no curated table
beside it to drift.
The lowercase shape guard is untouched for bare tokens. `resync` alone is exactly
the shape it exists to drop; `os meta resync` is a multi-word phrase that is
distinctive by construction, so the new kind is admitted beside the guard via
PHRASE_ANCHOR_KINDS rather than through it. Command phrases still face the
corpus-share guard.
Measured: 07ad424 goes from 0 matched pages to the page naming the command.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@os-steveos-steve added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/m labels Aug 17, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32031729217 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (3/3) — 失败步骤: Run this shard's tests(日志不可读,点进 job 看)

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 2 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding][devx] The docs-drift anchor derivation cannot see a CLI command name — os meta resync is a lowercase word, so the shape guard drops it

3 participants

@os-steve@os-zhuang@claude