The #15530 ruling landed in PR #17096: the standalone default emission of
`os create plugin` names the package `plugin-<name>` — unscoped — and writes
`"private": true`. `--in-repo` is unchanged and still emits a publishable
`@objectstack/plugin-<name>` under `packages/plugins/`.
Three hand-written statements still promised the pre-ruling behaviour and sent
a reader to a `npm publish` that npm refuses:
- `content/docs/deployment/cli.mdx` scaffolder-routing table, "Publishable?"
- `content/docs/deployment/cli.mdx` `#### os create` intro prose ("publishable")
- `content/docs/plugins/index.mdx` "Which artifact this page means" callout
All three are re-derived from the generator (`packages/cli/src/commands/create.ts`
`pluginDirName` / `pluginPackageName` / the standalone `private: true`), not from
the ruling text.
The "Publishable?" column keeps discriminating between the two scaffolders: the
`os init -t plugin` cell is "No, and no flag lifts it"; the `os create plugin`
cell is "Only with --in-repo".
Claude-Session: https://claude.ai/code/session_01DapQyvYrFb1MxSYe7BL2nt
Co-authored-by: Claude <noreply@anthropic.com>
os create plugin's standalone output is an unscoped,privatepackage namedplugin-NAME. Three hand-written statements incontent/docs/still promised the pre-ruling behaviour — a publishable@objectstack/plugin-NAME— so a reader who followed them is refused bynpm publishat the last step of a documented workflow, with the doc as the only thing that told them to try.Closes #17103
The emitted name, re-derived from the generator
Read out of
packages/cli/src/commands/create.tsonorigin/main, never copied from the ruling text or from the card:⇒ standalone, i.e. the default:
plugin-NAMEplus"private": true.--in-repo:@objectstack/plugin-NAME, publishable, landing underpackages/plugins/.Cross-checked against this package's own literal pins in
packages/cli/test/create.test.ts:260-281.packages/cliis read-only for this change — the generator is the evidence, never the target.What changed — three statements, not two
The card names two. A third turned up at source and is the same claim in the same section, so it is corrected here rather than left to falsify the fix:
content/docs/deployment/cli.mdxscaffolder-routing table, Publishable? cell@objectstack/plugin-NAMEpackage"--in-repo— the default standalone emission is an unscopedplugin-NAMEmarkedprivate: true"content/docs/deployment/cli.mdx,#### os createintro prosePlugincontract, built bytsc, publishable"privateflag and whycontent/docs/plugins/index.mdx"Which artifact this page means" callouttsc, published as@objectstack/plugin-NAME"--in-repoThe Publishable? column — what it now says, and why it still discriminates
The card's third item is a consequence, not a false claim: once
os create pluginis alsoprivate: true, a column whose two cells both read "No" stops doing the one job that table exists for. The decision taken here is to keep the column and make each cell say what is actually true of that scaffolder, which restores the discrimination on a real axis rather than a stale one:os init NAME -t plugin→ "No — the emittedpackage.jsonisprivate: true, and no flag lifts it". Measured, not assumed:renderScaffoldPackageJsoninpackages/cli/src/commands/init.ts:409-425writes a bare, unscopednameand an unconditionalprivate: true, with no placement flag anywhere in that command.os create plugin NAME→ "Only with--in-repo", with both halves spelled out.So the two cells give different answers, and the difference is the one a reader needs: one scaffolder can never be published, the other can be, under a flag that is for platform work inside this monorepo.
The card's executable criterion, run as a two-legged proof
The criterion: after the fix, no hand-written page states that
os create plugin's standalone output is publishable or@objectstack-scoped, while the--in-repobehaviour is still described accurately. The same sweep was run over both trees, scoped tocontent/docs/and excluding the release-ownedcontent/docs/releases/.Leg A —
origin/main(must find them). Placeholder-scoped spelling: 2 lines,content/docs/deployment/cli.mdx:106andcontent/docs/plugins/index.mdx:81. Publishability claims on pages namingos create plugin: 4 lines, addingcli.mdx:1337(the third statement) and the column header.Leg B — this branch (must not). Placeholder-scoped spelling: 3 lines, and reading each one, every occurrence is attached to
--in-repo:cli.mdx:106("--in-repoemits a publishable …"),cli.mdx:1371-1372("Only--in-repobelow emits a scoped, publishable …"),plugins/index.mdx:82("… under--in-repo"). No line states the standalone output is publishable or scoped.The
--in-repohalf survives, and is described accurately.--in-repois named on 8 lines across 4 pages after the change (5 lines / 4 pages before — the fix adds descriptions of it, it removes none), and both files'--in-repoprose is unchanged except where it now carries the scoped name the standalone emission gave up.Controls. Positive:
@objectstack/plugin-authis on 30 lines in both legs, unchanged. Fabricated:@objectstack/plugin-zzznotarealpluginandos create widgeteach return 0 in both legs, so the instrument can answer zero.What was deliberately NOT touched
@objectstack/plugin-occurs 89 times incontent/docs/outsidereleases/on this branch; 86 of those are genuinely first-party, genuinely published plugins and are untouched. That is a reading, not an assumption: the 11 distinct names behind those 86 occurrences —plugin-auth,plugin-security,plugin-audit,plugin-hono-server,plugin-email,plugin-sharing,plugin-approvals,plugin-reports,plugin-pinyin-search,plugin-dev,plugin-webhooks— were each resolved to a realpackages/plugins/plugin-*/package.jsonwhosenamematches and which carries noprivateflag. A blanket find-and-replace on that prefix would have been a much larger defect than the one being fixed.Read and left alone, as the card asked:
content/docs/protocol/kernel/index.mdx:387-408describes the scaffold and already prints./plugin-NAME/unscoped, and its--in-reposentence is aboutworkspace:*dependencies, not publishability — accurate.content/docs/getting-started/your-first-project.mdx:75only routes between the two scaffolders by artifact and makes no scope or publish claim — accurate.content/docs/deployment/cli.mdx:1341already reados create plugin analytics # Create ./plugin-analyticsand served as a positive control that the correct spelling was already reachable in this page.Verification
0, reconciled withnode scripts/pm/dispatch-gates.mjs --ranat36d9b307: "41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED". Exit codes were captured by redirect-then-$?, never through a pipe.PREREQUISITE NOT MET(three at exit3, two at exit1with the same substance) because the tree was unbuilt. Nothing was read from those runs;@objectstack/spec,@objectstack/lint,@objectstack/formula,@objectstack/clientand@objectstack/client-reactwere built and all five re-run to a real exit0.pnpm lint— the whole repository,eslint . --no-inline-config, exit0. Not narrowed.origin/mainand once after; both lists are byte-identical at 41 commands. The post-merge derivation is the one reconciled.Changeset
skip-changeset, and it is measured rather than assumed. No package'sfiles[]shipscontent/docs/— checked across all 70 workspace packages that declare afiles[], withdistas the positive control (70/70 match it) and a fabricated term as the negative (0). The docs site itself,apps/docs, isprivate: true. So this diff publishes nothing from any released package, which is exactly what that label is for.Clause-②: no
Contract-text:packages/cli/src/commands/create.ts:313-315,:357-360and:381, quoted above. Correcting prose to match code that already shipped is a pull-back onto already-declared behaviour: it widens no accept set, adds no key to any published payload and exports no symbol.Scope
Two files, both hand-written docs.
content/docs/releases/is release-owned and untouched.packages/cliis untouched. Numbers cited for context only, with no verb beside them: the ruling is on issue #15530, its code half landed as PR #17096, and issue #16140 was considered and rejected as a neighbour by the card.Generated by Claude Code
Generated by Claude Code