Harden the shipped consumer preset against a real monorepo consumer - #18
Conversation
The playground is the model consumer, but it was linted by the repo-local config — so it received basalt/token-layer-boundary, which is deliberately absent from the shipped preset because it guards this repo's internal layering and no consumer should ever get it. Extend the shipped preset through the workspace symlink instead, via the same node_modules-relative path a real consumer documents. oxlint 1.68.0 honours the nested config from the root `bun run lint` invocation (the nearest config replaces the root one for the subtree), so this needs no separate lint wiring. The playground now sees exactly what argo sees. No deltas: the preset alone is what a consumer gets, and dist/ and routeTree.gen.ts are already gitignored, which oxlint honours by default.
The playground now carries its own nested .oxlintrc.json extending the shipped preset, and oxlint resolves the nearest config as a replacement for the subtree — so the `#app` repo glob's apps/playground/src/** entry no longer matches anything. Narrow it to the package's own src and record why the omission is deliberate, since the glob is generated from SURFACES and would otherwise look like an oversight worth "fixing" back. The shipped preset's projection is unchanged: the repo/shipped glob split means this never reaches consumers. Lock the config-level chain that would otherwise rot silently: the repo glob excludes the playground, the playground extends the preset at the documented path, and that preset carries the visx boundaries but not token-layer-boundary — the mirror of surfaces-coverage.test.ts's assertion against the repo-local config.
📦 basalt-ui package modifiedTrigger the Make Release workflow after merging to publish to npm. |
|
Warning Review limit reached
Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
WalkthroughThe changes scope Basalt lint rules, add a playground Oxlint preset extension, and make init-generated CI and design paths derive from configured roots. Documentation and tests reflect the new behavior, while the root plugin development script is removed. ChangesConfigurable tooling
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant BasaltInit
participant Config
participant TemplateRenderer
participant CIWorkflow
BasaltInit->>Config: read basalt.roots
Config->>TemplateRenderer: provide ROOTS_GLOBS and SERIES_MODULE_PATH
TemplateRenderer->>CIWorkflow: render root-specific checks
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/basalt-ui/src/cli/index.ts`:
- Around line 145-165: Use the default roots whenever cfg.roots is undefined or
empty, not only when it is undefined. Update resolveSeriesModulePath and
buildTemplateVars so both select the same non-empty roots array before deriving
the series path or ROOTS_GLOBS; apply this in
packages/basalt-ui/src/cli/index.ts lines 145-165 and 471-472.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e1c33b82-01a9-4c85-86a0-23c43ec04a00
📒 Files selected for processing (10)
.oxlintrc.jsonapps/playground/.oxlintrc.jsonpackage.jsonpackages/basalt-ui/agent/skills/basalt-app/SKILL.mdpackages/basalt-ui/configs/check.ymlpackages/basalt-ui/configs/oxlint.jsonpackages/basalt-ui/src/cli/index.tspackages/basalt-ui/src/cli/init-sync.test.tspackages/basalt-ui/src/surfaces.tstests/oxlint-preset-sync.test.ts
💤 Files with no reviewable changes (1)
- package.json
Real-consumer onboarding surfaced three defects in the shipped consumer surface:
- The CI seed hardcoded `oxfmt 'src/**'`. A monorepo has no top-level src/, so the
glob matched nothing and oxfmt exited 2 — the consumer's very first CI run broke
for a reason that reads like a basalt bug. check.yml now renders {{ROOTS_GLOBS}}
from basalt.roots instead of being copied raw.
- The series path hardcoded `src/lib/series.ts`, so a consumer that set roots
CORRECTLY still got a DESIGN.md pointing at a path that cannot exist, and a
defaultExempt that exempted nothing. It now derives from the first root, keeping
argo's `<root>/lib/series.ts` convention; seriesModulePath still overrides.
- `eqeqeq: "error"` flagged the deliberate `x != null` nullish check. "smart" allows
it while still catching `== 1`.
roots was already the config axis for the guard walk; the seeds just weren't reading
it. Same knob, three fewer places that ignore it.
Both roots-derived seeds resolve through one `resolveRoots` helper: an explicit
`roots: []` falls back to the default rather than rendering an empty glob, which
would have reproduced the very "matches zero files" break this fixes. Roots are
POSIX-escaped into the generated CI shell string — consumer-authored config has no
business emitting a workflow that dies on a quoting error they can't trace back here.
6fcfcca to
445f031
Compare
Onboarding a real monorepo consumer exercised the shipped surface end-to-end and found three defects. All three share one root cause:
basalt.rootswas already the config axis for the guard walk, but the seeds weren't reading it.Fixes
CI seed matched nothing on a monorepo.
check.ymlhardcodedoxfmt 'src/**'. A monorepo has no top-levelsrc/, so the glob matched zero files and oxfmt exited 2 — the consumer's very first CI run broke, for a reason that reads like a basalt bug. The seed is now rendered throughfillTemplatewith a new{{ROOTS_GLOBS}}var instead of being copied raw.Series path was wrong for anyone who configured roots correctly. It hardcoded
src/lib/series.ts, so a consumer that correctly setroots: ['apps/web/src']still got aDESIGN.mdpointing at a path that cannot exist, and adefaultExemptthat exempted nothing. It now derives from the first root, keeping argo's<root>/lib/series.tsconvention.seriesModulePathstill overrides. argo sidestepped this by settingexemptexplicitly, which is why it never surfaced before.eqeqeqfought the nullish idiom. Bare"error"flagged the deliberatex != nullcheck (4 sites in the new consumer).["error", "smart"]allows it while still catching== 1— verified both ways against oxlint directly, so the rule doesn't get weaker.Also drops a dangling
dev:pluginscript pointing at./plugins/basalt, removed back in b4cb6cc.The two pre-existing commits on this branch make
apps/playgroundlint against the shipped preset — the dogfooding that makes this class of gap visible.Release
The
fix:commit touchespackages/basalt-ui/, so this releases as a patch. Consumers on 1.1.0 get the corrected seeds on their nextinit/sync.Verification
src/**fallback when roots is unset, theseriesModulePathoverride).fmt:check+lint+typecheckclean.check.ymlstaysmode: 'seed', sosyncnever overwrites a consumer's copy.Do not squash — the per-package changelog needs the granular commits.
Summary by CodeRabbit
New Features
Bug Fixes
eqeqeqvalidation.Documentation
Chores