Skip to content

docs(devx): the Which scaffolder? callout routes by ARTIFACT — plugin names a metadata plugin and a kernel code plugin (#15817) - #15834

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-15817-plugin-word-two-artifacts-docs
Sep 5, 2026
Merged

docs(devx): the Which scaffolder? callout routes by ARTIFACT — plugin names a metadata plugin and a kernel code plugin (#15817)#15834
baozhoutao merged 1 commit into
mainfrom
claude/issue-15817-plugin-word-two-artifacts-docs

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #15817

What was wrong

content/docs/deployment/cli.mdx carried a "Which scaffolder?" callout whose only job is to choose for the reader — and it chose the wrong artifact. The word plugin names two different things across the scaffolders, and the callout routed by the command word instead of by the artifact.

Before (content/docs/deployment/cli.mdx:83-86):

> **Which scaffolder?** For a new app, prefer **`npm create objectstack@latest`** — it
> also derives your namespace, pins the framework packages to the current release, and
> installs the AI skills bundle + `AGENTS.md`. Reach for `os init` when you want a
> **plugin** skeleton or a **bare config** in an existing directory.

A reader wanting "a plugin skeleton" was sent to os init -t plugin, which emits a metadata plugin — while every page under /docs/plugins teaches the kernel code plugin that os create plugin emits.

Premise confirmed against the source (read-only, no packages/cli change)

os init NAME -t plugin os create plugin NAME
build script build: 'objectstack compile'packages/cli/src/commands/init.ts:479 build: 'tsc'packages/cli/src/commands/create.ts:198
published? private: truerenderScaffoldPackageJson, packages/cli/src/commands/init.ts:236 publishable @objectstack/plugin-NAME, no private key — packages/cli/src/commands/create.ts:183-184
shape declarative objects: src/objects/__name___item.object.ts rendering const … : Data.Object = { … }, manifest type: 'plugin'init.ts:492, objects at init.ts:512-533 src/index.ts exporting const …Plugin: Plugin = { name, version, async init(context) {…}, async destroy() {…} }create.ts:236-256

The card's table is exact. premise_still_valid: true.

After

The callout now routes by artifact, with a table naming both artifacts, their command, what each emits, its build tool, whether it publishes, and the page to read next — plus a Callout recording why the two scaffolders are deliberately separate (the #15531 ruling: collapsing both under one command word would make the collision structural instead of documented), so the reason lives in the docs and not only in the issue.

Link targets quoted (all resolved by check:doc-anchors, and all pre-existing pages — no plugin documentation page was rewritten):

  • [Object Metadata](/docs/data-modeling/objects)content/docs/data-modeling/objects.mdx
  • [Plugin Anatomy](/docs/plugins/anatomy)content/docs/plugins/anatomy.mdx
  • [Plugin Development](/docs/plugins/development)content/docs/plugins/development.mdx
  • [Plugins & Packages](/docs/plugins)content/docs/plugins/index.mdx
  • same-page fragments #os-init (cli.mdx:79) and #os-create (cli.mdx:1288)

Three same-page precision fixes ride along, all the same defect and all inside the card's page:

  • cli.mdx:29 — "scaffolder for plugin skeletons" → "for metadata-plugin skeletons"
  • the os init Templates row — "Reusable plugin package with objects" → names it the metadata plugin and says it is not what os create plugin emits
  • cli.mdx:1290 (#### os create) — the reverse pointer, so a reader landing there is told which plugin this is

Re-check greps from the card

Before (on origin/main) and after (on this branch) — identical, both exit 0:

$ grep -rn "Which scaffolder" content/docs/deployment/cli.mdx
83:> **Which scaffolder?** For a new app, prefer **`npm create objectstack@latest`** — it        # before AND after
1292:[Which scaffolder?](#os-init)), or an example application — into the current directory:      # after only (new reverse pointer)

$ grep -rn "plugin" packages/cli/src/commands/init.ts packages/cli/src/commands/create.ts | grep -i template
packages/cli/src/commands/init.ts:709:    template: Flags.string({ char: 't', description: 'Template: app, plugin, empty', default: 'app' }),
packages/cli/src/commands/create.ts:19: *     the `plugin` template it did not resolve even INSIDE this monorepo —
packages/cli/src/commands/create.ts:26: * A fourth followed from making the emission real: the `plugin` template wrote
packages/cli/src/commands/create.ts:113: * down. Writing it down is how the `plugin` template came to declare
packages/cli/src/commands/create.ts:176:  plugin: defineTemplate({
packages/cli/src/commands/create.ts:430:    'Create a new standalone plugin or example project from a built-in template';

The second grep is byte-identical before and after: no packages/cli file is touched by this PR.

Measurement

Derived, not recalled: node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack → 39 commands for the 1-path change set. Exit codes captured before any pipe (cmd > log 2>&1; EXIT=$?). Run at fccd098e4.

Green (31 of 39) — including every gate that reads this page:

✅ check-doc-anchors: 310 internal #fragment link(s) across 410 source file(s) all resolve to a real heading
✓ doc authoring guard: 394 files clean — no bare metadata literals.
✓ check-docs-single-h1: 405 page(s) under content/docs/ carry no body-level `# ` heading
✓ check-cli-examples-parity: 1 declared pair(s) agree as SETS
check-nul-bytes: OK (scanned 7640 text file(s) -- 7640 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).

plus check-ci-filter-parity, check-closing-keyword-parity (+--self-test), check-comment-mask-corpus, check-doc-frontmatter, check-doc-route-spelling --advisory/--self-test, check-docs-section-name (+--self-test), check-section-landing-index (+--self-test), check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:docs-redirects, check:driver-memory-census, check:published-readme-links, check:react-page-adapter-contract, check:refd-timer-probe, check:role-word, check:skill-identifier-liveness, check:vendor-version-stamps, check:watch-hint-literal, @objectstack/spec check:empty-state / check:liveness / check:strictness-ledger / check:variant-docs / check:yaml-examples, and scripts/docs-audit/check-affected-docs.mjs.

⊘ NOT MEASURED (8) — never read as green. Each refused before measuring anything, on a prerequisite this container does not hold (an unbuilt workspace package or a gitignored build artifact), not on a finding:

gate refusal quoted
@objectstack/lint check:doc-formula-expressions PREREQUISITE NOT MET — the workspace package @objectstack/formula is not built (exit 3)
@objectstack/lint check:doc-security-posture same unbuilt-@objectstack/formula refusal (exit 3)
check:docs-transcript-drift PREREQUISITE NOT MET — … packages/lint/dist/index.js does not exist — @objectstack/lint is not built⛔ This is NOT a pass and NOT a finding: nothing was measured. (exit 3)
@objectstack/spec check:docs ❌ packages/spec/json-schema is missing. — gitignored build artifact (exit 1, a prerequisite refusal)
@objectstack/spec check:skill-examples ❌ packages/spec/dist holds no .d.ts declarations — the package is not built (exit 1, a prerequisite refusal)
check-doc-frontmatter.mjs --self-test Cannot find module 'fumadocs-core/content/md/frontmatter'apps/docs deps absent; the enforcing run (check-doc-frontmatter.mjs, no flag) is green
check-cross-package-test-inputs --union-into "$RUNNER_TEMP/…" derivation's own ⊘ NOT MEASURED — argv carries a CI-only variable
check-shard-attestation / check-test-completeness same: CI-only $RUNNER_TEMP / matrix.shard values

Declared narrowing: the repo-wide pnpm lint / full pnpm test farm is CI's run, not this one. Nothing above was skipped by choice on a gate this container could have measured.

Other readings

  • node scripts/pm/check-governed-merges.mjs --test content/docs/deployment/cli.mdx → exit 0, ✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.
  • git merge-tree --write-tree origin/main HEAD after a fresh fetch (origin/main = 98191d2cb) → exit 0, clean tree d4facadef662378931fe54cd594332bd01688649 — no conflict.
  • Open-PR collision scan over all 30 open PRs (GET /repos/objectstack-ai/objectstack/pulls/N/files): exactly one other open PR touches this file — feat(objectql,cli): backfillSummaryNulls accepts recomputeUndefinedOnEmpty — a just-declared min/max/avg roll-up can be filled on request (#15064) #15708, whose only cli.mdx hunk is at lines 912-946 (os migrate summary-nulls), ~800 lines from every line this PR edits. Disjoint hunks, no conflict.
  • grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' content/docs/deployment/cli.mdx → no match.

Scope

Docs only, one file. ⛔ No command-word rename: os init -t plugin-t metadata-plugin is an interface change on a published CLI and needs its own domain:cli card, per the triage boundary. ⛔ No plugin documentation page rewritten — link targets were verified, none was wrong. ⛔ content/docs/releases/ untouched. skip-changeset: nothing is published from any package by this diff.

🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

…ames two things

`content/docs/deployment/cli.mdx` carried a "Which scaffolder?" callout whose
only job is to choose for the reader, and it chose wrong: it sent anyone wanting
"a plugin skeleton" to `os init -t plugin`, which emits a METADATA plugin
(`objectstack compile`, `private: true`, declarative objects), while every page
under /docs/plugins teaches the KERNEL CODE plugin that `os create plugin` emits
(`tsc`, publishable, implements the `Plugin` contract with `init` / `destroy`).

The callout now routes by ARTIFACT rather than by command word, with a table
naming both artifacts, their commands, their build tool, whether they publish,
and the page to read next for each. A short paragraph on the same page records
WHY the two scaffolders are not merged (#15531: collapsing both under one word
would make the collision structural instead of documented), so the reason lives
in the docs and not only in the issue.

Docs only — no `packages/cli` change; a command-word rename is an interface
change that needs its own domain:cli card.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Flipped ready + auto-merge enabled (10:37Z, method: MERGE). All seven required contexts on fccd098e4 read non-failing: Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Governed Surface Queue Guard success; Build Core, Temporal Conformance (live PG + MySQL) skipped; no other red. git merge-tree --write-tree origin/main <branch> → EXIT=0 against d701e657d. Watched; on landing #15817 closes.


Generated by Claude Code

@baozhoutao
baozhoutao added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 5a21d73 Sep 5, 2026
37 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-15817-plugin-word-two-artifacts-docs branch September 5, 2026 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants