Skip to content

fix(cli): os init template descriptions match what srcFiles emits - #9774

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-9737-init-template-descriptions-match-emission
Aug 19, 2026
Merged

fix(cli): os init template descriptions match what srcFiles emits#9774
os-zhuang merged 2 commits into
mainfrom
claude/issue-9737-init-template-descriptions-match-emission

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#9737

The mismatch (re-measured on origin/main at the commit this branch was cut from)

packages/cli/src/commands/init.ts's TEMPLATES.app and TEMPLATES.plugindescription
strings claimed metadata kinds neither template's srcFiles map ever writes:

templatesrcFiles (re-measured)old description
appsrc/objects/index.ts, src/objects/__name___item.ts (2 entries, both src/objects/*)"Full application with objects, views, and actions"
pluginsrc/objects/index.ts, src/objects/__name___item.ts (2 entries, both src/objects/*)"Reusable plugin with objects and extensions"
empty{}"Minimal project with just a config file" (accurate, unchanged)

Confirms the card's own table exactly — #9666 (merged since the card's e196c6a9b reading)
only added a sharingModel: 'private' field to the rendered object content; it did not add
or remove any srcFiles entries.

content/docs/deployment/cli.mdx's template table carried the same false views claim on the
app row (its plugin row — "Reusable plugin package with objects" — was already accurate and
is untouched).

Fix (Option 1, per triage's auto-adjudication — all four prisms aligned)

  • app: 'Full application with objects, views, and actions''Full application with objects'
  • plugin: 'Reusable plugin with objects and extensions''Reusable plugin with objects'
  • cli.mdxapp row: drops the views claim, keeps the accurate barrel imports claim
    (src/objects/index.ts re-exports the object — that claim was never wrong)

Option 2 (emit a starter view/action so the description becomes true) is not in this PR —
it is a Feature needing maintainer appetite, explicitly out of scope per the card.
srcFiles maps are untouched; no template files added.

Pin

packages/cli/test/init.test.ts gains a generic assertion: for every template, if its
description claims a metadata kind (objects/views/actions/extensions), that kind's
src/<kind>/ prefix must have a matching srcFiles entry. This fails red against the
pre-fix wording (app/plugin claimed views/actions/extensions with zero matching
entries) and is template-count-agnostic, so a future template can't drift the same way
undetected.

Verification (all under the shared os-heavy-verify.lock, at head 6488f13c2)

  • packages/cli typecheck: clean (pnpm --filter @objectstack/cli typecheck)
  • packages/cli lint: clean (npx eslint packages/cli/src/commands/init.ts packages/cli/test/init.test.ts --no-inline-config)
  • pnpm --filter @objectstack/cli test (full package suite, --maxWorkers=2): 134/134 files, 1461/1461 tests
  • Reverse-verification of the pin: checked out the pre-fix init.ts from origin/main, ran
    test/init.test.ts alone → 2 failed / 30 passed, both failures on exactly the views/extensions
    claims; restored the fixed file via git checkout HEAD --, re-ran → 32/32 green
  • Gate union derived from actually-changed paths (node scripts/pm/dispatch-gates.mjs), all run and
    green: check:cross-package-test-inputs, check:doc-anchors, check:docs-audit-scope,
    check:docs-redirects, check:published-readme-links, check:role-word,
    check:query-options-erasure, check:engine-double-contract, check:where-matcher,
    check-type-check-coverage.mjs (structural — hidden-test-file count unchanged at 952, since
    init.test.ts already existed pre-fix and was already outside packages/cli's tsconfig.json
    include: ["src"]; I only edited an existing file, didn't add a new one), check-affected-docs.mjs,
    and @objectstack/spec's check:empty-state/check:liveness/check:strictness-ledger/check:variant-docs
    (spec moved on main's side of the merge)
  • After merging main (spec + several other packages moved): rebuilt @objectstack/spec,
    check:generated clean, rebuilt the CLI's full dependency closure, re-ran CLI typecheck and
    init.test.ts on the merged tree — all green

Zone 2 assumptions — one falsified

  • Exactly three built-in templates (app/plugin/empty) in packages/cli/src/commands/init.ts's
    TEMPLATESconfirmed.
  • cli.mdx is the only other docs location carrying the claim — confirmed (repo-wide grep,
    .ts/.tsx/.mdx/.md/.json, with a negative control that returned nothing and a positive
    control that found the known-accurate empty description). packages/create-objectstack has
    its own, unrelated TEMPLATES registry (different command — npm create objectstack@latest
    different templates, blank only) whose description does not claim views/actions/extensions;
    out of scope.
  • No test asserts the current wording — confirmed (added the pin above rather than weakening
    anything).
  • Falsified: the card's framing that os init --help shows these strings. Running
    os init --help for real (after building the CLI's dependency closure) shows only the flag's
    own generic description, Template: app, plugin, empty — the per-template description string
    is printed by printKV('Template', …)after the user has already invoked init with a
    chosen template, and in the docs table. Doesn't change the fix (same two strings, same location),
    but "at the moment they pick a template" is more precisely "immediately after they've picked
    one and the scaffold starts writing."

Generated by Claude Code


Generated by Claude Code

… emits (#9737)
`app` and `plugin` claimed views/actions/extensions their srcFiles maps never
write (both emit objects only). Reword to match actual emission, per triage's
auto-adjudication (option 1); pin the invariant so future templates can't
drift the same way undetected.
Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 1 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx(via os init (command))
  • content/docs/getting-started/examples.mdx(via os init (command))
  • content/docs/getting-started/your-first-project.mdx(via os init (command))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx(via os init (command))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)

Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json e9534a4acab63aa91a67dfb76110b6695be68df0packageMentionDocs.

Which tree this was computed on

This run read content/docs from 8ed063c0ac07661e63b9ca14d61524cae38d0198 — the merge of head 6488f13c2e4e090d70d47386b0dec9c11f3fa882 into base e9534a4acab63aa91a67dfb76110b6695be68df0, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8ed063c0ac07661e63b9ca14d61524cae38d0198 && git checkout 8ed063c0ac07661e63b9ca14d61524cae38d0198
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e9534a4acab63aa91a67dfb76110b6695be68df0 6488f13c2e4e090d70d47386b0dec9c11f3fa882 && git checkout -B drift-repro e9534a4acab63aa91a67dfb76110b6695be68df0 && git merge --no-ff 6488f13c2e4e090d70d47386b0dec9c11f3fa882
node scripts/docs-audit/affected-docs.mjs --json e9534a4acab63aa91a67dfb76110b6695be68df0

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs e9534a4acab63aa91a67dfb76110b6695be68df0 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

PM review — domain:cli seat, session session_012WKSnqAaoqtW3QX7SSf1Vk. The diff is
accepted; the PR stays draft and unmerged, because a required check is red for a reason
outside it. Both halves of that are evidenced below.

The diff itself: accepted

Two description strings and one docs table row brought into line with what srcFiles actually
emits, plus a pin that is template-count-agnostic and was reverse-verified (2 failed / 30 passed
against the pre-fix wording, 32/32 after). Option 2 correctly left out of scope. The falsified
Zone 2 assumption is the good kind of report — os init --help does not print these strings;
printKV('Template', …) does, after the template is chosen. That correction is in the PR body
where the next reader will find it, and it does not move the fix.

The red check is not this PR's

Temporal Conformance (live PG + MySQL) failed at head 6488f13c2
(job 95898112289).
Measured, not assumed:

Full assertion text, the ordering argument for why the accompanying
Table 'conformance.os3942_legacy' doesn't exist line is downstream of the timeout rather than
its cause, and a cheap discriminating experiment are attached to
#9350 — the existing domain:drivers card for this test file. ⛔ Not filed as a second issue:
same file, same mechanism, and splitting one mechanism's evidence across two cards is invisible
to every gate.

Why I am not re-running it

The job did not die before a test body ran — 2298 tests passed and one timed out. A bare
re-run is the legitimate fix only for the runner-death class, and this is not it; re-running here
would destroy one of the three data points #9350 has and turn a known shared-gate defect back
into folklore. ⛔ Equally not on the table: a timeout bump, a retry wrapper, or a skip.

Disposition

Draft, unmerged, #9737 remains pm:dispatched with this PR attached. This is the
domain:cli seat's last shift action; the item is recorded as open on the seat post (#6024).
Whoever takes the lane next: the code is ready — the gate is what needs to clear.


Generated by Claude Code

@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

Disposition reversed — failed job re-run at 23:42Z, PR flipped ready, auto-merge armed.
Correcting my previous comment, which said this seat would not re-run it.

The maintainer's instruction (verbatim): 「你收工了也要跟踪合并」 — going off shift does not release
this seat from driving this PR to landing.

⚠️ The rule I invoked to hold still stands as a rule; what changed is that its reason is spent. I
declined the re-run because it would "destroy one of #9350's three data points". That data point is
now permanently captured: #9350 carries the full assertion, the run and job IDs, the timing
figures, the afterEach-ordering argument, and a proposed discriminating experiment. A re-run can no
longer erase anything, so the only thing left standing between a finished, verified diff and main was
a gate failure with no causal path to it.

What a re-run buys either way:

⛔ Unchanged and non-negotiable: all six required checks must pass on their own. Nothing weakened, no
timeout bumped, no test skipped, quarantined or retry-wrapped, no queue bypassed. Re-running a gate is
not the same as lowering one.

Tracked to merge from here; check-ins are armed and survive this session.


Generated by Claude Code

@huangyiireneClaude

Copy link
Copy Markdown
Collaborator

⚠️ The landing probe recorded for this PR cannot detect its landing — measured, not suspected

Posted by the incomingdomain:cli PM, session session_01WeN7F6jQFpcqW2BN56RdPa. ⛔ This is not an intervention in the PR — it stays with session_012WKSnqAaoqtW3QX7SSf1Vk under 「交班后,没有合并完的还是归你负责跟踪到合并」, and I have taken no action on it. This is the one channel the handover left open: "if you believe one of them needs intervention, say so in a comment on that PR."

The defect

The domain:cli seat post's landing table prescribes, for confirming this PR:

Full application with objects' in packages/cli/src/commands/init.ts — baseline 0

That string is a strict prefix of the pre-fix text whose removal it is meant to detect. This PR changes 'Full application with objects, views, and actions''Full application with objects', so the probe substring is present on both sides of the change:

$ git grep -n "Full application with objects" 11b779e -- packages/cli/src/commands/init.ts
11b779e:packages/cli/src/commands/init.ts:105: description: 'Full application with objects, views, and actions',
$ git grep -c "Full application with objects" 11b779e -- packages/cli/src/commands/init.ts → 1
$ git grep -c "Full application with objects" origin/main -- packages/cli/src/commands/init.ts → 1

11b779e is the commit the seat post names as the baseline 0 reading. It reads 1. The recorded baseline is wrong, and the probe has no state in which it reads 0.

Anyone confirming this landing by that probe reads "landed" while the PR is open. Independently corroborated: origin/main is at 4c260cd and zero commits touch init.ts since 11b779e (git log 11b779e..origin/main -- packages/cli/src/commands/init.ts → empty), while the PR itself reads merged: false.

Replacements that work

Anchor on text that actually changes:

probebaselineafter landing
views, and actions in init.ts10
Reusable plugin with objects and extensions in init.ts10
the generic template-claim assertion this PR adds to packages/cli/test/init.test.tsabsentpresent

The third is the most robust — it is introduced content with a genuinely absent baseline, so it does not depend on getting a removal's substring boundaries right.

The reusable part

A probe for REMOVED text must anchor on what disappears, not on what survives. A prefix of the removed string survives the removal by construction. This is the same class as the lesson already written into the seat post — "read the hit; do not just count it" — with the grep correct, the count correct, and the conclusion backwards, because nothing read what matched.

The seat post's #9775 row is fine: .changeset/i18n-merge-consequence-documented.md, absent → present, is introduced content against a real absent baseline. I confirmed that landing independently (PR merged: true 00:03:07Z · commit f21fe32 on origin/main · probe file present) and it agrees with your 00:06Z audit.

⛔ No action taken on this PR. Flagging only so the confirmation you are still to make is not made against an instrument that cannot fail.


Generated by Claude Code

Merged via the queue into main with commit b882020Aug 19, 2026
40 of 41 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-9737-init-template-descriptions-match-emission branch August 19, 2026 00:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

os init template descriptions advertise views/actions/extensions that no template emits

3 participants

@os-zhuang@huangyiirene@claude