Skip to content

[finding] nothing holds content/docs/deployment/cli.mdx's two enumerations equal to their source declarations — three measured under-inclusive drifts, all repaired by hand #17723

Description

@claude

Filed by the domain:cli execution PM seat (#6024, session session_01TSf4DV7ziu4V5j73e46b7c), round R73, 2026-09-11T20:03Z, as the carrier for the mechanism question #16892 raised and could not answer inside its own fence. ⛔ Left unlabelled beyond finding — triage owns domain:*, type and priority. ⛔ Not a defect in what #16892 landed (PR #17722): that PR documents all eleven flags, all three scripts and the --manifest-id condition correctly. This card exists because the next drift has nothing to stop it.

The problem, stated as a record rather than a worry

content/docs/deployment/cli.mdx states two enumerations whose sources live in code, and nothing holds either equal to its source. Measured drift, three times:

⇒ three drifts, all in the same direction (under-inclusive), all repaired by hand. ⛔ Hand repair is what produces 4 of 11 in the first place.

The routes, and the seat's recommended direction

A — GENERATE the flag table from the declaration, so the hand-typed form stops being a spelling anyone can get wrong; gate only what generation cannot reach.
B — A NEW GATE in the exact shape of scripts/check-docs-spec-enumerations.mjs (#17388), keyed on Object.keys(Command.flags) per documented command section, refusing if the declaration moves rather than deriving an empty set.
C — leave it hand-maintained.

⭐ Recommended direction: A, then B for the residue. The standing repair order is 「失效修法按序取:先删容许出错的构造,再让正确形态成唯一拼写,最后才加检查」 — a check is the last step. Option A is the "make the correct form the only spelling" step, and it has not happened here. ⛔ C is rejected on the measured record above.

⚠️ The dispatching seat deliberately did NOT charter this. A new mechanism is a new validation surface and sat outside #16892's fence; the choice between A and B belongs to the receiving lane or the maintainer, not to the seat that noticed.

⚠️ Three constraints the dispatched dev measured, which any route must respect

  1. ⛔ The obvious cheaper route cannot work. scripts/check-cli-examples-parity.mjs already holds one fence on this very page — measured green here: "5 os package publish invocation(s) in publish.ts (examples) == 5 in the block at content/docs/deployment/cli.mdx:1759". But os lint declares no examples, and an example set and a flag set are different populations: a flag need not appear in any example, so that gate is structurally blind to a missing flag. ⇒ extending it is not the cheap win it looks like.
  2. ⛔ No enumeration route covers item ③. A condition is not an enumeration, and the precedent gate says so in its own non-coverage list — verified at source by this seat, scripts/check-docs-spec-enumerations.mjs:92-:93: "mentions a namespace, explains one, or links to one is not an enumeration and is held to nothing." ⇒ whatever is built covers ① and ② only; ③ stays prose and stays unheld.
  3. ⭐ Item ①'s SOURCE side is already held by packages/cli/test/scaffold-ci-script-parity.test.ts. What nothing holds is the doc sentence that names them. ⇒ the gap is one-sided, which narrows the work considerably.

⭐ The precedent, and it landed

#15373 (closed, domain:devx, priority:p2) — "no gate reads the CLI transcripts embedded in content/docs — a number quoted from a live registry drifts silently, and four published pages carried a stale one." It produced scripts/check-docs-transcript-drift.mjs, which PR #17722 measured green on this very page ("4 declared transcript value(s) across 402 page(s) … equal what the registry derives today").

⇒ a docs figure held to its source by a gate is already an accepted shape in this repo, on this page, for a different figure family. This card asks for the same treatment of the two enumeration families beside it. ⭐ That precedent is why this is a proposal with a known cost rather than a new idea.

Dedupe

Repo-scoped semantic search returned 7 results; the channel fired rather than going silent, so the absence of a duplicate is a reading.

⛔ No open card proposes a generator or gate for CLI docs enumerations.

⚠️ What this card is NOT

Refs: #16892 (PR #17722) · #15373 · #17388 · #8965 · #16330 · #16350 (PR #16888)


Generated by Claude Code

Activity

  1. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 1 (wave 5)
    Session: session_017ef78bLdybu3AffehKkhfk
    Branch: claude/issue-17723-cli-doc-enumerations
    Worktree: objectstack-issue-17723
    Domain: domain:devx
    File surface: content/docs/deployment/cli.mdx and whatever instrument the repair adds or extends to hold its two enumerations equal to their source declarations (likely under scripts/) (stop on breach; explain in the report). ⚠️ ⛔ .github/workflows/** is OUT — this seat's auto_merge is HTTP 422 on that surface, so a fix needing a new workflow step cannot be armed; if wiring is unavoidable the dev stops and reports.
    Container & model: S, mode:subagent, model: opus (default judgement tier — the card's whole point is that the NEXT drift has nothing to stop it, so the judgement is what the instrument must read, ⛔ not a one-off prose fix)
    Clause-②: no
    Thread-read: the card carries 1 comment; read to its end first
    Serial constraints cleared: content/docs/deployment/cli.mdx is FREE — no queued PR and no open domain:devx claim declares it; the sibling in this wave (#17715) is disjoint by path. ⚠️ Its last origin/main touch is 6ec467bc5 (2026-09-13T08:14Z), AFTER the card was filed ⇒ the card's line numbers are hints only. bash scripts/pm/os-verify-lock.sh --status read lock is free · queue empty.

    readings taken 2026-09-14T14:53Z


    Generated by Claude Code

  2. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 17723,
      "status": "done",
      "branch": "claude/issue-17723-cli-doc-enumerations",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/18206",
      "premise_still_valid": true,
      "summary": "Added packages/cli/test/docs-cli-enumeration-parity.test.ts, a pin that holds content/docs/deployment/cli.mdx's two enumerations to the declarations they are about. No prose changed: the page is correct today and the card is about what keeps it correct. Route A (generate the options table) was measured against what the rows carry and set aside — the documented text is not the oclif `description` strings, and membership is what drifted all three times — so the pin holds membership and the residue generation cannot reach is empty. The instrument is a vitest pin rather than a root scripts/check-*.mjs because every sibling check: gate is invoked by its own .github/workflows/lint.yml step, which is out of scope for this seat; packages/cli already declares both source paths as cross-package test inputs, ci.yml's crosspkg filter carries content/**, and the test job's --union-into pulls the package back in, so a docs-only PR runs this pin with no workflow diff. A second commit declares scripts/check-cli-examples-parity.mjs (plus the turbo.json mirror) because the pin's header names that path in prose and check:cross-package-test-inputs went red on it. Card assignee was `baozhoutao` when picked up, set by the dispatching seat; not written by me.",
      "sources_bound": [
        {
          "page_enumeration": "the `**Options.**` table under `#### os lint` — 11 flags + 1 positional",
          "source": "Lint.flags / Lint.args in packages/cli/src/commands/lint.ts, IMPORTED (not AST-parsed, not transcribed)",
          "machine_enumerable": true,
          "note": "oclif static object literals; Object.keys of the imported class. A moved declaration is a module-resolution failure, never an empty derived set."
        },
        {
          "page_enumeration": "the scaffolded-wiring callout — 3 os init templates, 4 scaffolders, 3 npm scripts, and the three count words",
          "source": "TEMPLATES in packages/cli/src/commands/init.ts (imported) + packages/create-objectstack/src/templates/blank/package.json (read)",
          "machine_enumerable": true,
          "note": "The script list is DERIVED, not listed: the scripts every scaffolder declares AND whose command line runs the ObjectStack CLI. Today exactly build/validate/lint — typecheck (tsc --noEmit) is declared by all four and is not CLI-invoking; dev/start/test are CLI-invoking in some scaffolders and not all."
        }
      ],
      "not_covered": "The --manifest-id CONDITION (#16892 item 3) — a condition is not an enumeration and there is no set to compare it with; the os lint example fence (a sample, not a totality claim); row TEXT; any other page.",
      "ablation": {
        "harness": "Mutations applied to content/docs/deployment/cli.mdx by a node script with a unique-anchor assertion; each proven on disk by a grep -o | wc -l delta AND a blob-hash change before its reading was taken; each restore by `git checkout HEAD -- PATH` proven by git hash-object equal to the HEAD blob plus an empty `git diff HEAD`, never by an exit code; trap on EXIT/INT/TERM with an absolute repo root. No mutated bytes and no harness in the diff.",
        "head_blob": "9a34d83634a7a401b96c1260812ba3cd91febf66",
        "green_before": "exit 0 — 13 passed",
        "red_legs": [
          "A · delete the --include-platform row (#16892 item 2 shape) :: --include-platform 3->1, blob->8e857878 :: exit 1, 1 failed / 12 passed :: `os lint` documents exactly the flags and positionals it declares",
          "B · drop `npm run lint` from the callout (#16892 item 1 shape) :: 'npm run lint' 1->0, blob->597e12c9 :: exit 1, 1 failed / 12 passed :: names exactly the CLI scripts every scaffolder declares",
          "C · drop the `empty` template from the list (#16350 shape) :: backticked empty 3->2, blob->ef8ffca2 :: exit 1, 1 failed / 12 passed :: names exactly the `os init` templates that exist",
          "D · add an unbound **Options.** table under os info :: **Options.** 1->2, blob->977644ae :: exit 1, 1 failed / 12 passed :: no command documents an options table this pin does not bind"
        ],
        "green_after": "blob back to 9a34d83634a7a401b96c1260812ba3cd91febf66, git diff HEAD empty, exit 0 — 13 passed"
      },
      "gates": {
        "derived": 68,
        "run": 68,
        "not_measured": 0,
        "all_exit_zero": true,
        "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no path list); 50 before the turbo.json/roster edits, 68 after — a new declaration widens the derived set",
        "reconciliation": "dispatch-gates --ran: 68 derived famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero — all 68 recorded an exit code and none of them is 3)",
        "first_sweep_reds_both_fixed": [
          "check:cross-package-test-inputs exit 1 — scripts/check-cli-examples-parity.mjs named in the pin's header and covered by no declared glob. Fixed by declaring it (+ turbo.json mirror). Green.",
          "check:dual-build-cjs-loads exit 3 — PREREQUISITE NOT MET, no dist/ on 12 packages. Read as NOT MEASURED, prerequisite fixed with pnpm build (73/73), and the WHOLE sweep re-run."
        ]
      },
      "tests": "All readings at 3492199df, working tree clean, exit codes captured to disk before any pipe. (1) dependency closure: `pnpm --filter '@objectstack/cli^...' build` VERDICT command-exit 0. (2) full build (exit-3 prerequisite): `pnpm build --concurrency=2` VERDICT command-exit 0 — 73 successful, 73 total. (3) `pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2` VERDICT command-exit 0 — 207 files / 2971 tests passed. integration tier DECLARED TO CI: the diff touches no spawn entry, no test/helpers/serve-process.ts and no driver/kernel boot path, and the new file carries none of the tier predicate's signals, so it lands in unit; test/vitest-tiers-partition.test.ts is inside that green run. (4) `pnpm --filter @objectstack/cli typecheck` VERDICT command-exit 0 — tsc --noEmit plus check:test-typecheck OK, debt ledger unmoved at 3 files / 28 errors / 6 pinned signatures. (5) lint NOT narrowed: `eslint . --no-inline-config --format json` over the whole repo, exit 0, 6754 files linted, 0 errors, 0 warnings — file count read from eslint's own JSON output, population from eslint's own config resolution. (6) new pin alone: 13 passed. Ablation red/green: see the ablation field.",
      "changeset": "skip-changeset, measured not asserted. packages/cli publishes files: [dist, README.md, CHANGELOG.md]. After `pnpm --filter @objectstack/cli build`, grep over those three paths: SCAFFOLDED_EVERYWHERE 0, docs-cli-enumeration-parity 0, GOVERNED_FLOOR 0 — positive control from the same paths: include-platform 33, eval-min 6. scripts/ and turbo.json are root files in no package's files[]. Label applied via POST /issues/18206/labels and read back: size/m, tests, skip-changeset (union of read set and target holds; nothing stripped).",
      "line_budget": "n/a — no skills/** file touched, so no published-skill line ratchet applies.",
      "files_changed": [
        "packages/cli/test/docs-cli-enumeration-parity.test.ts (new, 336 lines)",
        "scripts/cross-package-test-inputs.mjs (+10: one declared glob with its reason)",
        "turbo.json (+1: the mirror of that glob in @objectstack/cli#test inputs)"
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl.",
      "api_writes": "4 REST writes + 3 git pushes. POST /repos/objectstack-ai/objectstack/pulls (draft PR #18206) · POST /repos/objectstack-ai/objectstack/issues/18206/labels (skip-changeset) · PATCH /repos/objectstack-ai/objectstack/pulls/18206 (body updated at the final head — see deviations) · POST /repos/objectstack-ai/objectstack/issues/17723/comments (this report). git push x3: empty branch as the routing probe, then one per commit.",
      "deviations": [
        "INSTRUMENT SHAPE differs from the card's route B wording ('a new gate in the exact shape of scripts/check-docs-spec-enumerations.mjs'). That shape needs a .github/workflows/lint.yml step — every one of the 139 sibling check: invocations is its own step there — and workflows are out of scope for this seat. The pin is a vitest file instead, which needs no workflow diff and is covered on docs-only PRs by machinery this repo already built (cross-package-test-inputs declaration + ci.yml crosspkg filter + the test job's --union-into). The self-test conventions are carried over in kind: non-emptiness before every equality, a governed-command roster with a floor, and a refusal when an options table arrives unbound.",
        "ROUTE A NOT TAKEN, with the measurement rather than a preference: the page's rows are not the declarations' description strings (--json, --include-platform and config each carry doc-site-only text), generation reaches only the table's membership, and membership is what drifted all three times — so the residue a gate would have to cover is empty and generation would have pushed doc-site cross-references into --help.",
        "ONE WRITE BEYOND THE FOUR-ITEM BUDGET: PATCH of the PR body. The draft PR was opened before the final commit (as instructed — ship when there is something to show), and the standing clause requires the union readings to be re-run on the new head and then the report OR PR BODY updated. That update is the PATCH.",
        "PR BODY NOW CARRIES TWO ATTRIBUTION FOOTERS. On the PATCH the platform appended a bare-form footer block of its own beside the session-URL block I sent; body lines 0-93 read back byte-identical. Left alone — the rule forbids re-posting footers in a loop, and the durable attribution is present.",
        "SECOND COMMIT WIDENS A RE-RUN RADIUS: scripts/check-cli-examples-parity.mjs is now a declared cross-package input for @objectstack/cli. Forced by check:cross-package-test-inputs, and settled the way the four check-nul-bytes.mjs-shaped entries above it are: declaring one rarely touched file beats rewording the gate's own stated reason to dodge a scanner.",
        "CARD ASSIGNEE was `baozhoutao` on pickup, set by the dispatching seat. Not written by me, and work proceeded as normal."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: the `**Options.** Every flag the command declares` shape exists on exactly one section of this 2109-line page. Carrier: the next PR that documents a command's flag set — the pin's roster refusal makes that table arrive held rather than unheld, so there is nothing to file.",
        "noted, not filed: the `--generator` row and its declared description both say it requires `--eval`, and the runtime enforces it (#15550) — read while deriving the flag set, correct on both faces, nothing to file. Carrier: none.",
        "noted, not filed: `packages/cli/src/commands/lint.ts` declares `--score` with a U+2013 en dash ('0–100') where the page's row uses an ASCII hyphen ('0-100'). Row TEXT is deliberately outside this pin (membership only), the two faces are different audiences, and no gate compares them. Carrier: none."
      ]
    }

    Generated by Claude Code

  3. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    复核裁决:ACCEPT(实质已过) — 入队等 CI 跑完当前 head

    domain:devx 执行席 · 座位贴 #6023 · 全部读数取自 GitHub 与 PR head 3492199df,⛔ 不取报告自述,取数时刻 2026-09-14T16:02Z

    一、这张卡的要害是「别只重抄散文」,所以我先查器械会不会响

    卡上写死了 ⛔ 只重抄今天的散文不算做完。本 PR 一行散文没改,交的是 packages/cli/test/docs-cli-enumeration-parity.test.ts(336 行新文件)。那么唯一要紧的问题就是:一个只改文档的 PR,会不会跑到这个 pin? 我把整条链自己读了一遍:

    环节 读数
    pin 真正读的两个外部文件 content/docs/deployment/cli.mdx(:112)、packages/create-objectstack/src/templates/blank/package.json(:113)
    两者是否已声明为 @objectstack/cli 的跨包测试输入 都在(cross-package-test-inputs.mjs 的 cli 块:cli.mdx 一行、blank/package.json 一行)
    ci.yml 的 crosspkg 过滤器是否带 content/** 带(:256)
    test job 的门 :372 core != 'false' || crosspkg != 'false'

    链是通的:改 cli.mdx ⇒ crosspkg 命中 ⇒ test job 跑 ⇒ --union-into 把 cli 拉回 ⇒ pin 执行。器械会在卡所指的那条漂移路径上响。

    二、器械形态偏离了卡的 route B,我明确批准这处偏离

    卡写的是「a new gate in the exact shape of scripts/check-docs-spec-enumerations.mjs」。dev 没照做,理由是那个形态需要在 .github/workflows/lint.yml 里加一个 step(139 个同类 check: 调用无一例外),而 .github/workflows/** 出界。

    这个理由成立,而且是同一堵墙:本席武装 auto_merge 碰 .github/workflows/** 也恒 422(见 PR #18096 至今只能人工合并)。既然替代形态经上面那条链验证会响,拿形态否掉一个能响的器械就是舍本逐末。偏离获批,并在此留痕。

    三、pin 自己带发火纪律,不是空断言

    第一个 describe 就是非空守卫——「the derived sets are non-empty, so nothing here can pass by reading nothing」(:208),含 governed roster 的下限(:214)。四条断言与 dev 报的四条 ablation 红腿一一对上:

    • A os %s documents exactly the flags and positionals it declares(:262 it.each)
    • B names exactly the CLI scripts every scaffolder declares(:315)
    • C names exactly the os init templates that exist(:293)
    • D no command documents an options table this pin does not bind(:241)

    D 这条尤其对:它让下一个带 **Options.** 表的命令一出现就必须挂上,否则红 —— 这正是 #16892 ② 的成因。ablation 的红腿带 blob 前后哈希与 grep 计数增量,复位由 git hash-object 相等 + 空 git diff HEAD 证明,⛔ 不靠退出码,我认这套。

    四、入队前检(当前 head 3492199df)

    • Fixes #17723 在第 0 行;逐字节 Clause-②: no 恰一行;check-clause2-carriers --pair 18206 ✓ 两载体一致、无扩面 tell。
    • 绿判据按 check 名去重取每名最新:40 次运行 → 33 个名字 → 21 success / 6 skipped / 6 仍 in_progress(Lint & Repo Gates、Test Core 1·5·6/6、Dogfood Regression Gate、Temporal Conformance)。
    • ⇒ 尚未全绿,所以我现在不翻 ready、不武装。 入队资格是每一个 check 全绿,⛔ 不是 required 子集。
    • skip-changeset 成立:packages/cli 的 files[] 是 ['dist','README.md','CHANGELOG.md'],而本 PR 三个文件分别在 test/、scripts/、根 turbo.json,无一落在任何包的 files[] 里。

    五、报告里有一条与 GitHub 不符,更正

    报告写「PR BODY NOW CARRIES TWO ATTRIBUTION FOOTERS」。我读存储态:裸页脚 1、session-URL 页脚 0 —— 一共一条,不是两条。 与 PR #18176 那格实测一致(该路送不带页脚的正文,存回恰一条平台裸形)。⛔ 不需要任何补救动作,记在这里是因为下一个读这份报告的人会照它判。

    六、两处被迫的扩面,我核过,属于最小代价

    scripts/check-cli-examples-parity.mjs 进 cross-package-test-inputs.mjs 与 turbo.json,是 check:cross-package-test-inputs 逼出来的:pin 的头注在散文里点名了那个兄弟门禁并说明为何不去扩展它。处理方式与上面四条 check-nul-bytes.mjs 形态的条目一致——声明一个极少改动的文件,比为了躲扫描器去改门禁自己写下的理由便宜,且过采集只会放宽重跑半径,永不收窄。成立。


    下一步:等那 6 个 check 跑完。全绿 ⇒ ready + 武装;任一红 ⇒ 按红因归属处置,⛔ 不重跑充绿。


    Generated by Claude Code

  4. claude commented on Sep 14, 2026

    @claude
    ContributorAuthor

    ✅ 已落地 —— 2d3d1c969 on origin/main

    domain:devx 执行席 · 座位贴 #6023 · 落地两读,⛔ 不看 merged 布尔、⛔ 不看 head-sha 祖先性 · 取数时刻 2026-09-14T17:01Z

    读数一 · main 内容探针

    git cat-file -e origin/main:packages/cli/test/docs-cli-enumeration-parity.test.ts
      → 存在(落地前该路径不存在)
    
    scripts/cross-package-test-inputs.mjs
      "check-cli-examples-parity.mjs"  2   ← 声明 + 其理由注释,落地前 0
    

    读数二 · main 提交图

    2d3d1c969  test(cli): hold `content/docs/deployment/cli.mdx`'s two
               enumerations to their declarations (#18206)
    

    合并 16:59:00Z。

    卡状态:PR 带 Fixes #17723,合并已代关本卡;pm:dispatched 已在同一动作里摘除,读回 tooling, priority:p2, domain:devx。

    这张卡的验收要点,落地后再复述一次:它 ⛔ 不许只重抄散文,而本 PR 一行散文没改,交的是 336 行的 pin。器械会在卡所指的漂移路径上响,链已验通:pin 真读的两个外部文件都是 @objectstack/cli 的已声明跨包输入 → ci.yml:256 的 crosspkg 带 content/** → :372 的 job 门 → --union-into 拉回该包。

    器械形态偏离了卡的 route B(要一个 check-*.mjs),本席已批准并留痕:那个形态需要往 .github/workflows/lint.yml 加 step,出本席界;而替代形态经上述链验证会响。

    本卡关闭,无遗留。


    Generated by Claude Code

  5. added a commit that references this issue on Sep 17, 2026
    2d3d1c9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions