#2546 · bb plugin new scaffolds a plugin that cannot import @get-bb/plugin-sdk/testing: cron-parser is undeclared
Verdict: REPRODUCED · Root-cause confidence: high
1. TL;DR
A new plugin cannot load the backend test harness after a normal development install.
The harness imports cron-parser, but the scaffold does not declare that package.
The SDK marks the package as an optional peer, so npm does not install it for the consumer.
The test file fails during module load, before Vitest can register its first test.
2. Claims vs findings
| Claim from the issue | Status | Evidence |
|---|---|---|
A fresh scaffold cannot import @get-bb/plugin-sdk/testing. |
Verified | The base run failed with the reported Cannot find package 'cron-parser' error. See base-vitest.log. |
The backend harness imports better-sqlite3, cron-parser, hono, and zod. |
Verified | The imports occur on lines 4 through 7 of fake-plugin-host.ts. |
| The SDK declares all four packages as optional peers. | Verified | The SDK manifest lists each peer and gives each peer optional: true. |
The scaffold declares three packages but omits cron-parser. |
Verified | The scaffold has better-sqlite3 and hono in development dependencies. It has zod in dependencies. |
| A stale lockfile causes the fault. | Refuted | A new scaffold and a new npm install produced the fault. The test passed after one cron-parser install. |
| The frontend harness does not have this missing-peer fault. | Verified | The frontend entry imports React and Testing Library. The author guide tells users to install React, React DOM, Testing Library, and jsdom. See frontend harness imports and the author guide. |
3. Environment
- bb base commit:
ad79bbb5ec909524f8f281e62d860c588a86f332. - Host: Linux 7.0.0-29-generic, x86-64.
- Node:
v24.18.0. npm:11.16.0. pnpm:9.15.0. - The source build passed with
pnpm exec turbo run build. - No app server was necessary. No provider process was necessary.
- The isolated launcher reserved app
12997, server20997, and daemon28997. No process used these ports. - The unused data directory was
~/.bb-dev/tmp-bb-report-2546-investigate-sfpuif-48f8158d5e79.
4. Minimal reproduction
The published SDK version was unavailable on 2026-08-27. The source check therefore used a local archive from the same base commit.
- Prepare and build the base checkout.
git checkout ad79bbb5ec909524f8f281e62d860c588a86f332 pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build
- Create the scaffold and make a local SDK archive.
REPO="$(pwd)" mkdir -p "$REPO/.repro-2546" NODE_ENV=development node "$REPO/packages/scripts/dist/commands/run-cli.js" plugin new probe npm pack "$REPO/packages/plugin-sdk" --pack-destination "$REPO/.repro-2546"
The CLI still creates the scaffold when its automatic npm install cannot find the unpublished SDK.
- Point the scaffold at that archive and install its development packages.
cd "$REPO/bb-plugin-probe" npm pkg set 'devDependencies.@get-bb/plugin-sdk=file:'"$REPO"'/.repro-2546/get-bb-plugin-sdk-0.4.24.tgz' npm install --include=dev npm install --save-dev vitest
- Add this test as
server.test.ts.import { it } from "vitest"; import { createFakePluginHost } from "@get-bb/plugin-sdk/testing"; import plugin from "./server"; it("loads", async () => { const { bb } = createFakePluginHost({ pluginId: "probe" }); await plugin(bb); });Saved test: server.test.ts.
- Add a local Vitest configuration. This prevents Vitest from loading the bb monorepo configuration above the scaffold.
cat > vitest.config.ts <<'TS' import { defineConfig } from "vitest/config"; export default defineConfig({}); TSSaved configuration: vitest.config.ts.
- Run the test.
npx vitest run --config vitest.config.ts
Expected:
Test Files 1 passed (1) Tests 1 passed (1)
Actual:
RUN v4.1.11 /var/tmp/bb-2546-revise2-xYrQCz/bb-plugin-probe ❯ server.test.ts (0 test) ⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯ FAIL server.test.ts [ server.test.ts ] Error: Cannot find package 'cron-parser' imported from /var/tmp/bb-2546-revise2-xYrQCz/bb-plugin-probe/node_modules/@get-bb/plugin-sdk/dist/testing/index.js Did you mean to import "cron-parser/dist/index.js"? ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ Test Files 1 failed (1) Tests no tests Start at 17:47:08 Duration 100ms (transform 11ms, setup 0ms, import 0ms, tests 0ms, environment 0ms) vitest_exit=1Full output: base-vitest.log.
- Install the missing peer and run the same test again.
npm install --save-dev cron-parser@^5.5.0 npx vitest run --config vitest.config.ts
Actual:
RUN v4.1.11 /var/tmp/bb-2546-revise2-xYrQCz/bb-plugin-probe Test Files 1 passed (1) Tests 1 passed (1) Start at 17:47:14 Duration 155ms (transform 21ms, setup 0ms, import 76ms, tests 3ms, environment 0ms) vitest_exit=0Full output: base-after-cron-parser.log.
5. Root cause
The backend harness uses four bare package imports at module scope.
import Database from "better-sqlite3";
import { CronExpressionParser } from "cron-parser";
import { Hono } from "hono";
import { z } from "zod";
Source: fake-plugin-host.ts lines 4–7.
The SDK leaves these packages for the consumer and marks each package as optional.
"peerDependencies": {
"better-sqlite3": ">=12",
"cron-parser": "^5.5.0",
"hono": "^4.11.9",
"zod": "^4.3.6"
},
"peerDependenciesMeta": {
"better-sqlite3": { "optional": true },
"cron-parser": { "optional": true },
"hono": { "optional": true },
"zod": { "optional": true }
}
Source: package.json lines 151–188.
The scaffold has no cron-parser entry.
dependencies: {
...PLUGIN_STARTER_DEPENDENCIES,
zod: "^4.3.6",
},
devDependencies: {
"@get-bb/plugin-sdk": PLUGIN_SDK_VERSION,
"better-sqlite3": "^12.0.0",
hono: "^4.11.9",
typescript: "^5.7.0"
}
Source: plugin-scaffold.ts lines 1892–1925.
npm omits an optional peer unless the consumer declares it.
Node then reaches the unresolved import during module evaluation.
The test body never starts, which explains the zero-test result.
The deeper fault is contract drift between the harness imports and the scaffold manifest.
6. Proposed fix (first principles)
Add cron-parser to the scaffold development dependencies at the SDK peer range.
Add a contract test that checks every optional backend-harness peer against the generated manifest.
The test must read peerDependenciesMeta. It must not treat a required peer as optional.
An end-to-end test should pack the local SDK, install a new scaffold, and import the backend harness.
This change does not alter the server and daemon wire contract. It does not need a protocol version change.
7. PR review
PR #2550 · Unblock first-plugin authoring
The PR adds the correct cron-parser declaration for this issue.
The same test passed after that package was present, so the change addresses the root cause.
The PR also changes the thread-list guide and npm error output for issues #2547 and #2548.
Medium · npm stdout can hide the real stderr reason
npmFailureDetail joins stderr before stdout, then keeps only the last eight lines.
Twelve stdout lines therefore removed the complete stderr error from the warning.
The hostile test failed the PR assertion and showed only progress lines 5 through 12.
Evidence: pr-2550-mixed-output-test.log.
Location: apps/cli/src/commands/plugin.ts:496-507.
Fix: prefer stderr, or keep a separate tail from each stream. Add a mixed-stream test.
Low · the optional-peer test reads all peers
sdkOptionalPeers returns every key from peerDependencies.
It does not inspect peerDependenciesMeta[name].optional.
A future required peer would create a false failure because npm installs required peers.
Location: apps/cli/src/__tests__/plugin-scaffold-dependencies.test.ts:102-108.
Low · the scaffold repeats the harness comment
The same five-line comment appears before cron-parser and before hono.
Location: packages/templates/src/plugin-scaffold.ts:1915-1925.
Tests: The two CLI files passed 39 tests. The two server files passed 16 tests.
Turbo type checks passed for @bb/cli, @bb/server, and @bb/templates.
Verdict: REQUEST CHANGES.
The #2546 fix is correct. The broader PR needs the mixed-stream error fix before merge.
8. Related issues
9. Appendix
Reproduction evidence
- Base scaffold output
- Local SDK archive output
- Base npm install output
- Base failure output
- Passing output after the direct fix
PR evidence
- Complete PR diff
- Focused CLI tests
- Focused server tests
- Focused type checks
- Hostile mixed-output test
Commands run
gh issue view 2546 --comments --json ... gh pr view 2550 --comments --json ... gh pr diff 2550 pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build npm pack packages/plugin-sdk npm install --include=dev npm install --save-dev vitest cp /tmp/bb-reports/issues/2546/repro/vitest.config.ts vitest.config.ts npx vitest run --config vitest.config.ts npm install --save-dev cron-parser@^5.5.0 npx vitest run --config vitest.config.ts gh pr checkout 2550 --detach pnpm exec turbo run test --filter=@bb/cli --force -- ... pnpm exec turbo run test --filter=@bb/server --force -- ... pnpm exec turbo run typecheck --filter=@bb/cli --filter=@bb/server --filter=@bb/templates --force git fetch origin main git log ad79bbb5ec90..origin/main -- packages/templates/src/plugin-scaffold.ts
No later origin/main commit changed the relevant source path.
Verification
The verifier ran the original literal steps and reached a monorepo Vitest configuration error.
With the saved local configuration, the verifier saw the reported cron-parser failure and the passing direct-install result.
This revision adds that configuration step and links its saved file.
It also corrects the frontend claim and expands the scaffold evidence to include zod.