docs(deployment/cli): derive the two under-documented enumerations from source, and state the manifest-id fallback condition - #17722
Conversation
…state the manifest-id fallback's condition `content/docs/deployment/cli.mdx` was short in three places, all derived from the sources rather than hand-added: 1. The scaffolded-scripts sentence said "these" (three author-time commands) and then named two scripts. Every scaffolder in the tree declares all three: the `os init` templates `app`, `plugin` and `empty`, and the `create-objectstack` blank template. 2. `os lint` documented 4 of the 11 flags it declares. The section now enumerates all 11 plus its positional, including `--include-platform`, which the command's own hidden-i18n hint tells the reader to re-run with, and `--eval`/`--generator`, whose precondition is now stated. 3. The `--manifest-id` row presented `local.` + a slug as an unconditional default. It is not one: a derived id is held to `PackageSchema.manifestId` exactly as an explicit one is, and a manifest name that slugifies to a digit-first segment is refused before any network call. That is a missing CONDITION, not a missing list member, so it is stated as one. Docs-only; no published package changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
Review of record — ACCEPTHead reviewed: Reviewed-by:
Gates and CI on this head
Item ② re-derived by this seat, from the source and then from the pageThe 11 flags, taken from Then counted on the page, before and after, each flag queried as the page spells it with a right-boundary guard so
⇒ 4 of 11 → 11 of 11, and the seven zeros are load-bearing because a control that could have failed for the reason I was worried about did not. ⭐ Item ③ is repaired as a CONDITION, not a list member — the one way this card could have failed while looking doneThe table row went from "Default: ⭐ And it supplies a reason the card did not ask for and that is better than the one it would have accepted: " Accepted as reported, with the parts worth carrying
The open question was ruled BEFORE this reviewThe mechanism fork is answered on the card (
VerdictACCEPT. One file, the only authorised write surface; both lists derived from source and re-derived here with a control that could have failed; item ③ repaired as a condition with a better reason than was asked for; three sources read and none edited. Landing armed by this seat. Generated by Claude Code |
Fixes #16892
All three items land on
content/docs/deployment/cli.mdx, the whole authorised write surface.packages/cli/src/commands/lint.ts,packages/cli/src/commands/init.tsandpackages/cli/src/commands/package/publish.tswere read as sources of truth and not edited.One file, 46 insertions, 3 deletions.
Both lists are DERIVED, not hand-extended
Triage's instruction, and its reason — "4 of 11 is what hand-maintenance produces". Neither list was inherited from the card; both were re-derived here, and both agree with the card independently.
② the eleven declared
os lintflagsDerived by parsing the
static override flagsobject literal inpackages/cli/src/commands/lint.ts— an AST read of the declaration, so nothing is transcribed, and the deriver exits non-zero on any member shape it does not recognise instead of reporting a short list:plus the command's one positional,
config. Documented before this PR:--score,--fix,--strict,--json— 4 of 11. The section now carries all eleven and the positional.The seven zero readings carry a control that shares their failure mode. All eleven flags were queried the same way — the literal flag spelling as this page writes it inside bash fences and prose backticks, with a right-boundary guard so that
--evalcannot match--eval-min. The four already-documented flags are the control, and they fire: in theos lintsection they read 2 / 4 / 1 / 1. So the query shape demonstrably matches a flag this page does document, wrapped and fenced exactly as the target flags would be. The other seven read 0 in the section and 0 across the whole page, while--jsonreads 41 page-wide for other commands. A control on the channel would not have been a control on the query.Two of the seven are worth naming.
--include-platformis the flagos lintnames in its own hint (platform built-ins: N i18n issue(s) hidden — rerun with --include-platform to audit them), so a reader who followed the hint reached a page that did not mention it. And--generatorhas a precondition nothing on the page stated: passed without--evalthe run exits 1 on both faces rather than accepting a flag no code outside eval mode reads.① the scaffolded script maps
Derived from the scaffolders themselves — the three
os initTEMPLATESmaps (AST ofinit.ts) and the checked-inpackages/create-objectstack/src/templates/blank/package.json:validatebuildlintos init --template appos init --template pluginos init --template emptycreate-objectstackblank templateFour scaffolders, all three scripts, everywhere — so the sentence's "these" (three commands) now names three scripts.
One thing worth recording because it looks like a defect and is not: the
os initmaps wirebuildtoobjectstack compilewhile the blank template wires it toobjectstack build.os buildis a subclass alias ofos compile(packages/cli/src/commands/build.tsextendsCompile), so both run the same command and the page's mapping claim was incomplete, not wrong.packages/cli/test/scaffold-ci-script-parity.test.tsalready holds the two sides equal on exactly the workflow's script subset, and records the other divergences as deliberate.③ is repaired as a CONDITION, not as a list entry
The card and its follow-up comment both insist on this, and it decides the remedy. ①② are enumerations naming fewer members than exist — a reader is under-served but never misled. ③ is a claim about a fallback's reliability: the
--manifest-idrow presentedlocal.plus a slug as an unconditional default, and a reader plans around "there is always a default".So no member was added to a list. The row now names the full derivation chain and defers, and a paragraph below the options table states the condition: a derived id is held to exactly the rule an explicit one is —
PackageSchema.manifestId, the schema for the very column publish writes — slugifying has no letter-first rule, so a manifest named2024 Appderiveslocal.2024-app, which that schema rejects, and the command refuses before any network call, quoting the schema and naming which source produced the id. Verified at the source:deriveManifestIdinpackages/cli/src/commands/package/publish.tsand the single gate in itsrun().The chain is named in full (
artifact.manifest.id, else a slug ofmanifest.name, else a slug of the artifact filename) because the remedy the refusal prints differs by source — a reader who is told only "a slug of the artifact name" cannot tell which of the two derived sources they are in.A mechanism is proposed, and deliberately NOT built
This is the sixth-plus card this round on a hand-typed enumeration going stale, and a cheap mechanism holding the documented list equal to the source would be worth more than a corrected list. But a new gate is a new validation surface outside this card's fence, so this PR proposes and stops. Recorded so the taker does not re-derive it:
scripts/check-docs-spec-enumerations.mjs(No gate holds the hand-written docs enumerations of@objectstack/specsubpaths and protocol namespaces equal to the exports map #17388) holds hand-writtencontent/docs/enumerations equal to what a declaration declares, with the derivation reading a constant inside the package and refusing if that constant moves rather than deriving an empty set. A flag-table sibling keyed onObject.keys(Command.flags)per documented command section is the same shape.scripts/check-cli-examples-parity.mjsalready holds one fence on this very page equal to a command'sexamplesarray — butos lintdeclares noexamples, and more to the point an example set and a flag set are different populations: a flag need not appear in any example, so that gate cannot see a missing flag at all.①'s source side is, separately, already held:
scaffold-ci-script-paritykeeps every scaffolder's script set equal to the workflow the on-ramp ships. What nothing holds is the doc sentence naming them.Verification
The lane's full derived gate union, plus
pnpm lint, whichscripts/pm/dispatch-gates.mjsdoes not name. Every exit code captured before any pipe (cmd > log 2>&1; EXIT=$?).node scripts/pm/dispatch-gates.mjs --commandsagainst this branch at5d5f9ab0(the tool reads the change set from the merge base itself: 1 path).pnpm lint: exit 0, run repo-wide —eslint . --no-inline-configover the whole population, so no narrowing needed and none claimed.check:doc-formula-expressionsandcheck:doc-security-postureprintedPREREQUISITE NOT METat exit 3;check:docs-transcript-driftthe same at exit 3; andcheck:skill-examplesrefused at exit 1 becausepackages/client-react/distheld no declarations — a prerequisite refusal wearing a finding's exit code, which is why the printed verdict and not the number was read. Afterturbo run build --filter=@objectstack/formula --filter=@objectstack/lint --filter=@objectstack/client-react, all four: exit 0.check-doc-anchors: 340 internal fragment links across 407 files all resolve to a real heading — this covers the one in-page link this PR adds.check-cli-examples-parity: the 5os package publishinvocations inpublish.tsstill equal the 5 in the block on this page.check-docs-transcript-drift: 4 declared transcript values across 402 pages still equal what the registry derives, and no undeclared block quotes one.check-docs-spec-enumerations,check-docs-single-h1,check-doc-frontmatter,check-docs-section-name,check-section-landing-index: exit 0.@mdx-js/mdx3.1.1): it emits 219364 bytes of JS. The control leg hands the same compiler the same page plus an unclosedCalloutand it is refused (Expected a closing tag), so the green is a reading and not a no-op. This measures syntax only.Build Docsjob. It is a 30-minute Next build of 400+ pages, over this repo's foreground limit, and the deriver names it as a path-scheduled CI job with no local invocation. The MDX compile above covers the one failure class this diff could introduce; the job itself is CI's.Changeset:
skip-changeset, measured rather than assumedNothing published moves. Measured over every non-private
package.jsonin the tree: zero published packages name anycontent/-shaped path infiles[]. Control:packages/spec'sfiles[]reads back all ten of its real entries (dist,json-schema,liveness,prompts,llms.txt,README.md, …), none undercontent/; andapps/docs, the only consumer of this tree, isprivate: true. So this diff publishes nothing from any released package, which is the one thing that label is for.Acceptance notes
--manifest-idrow was the only row in theos package publishoptions table this card reached. Whether the other rows on that table carry unstated conditions was not swept — it is outside this card's three items, and I did not widen to it. Carrier: the next card that touches that table.packages/specbyte was touched, and nothing undercontent/docs/releases/was touched.Generated by Claude Code