Skip to content

fix(cli): os create plugin ships a starter test, so a fresh scaffold's npm test passes - #25236

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-4oxk47
Oct 22, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-4oxk47

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

Fixes #25175
Clause-②: no
Metadata-protocol: none

This is the os create plugin half of #25175, the half left after #25196 landed the os init -t plugin half (9c431bad3). With it, both plugin scaffolds pass their own npm test out of the box.

What changes

A project made by os create plugin NAME declared "test": "vitest" and contained no test file, so its first npm test exited 1 with No test files found, exiting with code 1. The plugin template in packages/cli/src/commands/create.ts (both placements) now:

  • writes src/index.test.ts, a starter test that runs the checks the kernel runs on a plugin when it loads one, before any of its code: a name, an init function (the loader's validatePluginStructure), and the protocol's PluginSchema (assertPluginContract), through @objectstack/spec/kernel, which the manifest already declares. Its header links the @objectstack/verify harness ("A runtime plugin" on protocol/kernel/plugin-spec) for tests that need a booted kernel;
  • writes tsconfig.build.json: extends: './tsconfig.json', exclude: ['dist', 'node_modules', 'src/**/*.test.ts'];
  • emits build: 'tsc -p tsconfig.build.json', dev: 'tsc -p tsconfig.build.json --watch' and test: 'vitest run' (one pass that exits, as os init emits). typecheck stays tsc --noEmit, on tsconfig.json.

The three public pages whose printed file tree create-plugin-docs-parity.test.ts holds equal to the emitted file set gain one line per new file: content/docs/plugins/index.mdx, content/docs/protocol/kernel/index.mdx and content/docs/protocol/kernel/plugin-spec.mdx (its CLI transcript, in emission order).

Changeset: .changeset/25175-create-plugin-scaffold-test.md (@objectstack/cli patch), with the one-line fix for a project scaffolded by an earlier release.

The route, and why it is honest

The card allows a starter test or a script that does not fail on an empty suite. This takes the starter test. The previous dev's four measured placements (report on #25175) were re-measured on this branch:

placement npm test after npm run build typecheck covers the test
S1 src/index.test.ts, build stays tsc 1 file dist/index.test.js + .d.ts emitted, the package ships its tests, and vitest collects the compiled copy too yes
S2 (this PR) src/index.test.ts + tsconfig.build.json 1 file dist/ = index.js, index.d.ts; still 1 file yes: a planted type error fails typecheck
S3 test/ outside rootDir: 'src' 1 file clean no (TS6059 if included)
S4 vitest run --passWithNoTests exit 0, nothing ran

S2 is the only row that keeps dist/ clean without hiding the test from the type checker. The pin below holds each of those properties, and the ablation shows each one can go red.

The test is not a placeholder: emitted with init renamed to start, it fails expected 'undefined' to be 'function'; emitted with version: 'v0.1.0', PluginSchema refuses it (ablation legs E and F).

Measured end to end (npm 10.9.4, registry @objectstack/spec 18.0.0, vitest 4.1.12, typescript 5.9.3)

os create plugin e2e-pkg from this branch's built CLI, outside the repository, then npx -y npm@10 install:

step exit reading
npm test 0 Test Files 1 passed (1), Tests 1 passed (1)
npm run build 0 dist/ = index.d.ts, index.js
npm test after the build 0 Test Files 1 passed (1)
npm run typecheck 0
npm test with src/index.test.ts removed 1 No test files found, exiting with code 1
baseline: main's os create plugin, npm test 1 No test files found, exiting with code 1

The pin, and its ablation

packages/cli/test/scaffold-test-script-passes.test.ts (unit tier) gains an os create block beside the os init one, sharing its sandbox (OS temp dir, no ancestor runner config, one symlink per declared dependency). Roster derived from create's templates (default placement) whose manifest declares test:

  • the emitted test script exits 0 and the files that ran are exactly the shipped test files;
  • after the emitted build script, dist/ holds index.js and no *.test.*, and test still runs exactly the shipped files;
  • the emitted typecheck passes, and fails once a type error is planted in the starter test;
  • control: the same project without its test files fails test.

Ablation through scripts/ablation-replace.mjs on create.ts (imported by relative source specifier, no dist/ on the path), each leg restored with blob == HEAD and git diff HEAD empty:

leg mutation result
A build: 'tsc' (S1) red: the build compiled the starter test into dist/: expected [ 'index.test.d.ts', 'index.test.js' ] to deeply equal []
B tsconfig.build.json without the test exclude red: same case, same reading
C typecheck: 'tsc -p tsconfig.build.json --noEmit' (test outside the checked program) red: a type error in the starter test passed typecheck
D starter test emitted as src/index.spec-off.ts red: 3 cases (no test file emitted; vitest run finds none)
E emitted plugin's init renamed to start red: expected 'undefined' to be 'function'
F emitted plugin's version v0.1.0 red: PluginSchema issue list not empty

Verification (head 2aed9ddea, after merging origin/main 03de83bf; the two incoming commits touch none of these files)

  • New pin + create-plugin-docs-parity + create: Test Files 3 passed (3), Tests 32 passed (32).
  • @objectstack/cli unit tier, full (vitest run --project unit --maxWorkers=2): Test Files 324 passed (324), Tests 4784 passed (4784). The integration tier is declared to CI: the diff touches no spawn entry and no integration-tier file.
  • pnpm --filter @objectstack/cli typecheck: exit 0, check:test-typecheck OK (no new signature). ESLint on the two changed TS files: clean.
  • Gates: all 97 commands from dispatch-gates.mjs --commands, plus check:template-version-sync and check:scaffold-emission-policy, exit 0. Four first exited 3 on prerequisites outside this diff (check-plugin-teardown-shape --self-test needs a pinned commit a shallow clone lacks; check:skill-examples, check:dual-build-cjs-loads and check:i18n-coverage read dist/ of packages outside the CLI closure) and exit 0 after git fetch of that commit and a full pnpm build.

Acceptance notes

  • The --in-repo placement gets the same three changes; its sandbox run is not pinned, because its tsconfig.json extends this monorepo's root config and means nothing outside it. Its emission is covered by create.test.ts and the docs-parity pin reads the default placement only, as before.
  • "Next steps" printed by os create still say pnpm install / pnpm build; it does not mention pnpm test. Unchanged here, to keep the plugin-spec transcript the only docs edit beyond the listings.
  • Process: this card ran as a project card thread on the harness-pinned branch claude/project-thread-4oxk47, not claude/issue-25175-*; the claim on cli: os init -t plugin and os create plugin emit a test script but no test file, so the fresh scaffold's npm test exits 1 with "No test files found" #25175 records that.

Generated by Claude Code

…s npm test passes

Clause-②: no
Metadata-protocol: none

Claude-Session: https://claude.ai/code/session_01QkpbEcZUG5GHxLNpzuuR5y
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet
objectstack-fleet Bot requested a review from os-sales October 22, 2026 13:54
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Oct 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/skills-reference.mdx (via node_modules (literal, a string literal in plugin))
  • content/docs/ai/skills.mdx (via node_modules (literal, a string literal in plugin))
  • content/docs/deployment/cli.mdx (via node_modules (literal, a string literal in plugin), os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/deployment/self-hosting.mdx (via node_modules (literal, a string literal in plugin))
  • content/docs/deployment/troubleshooting.mdx (via os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/getting-started/your-first-project.mdx (via os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/plugins/index.mdx (via tsconfig.build.json (literal, a string literal in plugin), os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/protocol/kernel/index.mdx (via tsconfig.build.json (literal, a string literal in plugin), os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/protocol/kernel/plugin-spec.mdx (via tsconfig.build.json (literal, a string literal in plugin), os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/upgrading.mdx (via node_modules (literal, a string literal in plugin))

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

  • content/docs/releases/v17/17-4.mdx (via os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/releases/v18/18-0.mdx (via node_modules (literal, a string literal in plugin))

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

Coarse fallback — 30 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 03de83bfe145eea8f495bcbc9886105715721b3f → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 9d763a7352bbb2c9da45d932f4d64a618b019745 — the merge of head 2aed9ddea3b47346abc41f4fb08711e44fca7150 into base 03de83bfe145eea8f495bcbc9886105715721b3f, 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 9d763a7352bbb2c9da45d932f4d64a618b019745 && git checkout 9d763a7352bbb2c9da45d932f4d64a618b019745
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 03de83bfe145eea8f495bcbc9886105715721b3f 2aed9ddea3b47346abc41f4fb08711e44fca7150 && git checkout -B drift-repro 03de83bfe145eea8f495bcbc9886105715721b3f && git merge --no-ff 2aed9ddea3b47346abc41f4fb08711e44fca7150

node scripts/docs-audit/affected-docs.mjs --json 03de83bfe145eea8f495bcbc9886105715721b3f

⚠️ 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 03de83bfe145eea8f495bcbc9886105715721b3f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:cli seat 1 (seat post #6024) · os-sales · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-22T16:20Z · head 2aed9ddea3

item reading
Shape 6 files, +217 / −7 (diff read by the seat). The plugin template in create.ts writes src/index.test.ts and tsconfig.build.json; build/dev compile through the latter, test becomes vitest run, typecheck stays on tsconfig.json. This is route S2 from the earlier dev's measured options, re-measured on this branch (PR body table). Inside claim 6341601909.
Premise #25175's remainder per Release 6340030798: the os create plugin half, needing the three docs pages the parity pin holds.
Gates (check-runs on this head) 43 runs: 37 success, 6 skipped, 0 failed. mergeable_state: clean. Report 6342523133: mcp_calls reads only, writes all through the relay.
Changeset and docs, read per sentence @objectstack/cli patch. The before/after sentences match the template diff; the "existing project" remedy names both the test file and tsconfig.build.json, which is what the twice-collected failure needs. The three docs pages each gain exactly the two new file lines in emission order; the link target heading "A runtime plugin" exists on protocol/kernel/plugin-spec.
Accepted deviations Branch claude/project-thread-4oxk47 (the card thread's assigned name).
Cross-lane touches content/docs/**, three file-tree listings, domain:devx: declaration posted on #6023 in this act.
Landing Not governed (check-governed-merges.mjs --pr 25236). Clause-②: no, no !: no contract review or dogfood battery owed. Ready + auto-merge through the relay in this act.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 22, 2026 16:22
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 6bc8cc6 Oct 22, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-4oxk47 branch October 22, 2026 16:47
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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cli: os init -t plugin and os create plugin emit a test script but no test file, so the fresh scaffold's npm test exits 1 with "No test files found"

2 participants