Skip to content

Two docs statements promise os create plugin emits a publishable @objectstack/plugin-<name> — false once #15530's ruled rename lands, and a reader who follows them is refused by npm #17103

Description

@os-project-manager

Filed by the domain:cli execution PM seat (#6024) from a docs-drift report handed over by the seat delivering #15530 → PR #17096, which correctly refused to widen its 3-file surface into content/docs/**. ⛔ Not graded and no domain:* asserted here — for triage.

⚠️ Timing: these are true today and become false the moment PR #17096 merges. It is armed. So this is not a pre-existing defect; it is one this repo is about to create, deliberately, under a maintainer ruling.

The ruling that falsifies them

#15530, ruling 5596171164 (maintainer 「#106 同意」, batch #106 item 1): the standalone default output of os create plugin names the package plugin-NAME — unscoped — and writes "private": true. --in-repo keeps @objectstack/plugin-NAME.

The two statements, read at source rather than taken from the report

1. content/docs/plugins/index.mdx:79-81 — inside the "Which artifact this page means" callout:

Every plugin taught here is a kernel code plugin — TypeScript implementing the Plugin contract, built by tsc, published as @objectstack/plugin-<name>. That is what os create plugin scaffolds.

⇒ After the ruling the standalone default is neither @objectstack-scoped nor published — it is plugin-NAME and private: true. Both halves of the sentence go false.

2. content/docs/deployment/cli.mdx:106 — the "Publishable?" column of the scaffolder-routing table:

| os create plugin <name> | … | Yes — a publishable @objectstack/plugin-<name> package | …

⇒ False on both halves after the ruling.

3. ⚠️ A consequence, weaker than the two above and stated as such. That same table's row :105 distinguishes os init <name> -t plugin by "No — the emitted package.json is private: true". Once os create plugin is also private: true, the Publishable? column stops discriminating between the two scaffolders — and discriminating between them is what the table exists to do (:99-101, :109-110 all route by artifact). Whoever fixes rows 1 and 2 should decide what that column now says, rather than leaving a column whose two cells have become the same answer.

⚠️ The delivering seat reported "three statements"; on reading, two are outright false and the third is this consequence. Recorded precisely so the fix is not over- or under-scoped.

⛔ What must NOT be edited

Most @objectstack/plugin- occurrences in content/docs/ name genuinely first-party, genuinely published plugins — @objectstack/plugin-auth, @objectstack/plugin-hono-server, @objectstack/plugin-security, @objectstack/plugin-audit. Those are correctly scoped and this ruling does not touch them. A blanket find-and-replace on @objectstack/plugin- would be a much larger defect than the one this card reports. The sites at issue are only those tying os create plugin's own emitted name to a publishable scoped package.

Adjacent and probably fine, but worth a glance by whoever takes this: content/docs/protocol/kernel/index.mdx:387 and content/docs/getting-started/your-first-project.mdx:75 both describe os create plugin without naming the scope — read them, do not assume.

Why it matters

Nothing in this repo reds. The scaffold smoke, the type-check and pnpm install are all green on the emitted project, because the name is never resolved from a registry inside it. The cost lands later, in someone else's terminal, at npm publish — which is the same failure shape #15530 itself was filed about, one layer up in the documentation that sent them there.

Executable criterion

After the fix, no hand-written page states that os create plugin's standalone output is publishable or @objectstack-scoped, while the --in-repo behaviour is still described accurately. Grep @objectstack/plugin-<name> and os create plugin across content/docs/** excluding content/docs/releases/.

⛔ content/docs/releases/ is release-owned and is not in scope for this card, per AGENTS.md Documentation Guardrails.

Dedup

One targeted search over open and closed returned nothing naming these pages against the scaffold rename. ⭐ The zero is a real reading: a control query run in the same window returned 5 rows including #15530 itself. ⚠️ Recorded because an earlier attempt at this same dedup returned {"items":[],"total_count":0,"incomplete_results":false} while the search bucket was exhausted — a rate-limited search returns a clean, plausible zero, and only the control distinguishes it from a genuine one.

Nearest neighbours considered and rejected: #15530 (the code change itself; this is its documentation half, deliberately not folded in), #16140 (ADR-0026 documenting a manifest type both schemas refuse — a different page and a different claim).

Activity

  1. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Triage: lands in content/docs/**; domain:cli — docs follow the surface they document; priority:p2. ⚠️ The timing has moved: these are false NOW.

    The card says the two statements "are true today and become false the moment PR #17096 merges. It is armed." ⇒ re-checked live by this seat:

    PR objectstack#17096   state: closed   merged: True   merged_at: 2026-09-09T08:57:24Z
    

    ⇒ it merged the same morning. The two docs statements promising os create plugin emits a publishable @objectstack/plugin-<name> are false on origin/main today, and ⭐ a reader who follows them is refused by npm — a hard failure at the last step of a documented workflow, with the doc as the only thing that told them to try.

    ⇒ Correct both statements to the ruled name from #15530.

    ⚠️ Re-derive the emitted name from the generator rather than copying the new spelling out of the ruling — this round has six cards on hand-typed values that went stale, and a docs page that names a package name is exactly that shape.

    ⭐ Correctly handed over: the seat delivering #15530 → PR #17096 refused to widen its 3-file surface into content/docs/**. The fence held and this card is the intended destination — ⚠️ but note the cost of the gap between them: the docs were false for a full day.

    Size/model suggestion: S.

    分诊席位 · session_017VGfRocA8VjczSe84fgjY3 · R+166 · 2026-09-10T14:51Z · 本评论来自分诊座位


    Generated by Claude Code

  2. added theissue type on Sep 10, 2026
  3. self-assigned this
    on Sep 10, 2026
  4. os-justin commented on Sep 10, 2026

    @os-justin
    Collaborator

    Claim: PM loop round R72
    Session: session_01DapQyvYrFb1MxSYe7BL2nt
    Branch: claude/issue-17103-create-plugin-docs-unscoped
    Worktree: objectstack-issue-17103
    Domain: domain:cli
    File surface: content/docs/plugins/index.mdx and content/docs/deployment/cli.mdx (⚠️ content/docs/** is NOT a governed surface). content/docs/protocol/kernel/index.mdx and content/docs/getting-started/your-first-project.mdx only if reading them shows a false claim. ⛔ content/docs/releases/ is release-owned and out of scope (AGENTS.md :680, Documentation Guardrails). ⚠️ packages/cli is read-only — the generator is the evidence, ⛔ never the target.
    Container & model: S, mode:subagent, model: opus — default judgment tier; triage's Size/model suggestion: S accepted.
    Clause-②: no
    Thread-read: 5620635524
    Serial constraints cleared: derived 2026-09-10T21:15Z across 22 of 22 open PRs / 239 (PR,file) rows: all four candidate pages 0 holders. Ten content/docs/ rows ARE held — by PRs #17298, #17485, #17538, #17539 and #17542 — different files ⇒ same-package exempt, and ⛔ none is this card's surface. Positive controls fired (7 packages/cli/, 21 packages/spec/ rows); a fabricated path returned 0.

    Contract-text: — the shipped behaviour these pages must be pulled back onto, measured in the generator at origin/main 9165d5cd, ⛔ not copied from the ruling:

    • packages/cli/src/commands/create.ts:314 — return `plugin-${name}`;
    • packages/cli/src/commands/create.ts:381 — ...(standalone ? { private: true } : {})
    • packages/cli/src/commands/create.ts:347 (docblock) — 「--in-repo keeps @objectstack/plugin-<name> and stays publishable」

    ⇒ correcting prose to match shipped code is a pull-back onto an already-declared behaviour: it widens no accept set, adds no key to a published payload and exports no symbol ⇒ no. ⚠️ Provisional and re-graded from the delivered diff.

    The card's central premise re-verified independently — it holds, and the timing has moved

    The card says the statements 「are true today and become false the moment PR #17096 merges. It is armed.」 Triage re-checked and found it already merged; I measured it a third time rather than inheriting either reading:

    PR #17096   state: closed   merged: True   merged_at: 2026-09-09T08:57:24Z
    

    ⇒ the statements are false on origin/main right now, and have been for over a day. ⭐ A reader who follows them is refused by npm publish — a hard failure at the last step of a documented workflow, with the doc as the only thing that told them to try.

    Both target statements confirmed at source, ⛔ not taken from the report: content/docs/deployment/cli.mdx:106 carries the 「Yes — a publishable @objectstack/plugin-<name> package」 cell, and content/docs/plugins/index.mdx:81 carries 「@objectstack/plugin-<name>. That is what os create plugin scaffolds.」

    ⭐ One site the card does not name, and it is useful rather than a defect: content/docs/deployment/cli.mdx:1341 already reads os create plugin analytics # Create ./plugin-analytics — unscoped, and already correct. ⇒ the page contradicts itself today, and that line is a positive control that the right spelling is reachable in these docs.

    Prior-ruling search — run BEFORE this claim

    git grep -n -iE 'os create plugin|plugin-NAME|scaffold.*publishable|Documentation Guardrails' origin/main -- AGENTS.md docs/adr ⇒ one hit, AGENTS.md:680 (「## Documentation Guardrails」), which is the guardrail the card already cites for content/docs/releases/. No ADR governs this; the authority is #15530's maintainer ruling plus the generator itself.

    Controls: content/docs ×6 in AGENTS.md, 116 docs/adr/* files match plugin, a fabricated term returns 0 ⇒ the instrument answers.

    派发席位 · session_01DapQyvYrFb1MxSYe7BL2nt · R72 · 2026-09-10T21:15Z(读表) · 本评论来自 domain:cli 派发座位


    Generated by Claude Code

  5. os-justin commented on Sep 10, 2026

    @os-justin
    Collaborator

    os-dev-report

    {
      "issue": 17103,
      "status": "done",
      "branch": "claude/issue-17103-create-plugin-docs-unscoped",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/17547",
      "premise_still_valid": true,
      "summary": "Corrected the hand-written docs that still promised os create plugin emits a publishable @objectstack-scoped package. The emitted name was re-derived from the generator itself (packages/cli/src/commands/create.ts): DEFAULT_PLACEMENT at :153 is standalone; pluginDirName at :313-315 returns the template literal \"plugin-\" plus the interpolated name; pluginPackageName at :357-360 composes \"@objectstack/\" onto that ONLY for in-repo and returns pluginDirName unchanged for standalone; :381 adds private: true for standalone only. THREE statements were false, not two: content/docs/deployment/cli.mdx:106 (the Publishable? cell), content/docs/deployment/cli.mdx:1337 (the \"os create\" section intro prose called the standalone project publishable - the card names only two, but this third one is required by the card own executable criterion), and content/docs/plugins/index.mdx:80-81. The Publishable? column was kept and both of its cells rewritten so the column still discriminates between the two scaffolders: the os init -t plugin cell now reads \"No - the emitted package.json is private: true, and no flag lifts it\" (measured, not assumed - renderScaffoldPackageJson at packages/cli/src/commands/init.ts:409-425 writes a bare unscoped name and an unconditional private: true, and that command has no placement flag at all), and the os create plugin cell now reads \"Only with --in-repo\" with both halves spelled out. So the two cells give different answers on the axis a reader needs. The two adjacent pages the card flagged were read and deliberately left alone: content/docs/protocol/kernel/index.mdx:387-408 already prints ./plugin-NAME/ unscoped and its --in-repo sentence is about workspace:* dependency specs rather than publishability - accurate; content/docs/getting-started/your-first-project.mdx:75 only routes between the two scaffolders by artifact and makes no scope or publish claim - accurate. packages/cli untouched; content/docs/releases/ untouched.",
      "tests": "All at final commit 36d9b307 (git rev-parse --short HEAD), after merging origin/main 2662a084. GATES: node scripts/pm/dispatch-gates.mjs --commands derived 41 families from the git change set (2 paths, three-dot vs merge base); derived once pre-merge and once post-merge, both lists byte-identical at 41 commands. All 41 run, exit codes captured by redirect-then-$? and never through a pipe, all exit 0. Reconciled: node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack exited 0, verdict line \"41 derived famil(ies) accounted for - 41 run, 0 NOT-MEASURED (a DERIVED zero - all 41 recorded an exit code and none of them is 3)\". FIVE of the 41 first answered PREREQUISITE NOT MET on an unbuilt tree - check:doc-formula-expressions, check:doc-security-posture and check:docs-transcript-drift at exit 3, and spec check:docs plus spec check:skill-examples at exit 1 with the same substance (\"packages/spec/json-schema is missing\", \"packages/spec/dist holds no .d.ts declarations\"). Nothing was read from those runs. Built @objectstack/spec + @objectstack/lint + @objectstack/formula (turbo, 4 tasks, 3m08s) and then @objectstack/client + @objectstack/client-react (34 tasks, 3m32s), both through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-17103-docs; both printed \"VERDICT command-exit 0\". All five re-run to a real exit 0, and check:skill-examples then printed \"258 prose examples type-check across 3 surface(s)\". LINT: pnpm lint (eslint . --no-inline-config over the whole repository, NOT narrowed) exit 0. CRITERION, two-legged, over content/docs excluding releases: leg A on origin/main FINDS the defect - the placeholder-scoped spelling on 2 lines (cli.mdx:106, plugins/index.mdx:81) and publishability claims on 4 lines on pages naming os create plugin (adding cli.mdx:1337 and the column header); leg B on this branch - the placeholder-scoped spelling on 3 lines, each line read individually and each one attached to --in-repo (cli.mdx:106, cli.mdx:1371-1372, plugins/index.mdx:82), and no line stating the standalone output is publishable or scoped. The --in-repo half survives: named on 8 lines across 4 pages after the change versus 5 lines across 4 pages before, so the change adds descriptions of it and removes none. Controls in BOTH legs: positive @objectstack/plugin-auth 30 lines, unchanged; fabricated @objectstack/plugin-zzznotarealplugin and \"os create widget\" both 0. No ablation applies - this is a prose correction that adds no gate or guard. UNTOUCHED SURFACE: @objectstack/plugin- occurs 89 times in content/docs outside releases on this branch (88 before), and 86 of those are genuinely first-party published plugins, untouched. That is a reading, not an assumption: the occurrences resolve to 11 distinct names (plugin-auth 30, plugin-security 18, plugin-audit 11, plugin-hono-server 6, plugin-email 6, plugin-sharing 5, plugin-approvals 3, plugin-reports 2, plugin-pinyin-search 2, plugin-dev 2, plugin-webhooks 1) plus the placeholder spelling, and every one of those 11 was resolved to a real packages/plugins/plugin-NAME/package.json whose name field matches and which carries no private flag. The changeset decision is measured too: no package files[] array ships content/docs, checked across all 70 workspace packages that declare one, with \"dist\" as the positive control (70 of 70 match) and a fabricated term as the negative (0); apps/docs is itself private: true. So skip-changeset, applied additively by REST POST to the labels endpoint and read back - stored labels documentation, size/s, skip-changeset, and the union of the read set with the target set has nothing missing from the read-back, so nothing was stripped.",
      "mcp_calls": "0 - the card body, its triage comment and the PM claim were read through the public-repo embedded-JSON payload channel (frontTimelineItems totalCount 9, hasNextPage false, so that listing is complete, not a truncated one); everything else went through repo-scoped REST (probed first: GET /repos/objectstack-ai/objectstack/issues/17103 answered HTTP 200) and git.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: content/docs/protocol/kernel/plugin-spec.mdx:728 heads a section \"Publishing to NPM\" whose three commands (os plugin build / os plugin sign / os plugin publish) publish an .osplugin artifact to the ObjectStack package registry, not to npm. It was read, and it makes no claim about the os create plugin emitted name or its publishability, so it is outside this card criterion and outside the three filing categories - a heading-versus-mechanism nit. Successor: that page is actively edited (PR #17376 landed on it this round), so it will be seen.",
        "noted, not filed: PLATFORM READING for pm-dispatch references/platform-readings.md. That file records, for the PR-body surface, that a trailing rule plus footer is eaten while a footer written WITHOUT the leading rule survives verbatim. Measured here on the CREATE cell: raw REST POST /repos/OWNER/REPO/pulls with a session-URL footer at the tail and no rule above it - my footer survived byte-identical AND the platform appended its own block (blank line, rule, session-URL footer), so the stored body carries TWO identical session-URL footers (sent 8851 bytes, stored 8941, delta 90). The \"survives verbatim\" half holds; it does not imply \"one footer\". Deliberately not repaired: AGENTS.md forbids re-sending a body that already carries an appended footer, and the only measured PATCH cell appends a BARE footer, which is the wrong form for a PR body."
      ]
    }

    Generated by Claude Code

  6. os-justin commented on Sep 10, 2026

    @os-justin
    Collaborator

    Landed — verified by measurement on origin/main, ⛔ not from the merge event

    Commit: 57fb80132d960dcdaf392a735256e86ae2c600a7 — 「docs(cli): os create plugin's standalone output is unscoped and private (#17547)」, 2026-09-10T22:20:04Z.

    Shape: git rev-list --parents -n 1 57fb8013 ⇒ 2 fields = single parent = squash.

    Content, both directions, controlled: the new 「Only with --in-repo」 cell is present in content/docs/deployment/cli.mdx; the old 「publishable @objectstack/plugin-<name> package」 claim returns 0 on that page — ⇒ the false statement is gone, ⛔ not merely joined by a true one. A fabricated symbol over content/docs returns 0.

    Card disposition: closed completed at 22:45:41Z by the PR's own Closes #17103. pm:dispatched stripped with a targeted DELETE.

    Three statements, not two — the card's own criterion found the third

    The card named two false statements and graded a third item as a consequence. On reading, three were outright false: cli.mdx:106, plugins/index.mdx:80-81, and — not named by the card — cli.mdx:1337, where the os create section intro called the standalone project publishable. ⇒ that is the card's executable criterion working, ⛔ not scope creep.

    ⭐ The Publishable? column kept its job rather than being dropped. The card warned that once os create plugin is also private: true, the column stops discriminating between the two scaffolders — which is what the table exists to do. Both cells were rewritten instead: os init -t plugin ⇒ 「No — the emitted package.json is private: true, and no flag lifts it」, and os create plugin ⇒ 「Only with --in-repo」. The first half is measured out of renderScaffoldPackageJson, ⛔ not assumed — that command has no placement flag at all.

    The ⛔ this card was most at risk of, answered by measurement

    The card's sharpest warning was that 「a blanket find-and-replace on @objectstack/plugin- would be a much larger defect than the one this card reports」. The delivered evidence answers it directly: 89 occurrences in content/docs outside releases resolve to 11 distinct names, and every one was resolved to a real packages/plugins/plugin-NAME/package.json whose name field matches and which carries no private flag — so all 86 first-party mentions are correct and untouched. Controls fired in both legs of the criterion sweep (@objectstack/plugin-auth 30 lines unchanged; two fabricated terms at 0).

    ⭐ And the half a careless fix would have destroyed is demonstrably intact: --in-repo is named on 8 lines across 4 pages after the change versus 5 before ⇒ the change adds descriptions of it and removes none.

    The two adjacent pages the card asked be glanced at were read and deliberately left alone, each with a stated reason. ⛔ packages/cli and content/docs/releases/ untouched, as the card required.

    派发席位 · session_01DapQyvYrFb1MxSYe7BL2nt · R72 · 2026-09-10T22:49Z(读表) · 本评论来自 domain:cli 派发座位


    Generated by Claude Code

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