Repository navigation
chore(docs): flatten MDX tab JSX out of the generated LLM artifacts - #408
Conversation
docusaurus-plugin-llms reads source markdown and exposes no transform hook, so <Tabs>/<TabItem> JSX lands verbatim in llms.txt, llms-full.txt and every per-page .md twin. Today that already leaks 112 tab tags from the skills pages; #402 would spread it across ~18 more. Add scripts/flatten-llms-tabs.mjs, chained onto the docs build. It can't be a Docusaurus plugin: core runs every plugin's postBuild under Promise.all, so a sibling would race the llms plugin. Two transforms, because there are two kinds of tabs: - <PackageManagerTabs command="..." /> collapses to a single fenced pnpm block, matching the default tab, so what an agent copies out of llms.txt is what a reader sees. - Generic <Tabs> linearizes to #### headings, keeping every TabItem body. Keeping only the first would have deleted the skills pages' complementary ProfileCard.tsx / _profilecard.scss / Usage tabs. Both run only outside code, fenced and inline. That is load-bearing, not defensive: bestax-bulma has its own Tabs component, and `<Tabs\b` matches `<Tabs.List` because \b sits between the s and the dot. Without masking, the flattener corrupts docs/api/components/tabs.md. Verified against the current build/: 0 theme tabs left outside code, all 19 <Tabs.List> usages preserved, api/components/tabs.md byte identical, and idempotent on a second run.
|
Warning Review limit reached
Next review available in: 1 minute 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: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
WalkthroughAdds a post-build Node script that flattens tab components in generated LLM documentation artifacts, preserves code examples, removes related imports, and includes tests, build wiring, and pipeline documentation. ChangesLLM tab flattening
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant BuildScript
participant Docusaurus
participant FlattenScript
participant LLMArtifacts
BuildScript->>Docusaurus: run docusaurus build
Docusaurus-->>LLMArtifacts: generate documentation artifacts
BuildScript->>FlattenScript: run post-build flattening
FlattenScript->>LLMArtifacts: scan and rewrite matching files
FlattenScript-->>BuildScript: report rewritten file count
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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 |
Preview DeploymentPreview URL: https://d24a409c.bestax.pages.dev |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@docs/scripts/flatten-llms-tabs.mjs`:
- Around line 106-107: Update the masking logic around the fenced-block and
inline-span replacements to use a CommonMark-compatible scanner that protects
all valid Markdown code delimiters, including tilde fences, longer backtick
fences, and multiline inline code, before flattening <Tabs> markup. Add
regression coverage for each affected form while preserving existing masking
behavior.
- Line 142: Update the prefilter in transform() so artifacts containing
tab-related imports are processed even when before lacks literal tab JSX;
include the relevant import patterns in the predicate or remove the guard and
rely on the existing after === before check, while preserving current skipping
behavior for unrelated artifacts.
- Around line 115-120: Move the import-removal and repeated-newline cleanup
replacements into the callback passed to outsideCode, applying them to each
chunk after flattenGenericTabs(flattenPackageManagerTabs(chunk)) and before
outsideCode restores held code. Keep outsideCode as the outer wrapper so fenced
examples retain their imports and intentional spacing.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 17348f9b-c640-4dbd-bdb7-83aa8762cc30
📒 Files selected for processing (4)
docs/CLAUDE.mddocs/package.jsondocs/scripts/flatten-llms-tabs.mjsdocs/scripts/flatten-llms-tabs.test.mjs
…e mask Three findings from the CodeRabbit review of #408: 1. Masking only recognised exact triple-backtick fences. Tilde fences and longer fences went unprotected. Replaced the regex with a line-based CommonMark scanner: either fence character, length >= 3, closing fence the same character and at least as long, up to 3 leading spaces, and a backtick info string may not contain backticks. An unterminated fence now runs to end of document and is masked rather than rewritten. Reproduced for tilde fences, which the old code mutated. The longer fence case in docs/api/helpers/theme.md and tutorial-basics did not reproduce as an observable defect, but the scanner removes the class. Multiline inline spans are still matched single-line on purpose: an unpaired backtick in prose would otherwise swallow the document. 2. Import removal and blank-line collapsing ran after held code was restored, so they reached into fenced examples — stripping `import Tabs …` demo lines and collapsing deliberate blank lines in code blocks. Both reproduced; both now run inside the mask. 3. The main() prefilter skipped artifacts carrying only tab imports, which transform() would still have cleaned. Shared as NEEDS_FLATTENING so the predicate and the transform agree. 21 tests, up from 13. Output over the current build/ is byte-identical to the pre-fix run — 0 theme tabs outside code, 19 <Tabs.List> preserved, api/components/tabs.md unchanged, idempotent — so this closes latent edge cases without moving real-world output.
|
All three addressed in 867b0e2. Two reproduced as real bugs, one didn't — details below, since the distinction matters for what changed. 1. Mask all valid Markdown code delimiters — fixedReplaced the fence regex with a line-based CommonMark scanner: either fence character, length ≥ 3, closing fence the same character and at least as long, up to 3 leading spaces of indentation, and a backtick fence's info string may not contain backticks. An unterminated fence now runs to end of document and is masked rather than rewritten — when we can't confidently parse, we don't mutate. Tilde fences reproduced. Against the pre-fix code: Longer fences did not reproduce. Multiline inline spans are still single-line on purpose. CommonMark permits them, but matching across newlines means one unpaired backtick in prose swallows the rest of the document. The failure mode of guessing wrong is far worse than the miss, so this is documented in the code rather than implemented. 2. Keep cleanup inside
|
Preview DeploymentPreview URL: https://4474abae.bestax.pages.dev |
There was a problem hiding this comment.
Deep review — 0 blocking · 2 advisory
| # | Severity | Area | Finding | Location |
|---|---|---|---|---|
| 1 | 🔵 Advisory | Robustness | Post-mask cleanup (import strip + \n{3,} collapse) runs on restored text including code fences, breaking the "never touch code" invariant the masking establishes. Harmless today, latent for future pages. |
docs/scripts/flatten-llms-tabs.mjs:118 |
| 2 | 🔵 Advisory | Robustness | Code masking only recognizes 3-backtick fences (^```…^```$); ~~~ tilde fences and ≥4-backtick fences are not masked, so a <Tabs.List>/theme-tab lookalike inside one would be rewritten. None exist today (Prettier normalizes to 3-backtick). |
docs/scripts/flatten-llms-tabs.mjs:106 |
Overall: The change is sound and unusually well-guarded. The load-bearing decisions — <Tabs(?=[\s>]) instead of <Tabs\b, masking code (fenced and inline) before transforming, and keeping every TabItem body — are correct and each has a matching regression test (13/13 pass; pnpm test is wired via the new docs test task, which turbo run test picks up). I verified against the real corpus: every library <Tabs …> usage in tabs.md, hero.md, components.md, and the migration guides lives inside a ```tsx live fence (so it's masked), the skills pages' <Tabs> blocks (theming.mdx, custom-component.mdx) linearize to #### headings keeping all bodies, and their in-fence imports are import React, {…} / named imports that the strip regex cannot match. The riskiest part is the two post-mask passes in transform() — see below — but they have no effect on current content. The human should decide whether to move those two passes inside outsideCode before PackageManagerTabs and the #402 page migration expand the corpus.
Residual risk — ways tab JSX could still leak into (or corrupt) the artifacts:
- Theme tabs surviving un-flattened: refuted — the file gate
/<Tabs|<TabItem|<PackageManagerTabs/plus<Tabs(?=[\s>])…</Tabs>cover both current skills blocks; the PR's e2e check reports 0 theme tabs left outside code and I confirmed the sources. - Library
<Tabs.List>corrupted: refuted for current content — all 19 usages sit inside 3-backtick fences and are masked;tabs.mdis byte-identical. Only breaks under advisory #2's exotic fence syntax, which Prettier prevents. - Content silently deleted: refuted —
flattenGenericTabsmaps everyTabItem(not just the first) anditems.length===0falls back toinner.trim(); the placeholder-collision test pins that digit runs in prose can't be mistaken for a masked-code sentinel. - Documented code mutated: the one real (but currently inert) gap — advisory #1. Requires a future fenced example containing a default
import Tabs/TabItem/PackageManagerTabs from …line or ≥3 consecutive blank lines; none exist today (Prettier caps blank runs at 1; the skills fences use named /Reactimports).
🏄 Clean set, dude — this flattener reads the break perfectly: masks the code, keeps every tab body, and dodges the
<Tabs.Listshorebreak that would've wiped out the library docs. The only ripples are two cleanup passes surfing just outside the mask, and they're glassy today. Paddle it out. 🌊
There was a problem hiding this comment.
Pull request overview
Adds a post-build “LLM artifact” cleanup step to the docs pipeline so that MDX tab JSX (<Tabs>, <TabItem>, and the upcoming <PackageManagerTabs />) doesn’t leak into llms.txt, llms-full.txt, or the per-page .md twins produced by docusaurus-plugin-llms. This supports the planned page-by-page migration in #402 without degrading the machine-readable docs surface.
Changes:
- Introduces
docs/scripts/flatten-llms-tabs.mjsto rewrite only the generated LLM artifacts afterdocusaurus build, flattening tab JSX outside code (fenced + inline). - Adds
node --testcoverage for the transforms and artifact selection logic. - Wires the flattener into the docs build and documents the pipeline behavior in
docs/CLAUDE.md.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| docs/scripts/flatten-llms-tabs.mjs | Implements post-build transforms to flatten tab JSX in LLM artifacts while masking fenced and inline code. |
| docs/scripts/flatten-llms-tabs.test.mjs | Adds node:test coverage for the transforms (tabs flattening, code masking, idempotency, artifact detection). |
| docs/package.json | Chains the flattener after docusaurus build and adds a docs-package test script to run the new tests. |
| docs/CLAUDE.md | Documents why/when the flattener runs and what it guarantees about code-block safety and output shape. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
…ings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
…ings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
…ings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
…420) * docs: add the API page generator, its CI gates and review aids Lands the tooling for generating the derivable parts of docs/docs/api, with no page generated yet: MANAGED_CATEGORIES and ORDERED_CATEGORIES both start empty and grow one category per follow-up, so the 87 page diffs stay reviewable instead of arriving as one 199-file change. Four regions per page, delimited by `<!-- bestax:generated <id> -->` markers — `overview` (the component's TSDoc summary), `import` (the public barrel), `props` (the `<X>Props` interfaces via the TypeScript compiler API) and `cssvars` (a new section parsed from the SCSS). Everything outside a marker pair is hand-written and preserved byte-for-byte; deleting a pair opts that region out, and `docs-section-order` makes that visible rather than silent. Three choices keep the output reading as hand-written rather than as typedoc: own members only, with the one catch-all `...` row the pages already wrote by hand; types from AST source text, never `checker.typeToString`, which expands `(typeof validColors)[number]` into 19 literals; and wide colour unions rendered as a link to the existing Valid values page. CI gates live in check-conformance.mjs, which already runs in CI, rather than a new workflow step: `docs-generated` recomputes each managed page in memory and diffs, `docs-section-order` holds the order and marker presence. Both were verified by tampering. Also closes two gate gaps this tooling exposed. `pnpm all` ran `turbo run format:check`, which only runs the per-package scripts — none cover `scripts/`, `docs/scripts/` or any `.md`; CI runs the root script, which does, so CI could be red while `pnpm all` was green. And nothing linted `scripts/` at all. `all` now calls the root format check and `lint` also runs eslint over `scripts` and `docs/scripts`; verified by planting an unused variable. Two review aids ship alongside, both one-shot and both used to validate the migration that follows: `check-docs-parity.mjs` fails on any prop, default, description word, code span, URL, live example or prose line that a generated page drops relative to its hand-written self, and `check-docs-wording.mjs` buckets every previously-documented prop by how its description changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7 * chore: restore the component-catalog preamble this stack reverted `scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble predating #365, so regenerating deleted 21 lines of shipped guidance: the per-component value-union warning, where to find the installed `.d.ts`, the `Skeleton` helper-prop exception, which sub-component families accept helper props, and the custom-component composition spine. The checked-in `component-catalog.md` still had #365's text, so `gen:catalog:check` regenerated it shorter and failed `git diff --exit-code` — this is why Build and Test is red on this PR. Restores the generator to main's version. The catalog now regenerates byte-identical to the committed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct four prop-table defects in the API page extractor Found by comparing every generated page against its origin/main original in a browser, and against the interfaces the tables claim to describe. - Bare alias names where the hand-written tables had real types. A one-member "union" (`type CellSpanValue = number`) was never a union node, and a union naming another alias (`BulmaFixedGridColsProp = BulmaFixedGridCols | 'auto'`) failed the all-members-simple test. Both fell through to the bare name with no `**Types:**` footnote, so cell.md's `colSpan`/`rowSpan` said `CellSpanValue` where main said `number`, and grid.md's `fixedCols` was opaque beside five siblings expanded to `0 | … | 12` in the same table. Member aliases now resolve to a fixpoint. - `children` synthesized for components that never render it. The row was emitted for any interface with a DOM base, but inheriting `children` is not rendering it: Divider spreads onto `<hr>`, so the row documented the one thing React throws on ("hr is a void element tag and must neither have `children`…"), and Icon always supplies its own JSX children, so anything passed is silently dropped. Emitted only where the implementation names `children`; the catch-all row still covers pass-through cases. - Sub-components dropped when their props are an inline DOM type rather than a named `*Props` interface. `Navbar.Divider` and `Pagination.Ellipsis` were omitted from the generated Subcomponents lists entirely — which is why both pages still carried a hand-written duplicate list. They are now listed (with no table, since they add no props of their own). - `never` and `false | true` merged from the branches of a discriminated union. slider.md rendered `minDistance` as `never | number` and `range` as `false | true`; the forbidding branch's `never` is noise standing where the reader needs a type. Verified: generator idempotent, all ten conformance checks green, check-docs-parity still reports 0 prop/default/description losses across all 87 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): carry the catch-all fix through the rest of the stack `scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the `catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without them and made #423 — which is stacked above #422 — revert them. Same file, same content, applied across the stack so the tip is consistent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): expand DateFormatOption instead of leaving a bare alias The three datetime pages had regressed from main's `string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is declared in `form/_pickerInternals/` and is not exported from the barrel — so a reader could neither import it nor look it up, and no `**Types:**` footnote covered it either. A union member naming a type the alias index cannot resolve (`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier) now falls back to its own source text. Blast radius is exactly the three `format` cells; no other page changes. Found by an exhaustive cell-by-cell diff of all 87 generated pages against their origin/main originals, which also settles the rest: one genuine prop row lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type or default difference is either an improvement over drifted prose or a bare alias that does carry a footnote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): rebase the manifests onto main and address the review findings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): restore bestax-migrate's manifest and the lockfile from main Rebasing the root and docs manifests left `bestax-migrate/package.json` behind, and regenerating the lockfile against it reintroduced a stale entry: the branch still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417 moved it to `devDependencies`, and is missing the `prepack`/`postpack` pack-manifest hooks those PRs added. The branch never modified that file — it only trails main — so taking main's copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): fail the alias fixpoint instead of degrading to bare names The deep review on #421 flagged that the alias-resolution fixpoint degrades silently: if a chain needs more than the round bound, the survivors fall back to a bare name plus a `**Types:**` footnote rather than the expansion, and nothing says so. Running out of rounds is a different condition from settling. An alias that genuinely cannot expand stops making progress and the loop exits clean — that path is unchanged. But if the bound cuts the loop off while it is still resolving, the survivors render as bare identifiers, which is the exact regression this generator exists to prevent. That case now throws, naming the unresolved aliases. The bound is a named constant (`ALIAS_FIXPOINT_ROUNDS`) so the error can point at it. Chains in this repo settle in two rounds; the bound only exists to stop a mutually-recursive pair spinning. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): make the marker stripper fail instead of no-opping silently The deep review asked whether this step fails loudly when it matches nothing. It did not — it logged "removed 0 marker(s) from 0 file(s)" and exited 0. That is the one failure this step exists to prevent. Its own header records why it is a build step and not a plugin: the first attempt was a `postBuild` hook, `postBuild` runs under `Promise.all`, it raced ahead of docusaurus-plugin-llms and silently found nothing to strip. A quiet no-op here ships ~600 markers into llms-full.txt and every per-page `.md` twin with a green build. Stripping nothing is only correct when there was nothing to strip, so the check compares against the SOURCE pages: zero stripped AND zero markers in docs/docs/api is the legitimate "no managed categories yet" state (which is this branch, with MANAGED_CATEGORIES empty). Zero stripped while the source carries markers means the built markdown moved, the marker format changed, or the ordering regressed — that now exits 1 and says which. Also answers the other half of the question: the glob only ever reaches `.md` files plus llms.txt / llms-full.txt, so it cannot touch built HTML. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct three defects the #420 deep review found **The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested `/BulmaClassesProps/` against the whole node's source text, so it fired for `Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the name appears as the omit KEY LIST, not as the base being omitted. It returned before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`, so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All standard HTML attributes" half. That is the mechanism behind the menu.md (4 tables) and dropdown.md (2) regression against main. Matched structurally now: the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose first type argument does. **`impliedFalse` applied to controlled-mode booleans.** An optional `boolean` with no destructured default is documented as `false`, which is right for a flag and wrong for a controlled prop, where `undefined` is what selects UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its own description says "If provided, component is controlled" — two statements that cannot both hold. A prop whose description says it controls the component now keeps an empty Default. **`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint` directly; `turbo.json` declares no `//#lint` root task and only bulma-ui, create-bestax and bestax-migrate define one (each scoped to its own `src`), so `eslint scripts docs/scripts` — which exists only in the ROOT `lint` script — was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for `format:check`, left in place for `lint`; CI caught it only because `ci.yml` runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and verified by planting an unused variable in `scripts/`. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): close the theme.md fence my earlier edit left open CI caught this and it is my regression, not a pre-existing one: main's `theme.md` passes `prettier --check` at its real path. Removing the stray four-backtick fence pair around "Theme with Styling" took out a fence that was load-bearing. The outer ```` closed the inner ```tsx block (a longer fence closes a shorter one), so deleting the pair left the StyledTheme example unterminated — it swallowed the "### Nested Themes" heading and the block after it, and `prettier --check` failed on the file. Closing the ```tsx block explicitly gives the structure the section was always meant to have: the heading renders as a heading and each example is its own closed block. `pnpm run format:check` is green across the repo. Worth recording how this got through: I ran typecheck, lint, tests and the docs build locally but not the ROOT `format:check` — which is exactly the "pnpm all green, CI red" gap this stack exists to close, and the gap only closed for `lint` two commits ago. The root format check is in my gate from here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * docs: restore the flatten-llms-tabs description this stack replaced Fourth instance of the same silent-deletion pattern, found by the deep review on #420. `docs/CLAUDE.md`'s "LLM docs pipeline" section documented `scripts/flatten-llms-tabs.mjs`; this stack swapped in the `strip-generated-markers.mjs` paragraph in its place rather than alongside it. The script is not dead. It is still the FIRST step in docs' build chain (`docusaurus build && node scripts/flatten-llms-tabs.mjs && node scripts/strip-generated-markers.mjs`), still covered by `flatten-llms-tabs.test.mjs`, and does something unrelated to what displaced it — flattening `<PackageManagerTabs>`/`<Tabs>` MDX so the JSX does not land verbatim in `llms.txt`/`llms-full.txt`. Nothing else in the repo documented it, so a reader had no way to learn it exists or why it cannot be a Docusaurus plugin. Both steps are now described in the order the chain runs them. Same mechanism as the `gen-component-catalog.mjs` preamble, the manifest downgrades and the deleted `style-mapping-sync` check: a replacement where an addition was meant, with no conflict to force a second look. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ --------- Co-authored-by: Claude <noreply@anthropic.com>
…#421) * docs: add the API page generator, its CI gates and review aids Lands the tooling for generating the derivable parts of docs/docs/api, with no page generated yet: MANAGED_CATEGORIES and ORDERED_CATEGORIES both start empty and grow one category per follow-up, so the 87 page diffs stay reviewable instead of arriving as one 199-file change. Four regions per page, delimited by `<!-- bestax:generated <id> -->` markers — `overview` (the component's TSDoc summary), `import` (the public barrel), `props` (the `<X>Props` interfaces via the TypeScript compiler API) and `cssvars` (a new section parsed from the SCSS). Everything outside a marker pair is hand-written and preserved byte-for-byte; deleting a pair opts that region out, and `docs-section-order` makes that visible rather than silent. Three choices keep the output reading as hand-written rather than as typedoc: own members only, with the one catch-all `...` row the pages already wrote by hand; types from AST source text, never `checker.typeToString`, which expands `(typeof validColors)[number]` into 19 literals; and wide colour unions rendered as a link to the existing Valid values page. CI gates live in check-conformance.mjs, which already runs in CI, rather than a new workflow step: `docs-generated` recomputes each managed page in memory and diffs, `docs-section-order` holds the order and marker presence. Both were verified by tampering. Also closes two gate gaps this tooling exposed. `pnpm all` ran `turbo run format:check`, which only runs the per-package scripts — none cover `scripts/`, `docs/scripts/` or any `.md`; CI runs the root script, which does, so CI could be red while `pnpm all` was green. And nothing linted `scripts/` at all. `all` now calls the root format check and `lint` also runs eslint over `scripts` and `docs/scripts`; verified by planting an unused variable. Two review aids ship alongside, both one-shot and both used to validate the migration that follows: `check-docs-parity.mjs` fails on any prop, default, description word, code span, URL, live example or prose line that a generated page drops relative to its hand-written self, and `check-docs-wording.mjs` buckets every previously-documented prop by how its description changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7 * docs(bulma-ui): generate the layout, columns and grid API pages First categories through the generator, plus `helpers/` reordered but not generated — four of its six pages document hooks with `## API` and no `## Props` at all, and `config.md`/`theme.md` keep their hand-written tables (theme.md's Props section is a ~350-line prose sub-API, not a table). `@property` blocks move onto the interface members as inline TSDoc, so a description is verifiable by position and reaches users' editors and the shipped .d.ts. Descriptions were seeded FROM the docs pages, not the other way round: the hand-written tables are consistently the richer text, and the page stays authoritative wherever the two disagree. Component TSDoc summaries were likewise seeded from the pages — the existing ones ("Bulma Hero component root.", "Container component for Bulma.") were worse than the prose they would have replaced. That text ships in the .d.ts, so read the `component-catalog.md` diff: it is the canary for this step. Verified: check-docs-parity reports 0 losses across all 87 pages; the generator is idempotent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7 * chore: restore the component-catalog preamble this stack reverted `scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble predating #365, so regenerating deleted 21 lines of shipped guidance: the per-component value-union warning, where to find the installed `.d.ts`, the `Skeleton` helper-prop exception, which sub-component families accept helper props, and the custom-component composition spine. The checked-in `component-catalog.md` still had #365's text, so `gen:catalog:check` regenerated it shorter and failed `git diff --exit-code` — this is why Build and Test is red on this PR. Restores the generator to main's version. The catalog now regenerates byte-identical to the committed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * chore: restore the component-catalog preamble this stack reverted `scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble predating #365, so regenerating deleted 21 lines of shipped guidance: the per-component value-union warning, where to find the installed `.d.ts`, the `Skeleton` helper-prop exception, which sub-component families accept helper props, and the custom-component composition spine. This branch regenerated the catalog with that stale generator, committing the loss into a shipped skill artifact. Restores the generator to main's version and regenerates; the preamble matches main again and the component one-liners are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct four prop-table defects in the API page extractor Found by comparing every generated page against its origin/main original in a browser, and against the interfaces the tables claim to describe. - Bare alias names where the hand-written tables had real types. A one-member "union" (`type CellSpanValue = number`) was never a union node, and a union naming another alias (`BulmaFixedGridColsProp = BulmaFixedGridCols | 'auto'`) failed the all-members-simple test. Both fell through to the bare name with no `**Types:**` footnote, so cell.md's `colSpan`/`rowSpan` said `CellSpanValue` where main said `number`, and grid.md's `fixedCols` was opaque beside five siblings expanded to `0 | … | 12` in the same table. Member aliases now resolve to a fixpoint. - `children` synthesized for components that never render it. The row was emitted for any interface with a DOM base, but inheriting `children` is not rendering it: Divider spreads onto `<hr>`, so the row documented the one thing React throws on ("hr is a void element tag and must neither have `children`…"), and Icon always supplies its own JSX children, so anything passed is silently dropped. Emitted only where the implementation names `children`; the catch-all row still covers pass-through cases. - Sub-components dropped when their props are an inline DOM type rather than a named `*Props` interface. `Navbar.Divider` and `Pagination.Ellipsis` were omitted from the generated Subcomponents lists entirely — which is why both pages still carried a hand-written duplicate list. They are now listed (with no table, since they add no props of their own). - `never` and `false | true` merged from the branches of a discriminated union. slider.md rendered `minDistance` as `never | number` and `range` as `false | true`; the forbidding branch's `never` is noise standing where the reader needs a type. Verified: generator idempotent, all ten conformance checks green, check-docs-parity still reports 0 prop/default/description losses across all 87 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): regenerate the grid pages with the corrected type cells Picks up the extractor fix from the base branch. `cell.md`'s `colSpan` and `rowSpan` go back to `number` — they had regressed to a bare `CellSpanValue`, which aliases exactly `number` and so told the reader strictly less. `grid.md`'s `fixedCols` expands to `0 | … | 12 | 'auto'` instead of a bare `BulmaFixedGridColsProp`; it was the only opaque cell in a table whose five sibling props were already fully expanded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): carry the catch-all fix through the rest of the stack `scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the `catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without them and made #423 — which is stacked above #422 — revert them. Same file, same content, applied across the stack so the tip is consistent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): carry the catch-all fix through the rest of the stack `scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the `catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without them and made #423 — which is stacked above #422 — revert them. Same file, same content, applied across the stack so the tip is consistent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): expand DateFormatOption instead of leaving a bare alias The three datetime pages had regressed from main's `string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is declared in `form/_pickerInternals/` and is not exported from the barrel — so a reader could neither import it nor look it up, and no `**Types:**` footnote covered it either. A union member naming a type the alias index cannot resolve (`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier) now falls back to its own source text. Blast radius is exactly the three `format` cells; no other page changes. Found by an exhaustive cell-by-cell diff of all 87 generated pages against their origin/main originals, which also settles the rest: one genuine prop row lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type or default difference is either an improvement over drifted prose or a bare alias that does carry a footnote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): expand DateFormatOption instead of leaving a bare alias The three datetime pages had regressed from main's `string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is declared in `form/_pickerInternals/` and is not exported from the barrel — so a reader could neither import it nor look it up, and no `**Types:**` footnote covered it either. A union member naming a type the alias index cannot resolve (`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier) now falls back to its own source text. Blast radius is exactly the three `format` cells; no other page changes. Found by an exhaustive cell-by-cell diff of all 87 generated pages against their origin/main originals, which also settles the rest: one genuine prop row lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type or default difference is either an improvement over drifted prose or a bare alias that does carry a footnote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): rebase the manifests onto main and address the review findings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): rebase the manifests onto main and address the review findings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): restore bestax-migrate's manifest and the lockfile from main Rebasing the root and docs manifests left `bestax-migrate/package.json` behind, and regenerating the lockfile against it reintroduced a stale entry: the branch still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417 moved it to `devDependencies`, and is missing the `prepack`/`postpack` pack-manifest hooks those PRs added. The branch never modified that file — it only trails main — so taking main's copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): restore bestax-migrate's manifest and the lockfile from main Rebasing the root and docs manifests left `bestax-migrate/package.json` behind, and regenerating the lockfile against it reintroduced a stale entry: the branch still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417 moved it to `devDependencies`, and is missing the `prepack`/`postpack` pack-manifest hooks those PRs added. The branch never modified that file — it only trails main — so taking main's copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): fail the alias fixpoint instead of degrading to bare names The deep review on #421 flagged that the alias-resolution fixpoint degrades silently: if a chain needs more than the round bound, the survivors fall back to a bare name plus a `**Types:**` footnote rather than the expansion, and nothing says so. Running out of rounds is a different condition from settling. An alias that genuinely cannot expand stops making progress and the loop exits clean — that path is unchanged. But if the bound cuts the loop off while it is still resolving, the survivors render as bare identifiers, which is the exact regression this generator exists to prevent. That case now throws, naming the unresolved aliases. The bound is a named constant (`ALIAS_FIXPOINT_ROUNDS`) so the error can point at it. Chains in this repo settle in two rounds; the bound only exists to stop a mutually-recursive pair spinning. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): make the marker stripper fail instead of no-opping silently The deep review asked whether this step fails loudly when it matches nothing. It did not — it logged "removed 0 marker(s) from 0 file(s)" and exited 0. That is the one failure this step exists to prevent. Its own header records why it is a build step and not a plugin: the first attempt was a `postBuild` hook, `postBuild` runs under `Promise.all`, it raced ahead of docusaurus-plugin-llms and silently found nothing to strip. A quiet no-op here ships ~600 markers into llms-full.txt and every per-page `.md` twin with a green build. Stripping nothing is only correct when there was nothing to strip, so the check compares against the SOURCE pages: zero stripped AND zero markers in docs/docs/api is the legitimate "no managed categories yet" state (which is this branch, with MANAGED_CATEGORIES empty). Zero stripped while the source carries markers means the built markdown moved, the marker format changed, or the ordering regressed — that now exits 1 and says which. Also answers the other half of the question: the glob only ever reaches `.md` files plus llms.txt / llms-full.txt, so it cannot touch built HTML. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct three defects the #420 deep review found **The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested `/BulmaClassesProps/` against the whole node's source text, so it fired for `Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the name appears as the omit KEY LIST, not as the base being omitted. It returned before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`, so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All standard HTML attributes" half. That is the mechanism behind the menu.md (4 tables) and dropdown.md (2) regression against main. Matched structurally now: the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose first type argument does. **`impliedFalse` applied to controlled-mode booleans.** An optional `boolean` with no destructured default is documented as `false`, which is right for a flag and wrong for a controlled prop, where `undefined` is what selects UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its own description says "If provided, component is controlled" — two statements that cannot both hold. A prop whose description says it controls the component now keeps an empty Default. **`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint` directly; `turbo.json` declares no `//#lint` root task and only bulma-ui, create-bestax and bestax-migrate define one (each scoped to its own `src`), so `eslint scripts docs/scripts` — which exists only in the ROOT `lint` script — was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for `format:check`, left in place for `lint`; CI caught it only because `ci.yml` runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and verified by planting an unused variable in `scripts/`. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct three defects the #420 deep review found **The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested `/BulmaClassesProps/` against the whole node's source text, so it fired for `Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the name appears as the omit KEY LIST, not as the base being omitted. It returned before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`, so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All standard HTML attributes" half. That is the mechanism behind the menu.md (4 tables) and dropdown.md (2) regression against main. Matched structurally now: the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose first type argument does. **`impliedFalse` applied to controlled-mode booleans.** An optional `boolean` with no destructured default is documented as `false`, which is right for a flag and wrong for a controlled prop, where `undefined` is what selects UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its own description says "If provided, component is controlled" — two statements that cannot both hold. A prop whose description says it controls the component now keeps an empty Default. **`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint` directly; `turbo.json` declares no `//#lint` root task and only bulma-ui, create-bestax and bestax-migrate define one (each scoped to its own `src`), so `eslint scripts docs/scripts` — which exists only in the ROOT `lint` script — was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for `format:check`, left in place for `lint`; CI caught it only because `ci.yml` runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and verified by planting an unused variable in `scripts/`. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(bulma-ui): correct the inverted Level.isMobile description Bulma's `.level.is-mobile` sets `display: flex; flex-direction: row` (`bulma/sass/layout/level.scss:27-30`) — it keeps the level HORIZONTAL on mobile. A level without it stacks vertically below tablet. The description said the opposite: "Enables mobile layout (stacks vertically on mobile)", and the Usage prose repeated it. The wording is pre-existing on main, in both the props table and the prose, so this stack did not introduce it. It matters here because the migration seeds TSDoc from the doc pages, which launders a page-level error into the shipped `.d.ts` and users' editor tooltips — a wider blast radius than the page alone. Corrected at the source, so the generated row follows, and in the hand-written Usage paragraph. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): close the theme.md fence my earlier edit left open CI caught this and it is my regression, not a pre-existing one: main's `theme.md` passes `prettier --check` at its real path. Removing the stray four-backtick fence pair around "Theme with Styling" took out a fence that was load-bearing. The outer ```` closed the inner ```tsx block (a longer fence closes a shorter one), so deleting the pair left the StyledTheme example unterminated — it swallowed the "### Nested Themes" heading and the block after it, and `prettier --check` failed on the file. Closing the ```tsx block explicitly gives the structure the section was always meant to have: the heading renders as a heading and each example is its own closed block. `pnpm run format:check` is green across the repo. Worth recording how this got through: I ran typecheck, lint, tests and the docs build locally but not the ROOT `format:check` — which is exactly the "pnpm all green, CI red" gap this stack exists to close, and the gap only closed for `lint` two commits ago. The root format check is in my gate from here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * docs: restore the flatten-llms-tabs description this stack replaced Fourth instance of the same silent-deletion pattern, found by the deep review on #420. `docs/CLAUDE.md`'s "LLM docs pipeline" section documented `scripts/flatten-llms-tabs.mjs`; this stack swapped in the `strip-generated-markers.mjs` paragraph in its place rather than alongside it. The script is not dead. It is still the FIRST step in docs' build chain (`docusaurus build && node scripts/flatten-llms-tabs.mjs && node scripts/strip-generated-markers.mjs`), still covered by `flatten-llms-tabs.test.mjs`, and does something unrelated to what displaced it — flattening `<PackageManagerTabs>`/`<Tabs>` MDX so the JSX does not land verbatim in `llms.txt`/`llms-full.txt`. Nothing else in the repo documented it, so a reader had no way to learn it exists or why it cannot be a Docusaurus plugin. Both steps are now described in the order the chain runs them. Same mechanism as the `gen-component-catalog.mjs` preamble, the manifest downgrades and the deleted `style-mapping-sync` check: a replacement where an addition was meant, with no conflict to force a second look. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ --------- Co-authored-by: Claude <noreply@anthropic.com>
* docs: add the API page generator, its CI gates and review aids Lands the tooling for generating the derivable parts of docs/docs/api, with no page generated yet: MANAGED_CATEGORIES and ORDERED_CATEGORIES both start empty and grow one category per follow-up, so the 87 page diffs stay reviewable instead of arriving as one 199-file change. Four regions per page, delimited by `<!-- bestax:generated <id> -->` markers — `overview` (the component's TSDoc summary), `import` (the public barrel), `props` (the `<X>Props` interfaces via the TypeScript compiler API) and `cssvars` (a new section parsed from the SCSS). Everything outside a marker pair is hand-written and preserved byte-for-byte; deleting a pair opts that region out, and `docs-section-order` makes that visible rather than silent. Three choices keep the output reading as hand-written rather than as typedoc: own members only, with the one catch-all `...` row the pages already wrote by hand; types from AST source text, never `checker.typeToString`, which expands `(typeof validColors)[number]` into 19 literals; and wide colour unions rendered as a link to the existing Valid values page. CI gates live in check-conformance.mjs, which already runs in CI, rather than a new workflow step: `docs-generated` recomputes each managed page in memory and diffs, `docs-section-order` holds the order and marker presence. Both were verified by tampering. Also closes two gate gaps this tooling exposed. `pnpm all` ran `turbo run format:check`, which only runs the per-package scripts — none cover `scripts/`, `docs/scripts/` or any `.md`; CI runs the root script, which does, so CI could be red while `pnpm all` was green. And nothing linted `scripts/` at all. `all` now calls the root format check and `lint` also runs eslint over `scripts` and `docs/scripts`; verified by planting an unused variable. Two review aids ship alongside, both one-shot and both used to validate the migration that follows: `check-docs-parity.mjs` fails on any prop, default, description word, code span, URL, live example or prose line that a generated page drops relative to its hand-written self, and `check-docs-wording.mjs` buckets every previously-documented prop by how its description changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7 * docs(bulma-ui): generate the layout, columns and grid API pages First categories through the generator, plus `helpers/` reordered but not generated — four of its six pages document hooks with `## API` and no `## Props` at all, and `config.md`/`theme.md` keep their hand-written tables (theme.md's Props section is a ~350-line prose sub-API, not a table). `@property` blocks move onto the interface members as inline TSDoc, so a description is verifiable by position and reaches users' editors and the shipped .d.ts. Descriptions were seeded FROM the docs pages, not the other way round: the hand-written tables are consistently the richer text, and the page stays authoritative wherever the two disagree. Component TSDoc summaries were likewise seeded from the pages — the existing ones ("Bulma Hero component root.", "Container component for Bulma.") were worse than the prose they would have replaced. That text ships in the .d.ts, so read the `component-catalog.md` diff: it is the canary for this step. Verified: check-docs-parity reports 0 losses across all 87 pages; the generator is idempotent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7 * docs(bulma-ui): generate the elements API pages 30 pages. Two fixes to the extractor that this category forced, both found by check-docs-parity rather than by reading pages: - Heritage through `Omit`/`Pick` and imported (alias) symbols. `LinkButtonProps extends Omit<ButtonProps, …>` resolved the symbol of `Omit` — a lib type — so all 14 of its inherited props landed in the catch-all row instead of a table. - `Table`'s six sub-components are imported rather than declared locally, so the page rendered no sub-tables at all and the cell components' props vanished. Props the old tables documented but no interface declares (`href` on Link, `value` on ListItem, the `<ol>` attributes on OrderedList, `skeleton` from the helper props) are parked as `@extraProp`, carrying their type and default. That keeps the page's information in the source, where the rest of it now lives. `Button`'s component JSDoc was attached to an unrelated `const` rather than to `Button`, so its summary never reached the page; moved. Verified: 0 parity losses across all 87 pages; of the 215 previously-documented props on these pages, 178 read identically and 37 keep their sentence with more appended — none replaced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7 * chore: restore the component-catalog preamble this stack reverted `scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble predating #365, so regenerating deleted 21 lines of shipped guidance: the per-component value-union warning, where to find the installed `.d.ts`, the `Skeleton` helper-prop exception, which sub-component families accept helper props, and the custom-component composition spine. The checked-in `component-catalog.md` still had #365's text, so `gen:catalog:check` regenerated it shorter and failed `git diff --exit-code` — this is why Build and Test is red on this PR. Restores the generator to main's version. The catalog now regenerates byte-identical to the committed file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * chore: restore the component-catalog preamble this stack reverted `scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble predating #365, so regenerating deleted 21 lines of shipped guidance: the per-component value-union warning, where to find the installed `.d.ts`, the `Skeleton` helper-prop exception, which sub-component families accept helper props, and the custom-component composition spine. This branch regenerated the catalog with that stale generator, committing the loss into a shipped skill artifact. Restores the generator to main's version and regenerates; the preamble matches main again and the component one-liners are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * chore: restore the component-catalog preamble this stack reverted `scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble predating #365, so regenerating deleted 21 lines of shipped guidance: the per-component value-union warning, where to find the installed `.d.ts`, the `Skeleton` helper-prop exception, which sub-component families accept helper props, and the custom-component composition spine. This branch regenerated the catalog with that stale generator, committing the loss into a shipped skill artifact. Restores the generator to main's version and regenerates; the preamble matches main again and the component one-liners are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct four prop-table defects in the API page extractor Found by comparing every generated page against its origin/main original in a browser, and against the interfaces the tables claim to describe. - Bare alias names where the hand-written tables had real types. A one-member "union" (`type CellSpanValue = number`) was never a union node, and a union naming another alias (`BulmaFixedGridColsProp = BulmaFixedGridCols | 'auto'`) failed the all-members-simple test. Both fell through to the bare name with no `**Types:**` footnote, so cell.md's `colSpan`/`rowSpan` said `CellSpanValue` where main said `number`, and grid.md's `fixedCols` was opaque beside five siblings expanded to `0 | … | 12` in the same table. Member aliases now resolve to a fixpoint. - `children` synthesized for components that never render it. The row was emitted for any interface with a DOM base, but inheriting `children` is not rendering it: Divider spreads onto `<hr>`, so the row documented the one thing React throws on ("hr is a void element tag and must neither have `children`…"), and Icon always supplies its own JSX children, so anything passed is silently dropped. Emitted only where the implementation names `children`; the catch-all row still covers pass-through cases. - Sub-components dropped when their props are an inline DOM type rather than a named `*Props` interface. `Navbar.Divider` and `Pagination.Ellipsis` were omitted from the generated Subcomponents lists entirely — which is why both pages still carried a hand-written duplicate list. They are now listed (with no table, since they add no props of their own). - `never` and `false | true` merged from the branches of a discriminated union. slider.md rendered `minDistance` as `never | number` and `range` as `false | true`; the forbidding branch's `never` is noise standing where the reader needs a type. Verified: generator idempotent, all ten conformance checks green, check-docs-parity still reports 0 prop/default/description losses across all 87 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): drop the stale duplicate tables the elements pages kept `table.md` and `figure.md` still carried their pre-migration sub-component tables immediately after the generated block, so every sub-component was documented twice with disagreeing values — `isSelected` defaulted to `false` in the generated table and `—` in the legacy copy, and figure's stale `Figure.Caption Props` still dumped the 19-member colour union inline that this migration exists to remove. The generated tables supersede both. `delete.md`, `divider.md` and `icon.md` lose a synthesized `children` row for props those components do not render — `<hr>` throws on children and `Icon` overrides them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): regenerate the grid pages with the corrected type cells Picks up the extractor fix from the base branch. `cell.md`'s `colSpan` and `rowSpan` go back to `number` — they had regressed to a bare `CellSpanValue`, which aliases exactly `number` and so told the reader strictly less. `grid.md`'s `fixedCols` expands to `0 | … | 12 | 'auto'` instead of a bare `BulmaFixedGridColsProp`; it was the only opaque cell in a table whose five sibling props were already fully expanded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): add missing DOM_ELEMENT_LABELS entries and stop mislinking Helper Props Three elements (Divider/hr, Pre, Progress) fell back to the generic "HTML" label because DOM_ELEMENT_LABELS had no entry for their concrete interfaces. Separately, skeleton.md's catch-all row linked to Helper Props even though SkeletonProps doesn't extend BulmaClassesProps and Skeleton never calls useBulmaClasses — catchAllRow() now reports whether BulmaClassesProps is actually in the heritage, and the generator only renders the link when it is. Co-authored-by: Alex Smith <allxsmith@users.noreply.github.com> * fix(docs): carry the catch-all fix through the rest of the stack `scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the `catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without them and made #423 — which is stacked above #422 — revert them. Same file, same content, applied across the stack so the tip is consistent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): carry the catch-all fix through the rest of the stack `scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the `catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without them and made #423 — which is stacked above #422 — revert them. Same file, same content, applied across the stack so the tip is consistent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): expand DateFormatOption instead of leaving a bare alias The three datetime pages had regressed from main's `string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is declared in `form/_pickerInternals/` and is not exported from the barrel — so a reader could neither import it nor look it up, and no `**Types:**` footnote covered it either. A union member naming a type the alias index cannot resolve (`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier) now falls back to its own source text. Blast radius is exactly the three `format` cells; no other page changes. Found by an exhaustive cell-by-cell diff of all 87 generated pages against their origin/main originals, which also settles the rest: one genuine prop row lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type or default difference is either an improvement over drifted prose or a bare alias that does carry a footnote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): expand DateFormatOption instead of leaving a bare alias The three datetime pages had regressed from main's `string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is declared in `form/_pickerInternals/` and is not exported from the barrel — so a reader could neither import it nor look it up, and no `**Types:**` footnote covered it either. A union member naming a type the alias index cannot resolve (`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier) now falls back to its own source text. Blast radius is exactly the three `format` cells; no other page changes. Found by an exhaustive cell-by-cell diff of all 87 generated pages against their origin/main originals, which also settles the rest: one genuine prop row lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type or default difference is either an improvement over drifted prose or a bare alias that does carry a footnote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): expand DateFormatOption instead of leaving a bare alias The three datetime pages had regressed from main's `string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is declared in `form/_pickerInternals/` and is not exported from the barrel — so a reader could neither import it nor look it up, and no `**Types:**` footnote covered it either. A union member naming a type the alias index cannot resolve (`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier) now falls back to its own source text. Blast radius is exactly the three `format` cells; no other page changes. Found by an exhaustive cell-by-cell diff of all 87 generated pages against their origin/main originals, which also settles the rest: one genuine prop row lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type or default difference is either an improvement over drifted prose or a bare alias that does carry a footnote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): rebase the manifests onto main and address the review findings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): rebase the manifests onto main and address the review findings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): rebase the manifests onto main and address the review findings **Manifests.** These branches were cut before 19 commits landed on main and were never refreshed, so relative to their OWN merge base they downgraded `@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2, `@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2, `prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*` 3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the `flatten-llms-tabs` tests #408 added. Because the branches modify those lines rather than merely trailing main, a merge would have carried the downgrades in. `package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with only this stack's own script additions re-applied on top: `gen:api-sources`, `gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts` and `docs/scripts`, the root `format:check` fix in `all`, and the marker-stripping step chained after the llms flattener. The stack adds no dependency of its own, so main's lockfile is exactly right. **Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422: - The alias fixpoint resolved the mixed unions and the `indirect` renames in two separate passes, so a mixed union naming a forward-only alias — or a rename pointing at a mixed alias — stayed opaque forever. Both reviewers flagged it independently. The two now interleave in one bounded fixpoint. - `rendersChildren` matched `/\bchildren\b/` against raw source text, which counts the word in a comment or an unrelated string. It is an AST walk now. - Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the migration stacked the new one on top of the old instead of replacing it, so dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break clause the first block had and the page had lost. - A small local interface named in a type cell now renders its object shape: `icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?: string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is exported from the barrel, so the bare name was unlookupable. - Two pre-existing `helpers/` defects, relocated by this stack's reorder and worth fixing while the pages are open: a stray four-backtick fence in `theme.md` trapped the "Theme with Styling" heading and its example inside a code block, and `usebulmaclasses.md` claimed a `className` the example never passes and `has-text-info-mobile` for `color: 'link'`. Both refactors are behaviour-preserving: the generator emits byte-identical output for them today. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is unchanged at one lost prop row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): restore bestax-migrate's manifest and the lockfile from main Rebasing the root and docs manifests left `bestax-migrate/package.json` behind, and regenerating the lockfile against it reintroduced a stale entry: the branch still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417 moved it to `devDependencies`, and is missing the `prepack`/`postpack` pack-manifest hooks those PRs added. The branch never modified that file — it only trails main — so taking main's copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): restore bestax-migrate's manifest and the lockfile from main Rebasing the root and docs manifests left `bestax-migrate/package.json` behind, and regenerating the lockfile against it reintroduced a stale entry: the branch still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417 moved it to `devDependencies`, and is missing the `prepack`/`postpack` pack-manifest hooks those PRs added. The branch never modified that file — it only trails main — so taking main's copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): restore bestax-migrate's manifest and the lockfile from main Rebasing the root and docs manifests left `bestax-migrate/package.json` behind, and regenerating the lockfile against it reintroduced a stale entry: the branch still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417 moved it to `devDependencies`, and is missing the `prepack`/`postpack` pack-manifest hooks those PRs added. The branch never modified that file — it only trails main — so taking main's copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): fail the alias fixpoint instead of degrading to bare names The deep review on #421 flagged that the alias-resolution fixpoint degrades silently: if a chain needs more than the round bound, the survivors fall back to a bare name plus a `**Types:**` footnote rather than the expansion, and nothing says so. Running out of rounds is a different condition from settling. An alias that genuinely cannot expand stops making progress and the loop exits clean — that path is unchanged. But if the bound cuts the loop off while it is still resolving, the survivors render as bare identifiers, which is the exact regression this generator exists to prevent. That case now throws, naming the unresolved aliases. The bound is a named constant (`ALIAS_FIXPOINT_ROUNDS`) so the error can point at it. Chains in this repo settle in two rounds; the bound only exists to stop a mutually-recursive pair spinning. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): make the marker stripper fail instead of no-opping silently The deep review asked whether this step fails loudly when it matches nothing. It did not — it logged "removed 0 marker(s) from 0 file(s)" and exited 0. That is the one failure this step exists to prevent. Its own header records why it is a build step and not a plugin: the first attempt was a `postBuild` hook, `postBuild` runs under `Promise.all`, it raced ahead of docusaurus-plugin-llms and silently found nothing to strip. A quiet no-op here ships ~600 markers into llms-full.txt and every per-page `.md` twin with a green build. Stripping nothing is only correct when there was nothing to strip, so the check compares against the SOURCE pages: zero stripped AND zero markers in docs/docs/api is the legitimate "no managed categories yet" state (which is this branch, with MANAGED_CATEGORIES empty). Zero stripped while the source carries markers means the built markdown moved, the marker format changed, or the ordering regressed — that now exits 1 and says which. Also answers the other half of the question: the glob only ever reaches `.md` files plus llms.txt / llms-full.txt, so it cannot touch built HTML. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct three defects the #420 deep review found **The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested `/BulmaClassesProps/` against the whole node's source text, so it fired for `Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the name appears as the omit KEY LIST, not as the base being omitted. It returned before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`, so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All standard HTML attributes" half. That is the mechanism behind the menu.md (4 tables) and dropdown.md (2) regression against main. Matched structurally now: the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose first type argument does. **`impliedFalse` applied to controlled-mode booleans.** An optional `boolean` with no destructured default is documented as `false`, which is right for a flag and wrong for a controlled prop, where `undefined` is what selects UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its own description says "If provided, component is controlled" — two statements that cannot both hold. A prop whose description says it controls the component now keeps an empty Default. **`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint` directly; `turbo.json` declares no `//#lint` root task and only bulma-ui, create-bestax and bestax-migrate define one (each scoped to its own `src`), so `eslint scripts docs/scripts` — which exists only in the ROOT `lint` script — was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for `format:check`, left in place for `lint`; CI caught it only because `ci.yml` runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and verified by planting an unused variable in `scripts/`. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct three defects the #420 deep review found **The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested `/BulmaClassesProps/` against the whole node's source text, so it fired for `Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the name appears as the omit KEY LIST, not as the base being omitted. It returned before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`, so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All standard HTML attributes" half. That is the mechanism behind the menu.md (4 tables) and dropdown.md (2) regression against main. Matched structurally now: the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose first type argument does. **`impliedFalse` applied to controlled-mode booleans.** An optional `boolean` with no destructured default is documented as `false`, which is right for a flag and wrong for a controlled prop, where `undefined` is what selects UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its own description says "If provided, component is controlled" — two statements that cannot both hold. A prop whose description says it controls the component now keeps an empty Default. **`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint` directly; `turbo.json` declares no `//#lint` root task and only bulma-ui, create-bestax and bestax-migrate define one (each scoped to its own `src`), so `eslint scripts docs/scripts` — which exists only in the ROOT `lint` script — was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for `format:check`, left in place for `lint`; CI caught it only because `ci.yml` runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and verified by planting an unused variable in `scripts/`. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): correct three defects the #420 deep review found **The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested `/BulmaClassesProps/` against the whole node's source text, so it fired for `Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the name appears as the omit KEY LIST, not as the base being omitted. It returned before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`, so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All standard HTML attributes" half. That is the mechanism behind the menu.md (4 tables) and dropdown.md (2) regression against main. Matched structurally now: the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose first type argument does. **`impliedFalse` applied to controlled-mode booleans.** An optional `boolean` with no destructured default is documented as `false`, which is right for a flag and wrong for a controlled prop, where `undefined` is what selects UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its own description says "If provided, component is controlled" — two statements that cannot both hold. A prop whose description says it controls the component now keeps an empty Default. **`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint` directly; `turbo.json` declares no `//#lint` root task and only bulma-ui, create-bestax and bestax-migrate define one (each scoped to its own `src`), so `eslint scripts docs/scripts` — which exists only in the ROOT `lint` script — was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for `format:check`, left in place for `lint`; CI caught it only because `ci.yml` runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and verified by planting an unused variable in `scripts/`. Verified: generator idempotent, ten of ten conformance checks green, `gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(bulma-ui): correct the inverted Level.isMobile description Bulma's `.level.is-mobile` sets `display: flex; flex-direction: row` (`bulma/sass/layout/level.scss:27-30`) — it keeps the level HORIZONTAL on mobile. A level without it stacks vertically below tablet. The description said the opposite: "Enables mobile layout (stacks vertically on mobile)", and the Usage prose repeated it. The wording is pre-existing on main, in both the props table and the prose, so this stack did not introduce it. It matters here because the migration seeds TSDoc from the doc pages, which launders a page-level error into the shipped `.d.ts` and users' editor tooltips — a wider blast radius than the page alone. Corrected at the source, so the generated row follows, and in the hand-written Usage paragraph. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * fix(docs): close the theme.md fence my earlier edit left open CI caught this and it is my regression, not a pre-existing one: main's `theme.md` passes `prettier --check` at its real path. Removing the stray four-backtick fence pair around "Theme with Styling" took out a fence that was load-bearing. The outer ```` closed the inner ```tsx block (a longer fence closes a shorter one), so deleting the pair left the StyledTheme example unterminated — it swallowed the "### Nested Themes" heading and the block after it, and `prettier --check` failed on the file. Closing the ```tsx block explicitly gives the structure the section was always meant to have: the heading renders as a heading and each example is its own closed block. `pnpm run format:check` is green across the repo. Worth recording how this got through: I ran typecheck, lint, tests and the docs build locally but not the ROOT `format:check` — which is exactly the "pnpm all green, CI red" gap this stack exists to close, and the gap only closed for `lint` two commits ago. The root format check is in my gate from here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ * docs: restore the flatten-llms-tabs description this stack replaced Fourth instance of the same silent-deletion pattern, found by the deep review on #420. `docs/CLAUDE.md`'s "LLM docs pipeline" section documented `scripts/flatten-llms-tabs.mjs`; this stack swapped in the `strip-generated-markers.mjs` paragraph in its place rather than alongside it. The script is not dead. It is still the FIRST step in docs' build chain (`docusaurus build && node scripts/flatten-llms-tabs.mjs && node scripts/strip-generated-markers.mjs`), still covered by `flatten-llms-tabs.test.mjs`, and does something unrelated to what displaced it — flattening `<PackageManagerTabs>`/`<Tabs>` MDX so the JSX does not land verbatim in `llms.txt`/`llms-full.txt`. Nothing else in the repo documented it, so a reader had no way to learn it exists or why it cannot be a Docusaurus plugin. Both steps are now described in the order the chain runs them. Same mechanism as the `gen-component-catalog.mjs` preamble, the manifest downgrades and the deleted `style-mapping-sync` check: a replacement where an addition was meant, with no conflict to force a second look. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Alex Smith <allxsmith@users.noreply.github.com>
* docs: add the API page generator, its CI gates and review aids
Lands the tooling for generating the derivable parts of docs/docs/api, with no
page generated yet: MANAGED_CATEGORIES and ORDERED_CATEGORIES both start empty
and grow one category per follow-up, so the 87 page diffs stay reviewable
instead of arriving as one 199-file change.
Four regions per page, delimited by `<!-- bestax:generated <id> -->` markers —
`overview` (the component's TSDoc summary), `import` (the public barrel),
`props` (the `<X>Props` interfaces via the TypeScript compiler API) and
`cssvars` (a new section parsed from the SCSS). Everything outside a marker pair
is hand-written and preserved byte-for-byte; deleting a pair opts that region
out, and `docs-section-order` makes that visible rather than silent.
Three choices keep the output reading as hand-written rather than as typedoc:
own members only, with the one catch-all `...` row the pages already wrote by
hand; types from AST source text, never `checker.typeToString`, which expands
`(typeof validColors)[number]` into 19 literals; and wide colour unions rendered
as a link to the existing Valid values page.
CI gates live in check-conformance.mjs, which already runs in CI, rather than a
new workflow step: `docs-generated` recomputes each managed page in memory and
diffs, `docs-section-order` holds the order and marker presence. Both were
verified by tampering.
Also closes two gate gaps this tooling exposed. `pnpm all` ran
`turbo run format:check`, which only runs the per-package scripts — none cover
`scripts/`, `docs/scripts/` or any `.md`; CI runs the root script, which does, so
CI could be red while `pnpm all` was green. And nothing linted `scripts/` at
all. `all` now calls the root format check and `lint` also runs eslint over
`scripts` and `docs/scripts`; verified by planting an unused variable.
Two review aids ship alongside, both one-shot and both used to validate the
migration that follows: `check-docs-parity.mjs` fails on any prop, default,
description word, code span, URL, live example or prose line that a generated
page drops relative to its hand-written self, and `check-docs-wording.mjs`
buckets every previously-documented prop by how its description changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7
* docs(bulma-ui): generate the layout, columns and grid API pages
First categories through the generator, plus `helpers/` reordered but not
generated — four of its six pages document hooks with `## API` and no `## Props`
at all, and `config.md`/`theme.md` keep their hand-written tables (theme.md's
Props section is a ~350-line prose sub-API, not a table).
`@property` blocks move onto the interface members as inline TSDoc, so a
description is verifiable by position and reaches users' editors and the shipped
.d.ts. Descriptions were seeded FROM the docs pages, not the other way round:
the hand-written tables are consistently the richer text, and the page stays
authoritative wherever the two disagree.
Component TSDoc summaries were likewise seeded from the pages — the existing ones
("Bulma Hero component root.", "Container component for Bulma.") were worse than
the prose they would have replaced. That text ships in the .d.ts, so read the
`component-catalog.md` diff: it is the canary for this step.
Verified: check-docs-parity reports 0 losses across all 87 pages; the generator
is idempotent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7
* docs(bulma-ui): generate the elements API pages
30 pages. Two fixes to the extractor that this category forced, both found by
check-docs-parity rather than by reading pages:
- Heritage through `Omit`/`Pick` and imported (alias) symbols. `LinkButtonProps
extends Omit<ButtonProps, …>` resolved the symbol of `Omit` — a lib type — so
all 14 of its inherited props landed in the catch-all row instead of a table.
- `Table`'s six sub-components are imported rather than declared locally, so the
page rendered no sub-tables at all and the cell components' props vanished.
Props the old tables documented but no interface declares (`href` on Link,
`value` on ListItem, the `<ol>` attributes on OrderedList, `skeleton` from the
helper props) are parked as `@extraProp`, carrying their type and default. That
keeps the page's information in the source, where the rest of it now lives.
`Button`'s component JSDoc was attached to an unrelated `const` rather than to
`Button`, so its summary never reached the page; moved.
Verified: 0 parity losses across all 87 pages; of the 215 previously-documented
props on these pages, 178 read identically and 37 keep their sentence with more
appended — none replaced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7
* docs(bulma-ui): generate the components and form API pages
The last 41 pages, completing the migration: all 87 now share one section order
and one table shape.
Four extractor fixes this pair forced, each found by check-docs-parity:
- Props types that are type ALIASES, not interfaces. `ControlProps` and
`SliderProps` are unions, and treating a props type as necessarily an interface
rendered both pages with no table at all — 42 props.
- Defaults destructured in the function body rather than the parameter (`Slider`,
`TimeInputBase`), and defaults belonging to a base module a thin wrapper
renders (`TimeInput` -> `TimeInputBase`). `editable` read as `false` on
timeinput.md where the source says `true`.
- Type cells no longer show a bare alias name: short unions inline, long ones get
a `**Types:**` footnote built from the alias's own TSDoc.
- `**Subcomponents:**` renders each sub's summary sentence, which the pages
carried and a bare name list dropped; a sub with its own page is linked rather
than restated.
Two pages needed a hand edit first: tabs.md and taginput.md had prose sitting
BETWEEN prop tables, where the marker design has nowhere to put it.
Verified across all 87 pages: 0 parity losses, and of 1083 previously-documented
props 953 read identically, 117 keep their sentence with more appended, 12 have
it intact inside a longer one, 1 changed (a comma).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011c6ipfvBsYgF3Jf91ZzSr7
* chore: restore the component-catalog preamble this stack reverted
`scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble
predating #365, so regenerating deleted 21 lines of shipped guidance: the
per-component value-union warning, where to find the installed `.d.ts`, the
`Skeleton` helper-prop exception, which sub-component families accept helper
props, and the custom-component composition spine.
The checked-in `component-catalog.md` still had #365's text, so
`gen:catalog:check` regenerated it shorter and failed `git diff --exit-code` —
this is why Build and Test is red on this PR.
Restores the generator to main's version. The catalog now regenerates
byte-identical to the committed file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* chore: restore the component-catalog preamble this stack reverted
`scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble
predating #365, so regenerating deleted 21 lines of shipped guidance: the
per-component value-union warning, where to find the installed `.d.ts`, the
`Skeleton` helper-prop exception, which sub-component families accept helper
props, and the custom-component composition spine.
This branch regenerated the catalog with that stale generator, committing the
loss into a shipped skill artifact. Restores the generator to main's version
and regenerates; the preamble matches main again and the component one-liners
are unaffected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* chore: restore the component-catalog preamble this stack reverted
`scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble
predating #365, so regenerating deleted 21 lines of shipped guidance: the
per-component value-union warning, where to find the installed `.d.ts`, the
`Skeleton` helper-prop exception, which sub-component families accept helper
props, and the custom-component composition spine.
This branch regenerated the catalog with that stale generator, committing the
loss into a shipped skill artifact. Restores the generator to main's version
and regenerates; the preamble matches main again and the component one-liners
are unaffected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* chore: restore the component-catalog preamble this stack reverted
`scripts/gen-component-catalog.mjs` carried a copy of the catalog preamble
predating #365, so regenerating deleted 21 lines of shipped guidance: the
per-component value-union warning, where to find the installed `.d.ts`, the
`Skeleton` helper-prop exception, which sub-component families accept helper
props, and the custom-component composition spine.
This branch regenerated the catalog with that stale generator, committing the
loss into a shipped skill artifact. Restores the generator to main's version
and regenerates; the preamble matches main again and the component one-liners
are unaffected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): correct four prop-table defects in the API page extractor
Found by comparing every generated page against its origin/main original in a
browser, and against the interfaces the tables claim to describe.
- Bare alias names where the hand-written tables had real types. A one-member
"union" (`type CellSpanValue = number`) was never a union node, and a union
naming another alias (`BulmaFixedGridColsProp = BulmaFixedGridCols |
'auto'`) failed the all-members-simple test. Both fell through to the bare
name with no `**Types:**` footnote, so cell.md's `colSpan`/`rowSpan` said
`CellSpanValue` where main said `number`, and grid.md's `fixedCols` was
opaque beside five siblings expanded to `0 | … | 12` in the same table.
Member aliases now resolve to a fixpoint.
- `children` synthesized for components that never render it. The row was
emitted for any interface with a DOM base, but inheriting `children` is not
rendering it: Divider spreads onto `<hr>`, so the row documented the one
thing React throws on ("hr is a void element tag and must neither have
`children`…"), and Icon always supplies its own JSX children, so anything
passed is silently dropped. Emitted only where the implementation names
`children`; the catch-all row still covers pass-through cases.
- Sub-components dropped when their props are an inline DOM type rather than a
named `*Props` interface. `Navbar.Divider` and `Pagination.Ellipsis` were
omitted from the generated Subcomponents lists entirely — which is why both
pages still carried a hand-written duplicate list. They are now listed (with
no table, since they add no props of their own).
- `never` and `false | true` merged from the branches of a discriminated
union. slider.md rendered `minDistance` as `never | number` and `range` as
`false | true`; the forbidding branch's `never` is noise standing where the
reader needs a type.
Verified: generator idempotent, all ten conformance checks green,
check-docs-parity still reports 0 prop/default/description losses across all
87 pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): drop the stale duplicate tables the elements pages kept
`table.md` and `figure.md` still carried their pre-migration sub-component
tables immediately after the generated block, so every sub-component was
documented twice with disagreeing values — `isSelected` defaulted to `false`
in the generated table and `—` in the legacy copy, and figure's stale
`Figure.Caption Props` still dumped the 19-member colour union inline that
this migration exists to remove. The generated tables supersede both.
`delete.md`, `divider.md` and `icon.md` lose a synthesized `children` row for
props those components do not render — `<hr>` throws on children and `Icon`
overrides them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): regenerate the grid pages with the corrected type cells
Picks up the extractor fix from the base branch. `cell.md`'s `colSpan` and
`rowSpan` go back to `number` — they had regressed to a bare `CellSpanValue`,
which aliases exactly `number` and so told the reader strictly less. `grid.md`'s
`fixedCols` expands to `0 | … | 12 | 'auto'` instead of a bare
`BulmaFixedGridColsProp`; it was the only opaque cell in a table whose five
sibling props were already fully expanded.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(bulma-ui): stop three props tables documenting props the components reject
Found by comparing each generated page against its origin/main original in a
browser and against the interface it claims to describe.
`@extraProp` tags placed on the wrong interface published props that do not
exist. `RateProps` carried `index`, `isActive` and `isHovered` — fields of
`RateIconProps`, the object handed to `customIcon`, which `Rate` never
destructures; `CarouselProps` carried `active`, which belongs to
`CarouselItemProps`. Passing either lands an unknown attribute on the root
element and React warns. Both sets are already documented correctly in their
own sub-tables.
`DateTimeInput`'s three `@extraProp` tags omitted the `{type}` braces, so
`name`, `form` and `required` rendered with an empty Type cell and `required`
lost its default — the `DateInput` and `TimeInput` siblings had them.
Also restores prose the pages carried and the source TSDoc had lost, so the
generated Subcomponents bullets keep it: the `Panel.Icon` "accepts all Icon
props" note, `Navbar.Divider` and `Pagination.Ellipsis`'s descriptions, and
the five `Sidebar.*` summaries. Per this stack's own rule, where the page and
the comment disagree the page is authoritative.
Section order restored to Accessibility → Related on `collapse`, `loading`,
`tooltip` and `switch`, which had it inverted against the other 83 pages, and
the stale duplicate blocks removed from `navbar`, `panel`, `pagination` and
`sidebar` — `sidebar` was emitting duplicate heading anchors, which broke its
table of contents and deep links.
Verified: generator idempotent, ten of ten conformance checks green, docs
build clean, bulma-ui typecheck/lint/tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): add missing DOM_ELEMENT_LABELS entries and stop mislinking Helper Props
Three elements (Divider/hr, Pre, Progress) fell back to the generic "HTML"
label because DOM_ELEMENT_LABELS had no entry for their concrete interfaces.
Separately, skeleton.md's catch-all row linked to Helper Props even though
SkeletonProps doesn't extend BulmaClassesProps and Skeleton never calls
useBulmaClasses — catchAllRow() now reports whether BulmaClassesProps is
actually in the heritage, and the generator only renders the link when it is.
Co-authored-by: Alex Smith <allxsmith@users.noreply.github.com>
* fix(docs): carry the catch-all fix through the rest of the stack
`scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by
every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the
`catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without
them and made #423 — which is stacked above #422 — revert them. Same file,
same content, applied across the stack so the tip is consistent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): carry the catch-all fix through the rest of the stack
`scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by
every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the
`catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without
them and made #423 — which is stacked above #422 — revert them. Same file,
same content, applied across the stack so the tip is consistent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): carry the catch-all fix through the rest of the stack
`scripts/lib/props-extract.mjs` and `scripts/gen-api-docs.mjs` are shared by
every branch here, so landing the `DOM_ELEMENT_LABELS` additions and the
`catchAll -> {text, helpers}` change on #422 alone left #420 and #421 without
them and made #423 — which is stacked above #422 — revert them. Same file,
same content, applied across the stack so the tip is consistent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): expand DateFormatOption instead of leaving a bare alias
The three datetime pages had regressed from main's
`string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is
declared in `form/_pickerInternals/` and is not exported from the barrel — so a
reader could neither import it nor look it up, and no `**Types:**` footnote
covered it either.
A union member naming a type the alias index cannot resolve
(`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier)
now falls back to its own source text. Blast radius is exactly the three
`format` cells; no other page changes.
Found by an exhaustive cell-by-cell diff of all 87 generated pages against
their origin/main originals, which also settles the rest: one genuine prop row
lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type
or default difference is either an improvement over drifted prose or a bare
alias that does carry a footnote.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): expand DateFormatOption instead of leaving a bare alias
The three datetime pages had regressed from main's
`string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is
declared in `form/_pickerInternals/` and is not exported from the barrel — so a
reader could neither import it nor look it up, and no `**Types:**` footnote
covered it either.
A union member naming a type the alias index cannot resolve
(`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier)
now falls back to its own source text. Blast radius is exactly the three
`format` cells; no other page changes.
Found by an exhaustive cell-by-cell diff of all 87 generated pages against
their origin/main originals, which also settles the rest: one genuine prop row
lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type
or default difference is either an improvement over drifted prose or a bare
alias that does carry a footnote.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): expand DateFormatOption instead of leaving a bare alias
The three datetime pages had regressed from main's
`string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is
declared in `form/_pickerInternals/` and is not exported from the barrel — so a
reader could neither import it nor look it up, and no `**Types:**` footnote
covered it either.
A union member naming a type the alias index cannot resolve
(`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier)
now falls back to its own source text. Blast radius is exactly the three
`format` cells; no other page changes.
Found by an exhaustive cell-by-cell diff of all 87 generated pages against
their origin/main originals, which also settles the rest: one genuine prop row
lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type
or default difference is either an improvement over drifted prose or a bare
alias that does carry a footnote.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): expand DateFormatOption instead of leaving a bare alias
The three datetime pages had regressed from main's
`string | Intl.DateTimeFormatOptions` to a bare `DateFormatOption`, which is
declared in `form/_pickerInternals/` and is not exported from the barrel — so a
reader could neither import it nor look it up, and no `**Types:**` footnote
covered it either.
A union member naming a type the alias index cannot resolve
(`Intl.DateTimeFormatOptions` is a qualified name, not an indexed identifier)
now falls back to its own source text. Blast radius is exactly the three
`format` cells; no other page changes.
Found by an exhaustive cell-by-cell diff of all 87 generated pages against
their origin/main originals, which also settles the rest: one genuine prop row
lost across the whole migration (`card.md`'s `m`/`p`), and every remaining type
or default difference is either an improvement over drifted prose or a bare
alias that does carry a footnote.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): rebase the manifests onto main and address the review findings
**Manifests.** These branches were cut before 19 commits landed on main and
were never refreshed, so relative to their OWN merge base they downgraded
`@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2,
`@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2,
`prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*`
3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped
docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the
`flatten-llms-tabs` tests #408 added. Because the branches modify those lines
rather than merely trailing main, a merge would have carried the downgrades in.
`package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with
only this stack's own script additions re-applied on top: `gen:api-sources`,
`gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts`
and `docs/scripts`, the root `format:check` fix in `all`, and the
marker-stripping step chained after the llms flattener. The stack adds no
dependency of its own, so main's lockfile is exactly right.
**Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422:
- The alias fixpoint resolved the mixed unions and the `indirect` renames in
two separate passes, so a mixed union naming a forward-only alias — or a
rename pointing at a mixed alias — stayed opaque forever. Both reviewers
flagged it independently. The two now interleave in one bounded fixpoint.
- `rendersChildren` matched `/\bchildren\b/` against raw source text, which
counts the word in a comment or an unrelated string. It is an AST walk now.
- Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the
migration stacked the new one on top of the old instead of replacing it, so
dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break
clause the first block had and the page had lost.
- A small local interface named in a type cell now renders its object shape:
`icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?:
string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on
both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is
exported from the barrel, so the bare name was unlookupable.
- Two pre-existing `helpers/` defects, relocated by this stack's reorder and
worth fixing while the pages are open: a stray four-backtick fence in
`theme.md` trapped the "Theme with Styling" heading and its example inside a
code block, and `usebulmaclasses.md` claimed a `className` the example never
passes and `has-text-info-mobile` for `color: 'link'`.
Both refactors are behaviour-preserving: the generator emits byte-identical
output for them today. Verified: generator idempotent, ten of ten conformance
checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui
typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is
unchanged at one lost prop row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): rebase the manifests onto main and address the review findings
**Manifests.** These branches were cut before 19 commits landed on main and
were never refreshed, so relative to their OWN merge base they downgraded
`@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2,
`@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2,
`prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*`
3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped
docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the
`flatten-llms-tabs` tests #408 added. Because the branches modify those lines
rather than merely trailing main, a merge would have carried the downgrades in.
`package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with
only this stack's own script additions re-applied on top: `gen:api-sources`,
`gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts`
and `docs/scripts`, the root `format:check` fix in `all`, and the
marker-stripping step chained after the llms flattener. The stack adds no
dependency of its own, so main's lockfile is exactly right.
**Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422:
- The alias fixpoint resolved the mixed unions and the `indirect` renames in
two separate passes, so a mixed union naming a forward-only alias — or a
rename pointing at a mixed alias — stayed opaque forever. Both reviewers
flagged it independently. The two now interleave in one bounded fixpoint.
- `rendersChildren` matched `/\bchildren\b/` against raw source text, which
counts the word in a comment or an unrelated string. It is an AST walk now.
- Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the
migration stacked the new one on top of the old instead of replacing it, so
dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break
clause the first block had and the page had lost.
- A small local interface named in a type cell now renders its object shape:
`icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?:
string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on
both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is
exported from the barrel, so the bare name was unlookupable.
- Two pre-existing `helpers/` defects, relocated by this stack's reorder and
worth fixing while the pages are open: a stray four-backtick fence in
`theme.md` trapped the "Theme with Styling" heading and its example inside a
code block, and `usebulmaclasses.md` claimed a `className` the example never
passes and `has-text-info-mobile` for `color: 'link'`.
Both refactors are behaviour-preserving: the generator emits byte-identical
output for them today. Verified: generator idempotent, ten of ten conformance
checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui
typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is
unchanged at one lost prop row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): rebase the manifests onto main and address the review findings
**Manifests.** These branches were cut before 19 commits landed on main and
were never refreshed, so relative to their OWN merge base they downgraded
`@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2,
`@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2,
`prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*`
3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped
docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the
`flatten-llms-tabs` tests #408 added. Because the branches modify those lines
rather than merely trailing main, a merge would have carried the downgrades in.
`package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with
only this stack's own script additions re-applied on top: `gen:api-sources`,
`gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts`
and `docs/scripts`, the root `format:check` fix in `all`, and the
marker-stripping step chained after the llms flattener. The stack adds no
dependency of its own, so main's lockfile is exactly right.
**Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422:
- The alias fixpoint resolved the mixed unions and the `indirect` renames in
two separate passes, so a mixed union naming a forward-only alias — or a
rename pointing at a mixed alias — stayed opaque forever. Both reviewers
flagged it independently. The two now interleave in one bounded fixpoint.
- `rendersChildren` matched `/\bchildren\b/` against raw source text, which
counts the word in a comment or an unrelated string. It is an AST walk now.
- Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the
migration stacked the new one on top of the old instead of replacing it, so
dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break
clause the first block had and the page had lost.
- A small local interface named in a type cell now renders its object shape:
`icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?:
string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on
both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is
exported from the barrel, so the bare name was unlookupable.
- Two pre-existing `helpers/` defects, relocated by this stack's reorder and
worth fixing while the pages are open: a stray four-backtick fence in
`theme.md` trapped the "Theme with Styling" heading and its example inside a
code block, and `usebulmaclasses.md` claimed a `className` the example never
passes and `has-text-info-mobile` for `color: 'link'`.
Both refactors are behaviour-preserving: the generator emits byte-identical
output for them today. Verified: generator idempotent, ten of ten conformance
checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui
typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is
unchanged at one lost prop row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): rebase the manifests onto main and address the review findings
**Manifests.** These branches were cut before 19 commits landed on main and
were never refreshed, so relative to their OWN merge base they downgraded
`@commitlint/cli` 21.2.1→21.0.2, `@commitlint/config-conventional` 21.2.0→21.0.2,
`@semantic-release/github` 12.0.9→12.0.2, `semantic-release` 25.0.8→25.0.2,
`prettier` 3.9.6→3.9.4, `react`/`react-dom` 19.2.8→19.2.3, `@docusaurus/*`
3.10.2→3.9.2 and `@fortawesome/fontawesome-free` 7.3.1→7.2.0 — and dropped
docs' `"test": "node --test \"scripts/*.test.mjs\""`, silently disabling the
`flatten-llms-tabs` tests #408 added. Because the branches modify those lines
rather than merely trailing main, a merge would have carried the downgrades in.
`package.json`, `docs/package.json` and `pnpm-lock.yaml` are now main's, with
only this stack's own script additions re-applied on top: `gen:api-sources`,
`gen:api-docs`, `gen:api-docs:check`, `gen`, the `lint` widening over `scripts`
and `docs/scripts`, the root `format:check` fix in `all`, and the
marker-stripping step chained after the llms flattener. The stack adds no
dependency of its own, so main's lockfile is exactly right.
**Review findings.** From the CodeRabbit and Claude deep reviews on #421/#422:
- The alias fixpoint resolved the mixed unions and the `indirect` renames in
two separate passes, so a mixed union naming a forward-only alias — or a
rename pointing at a mixed alias — stayed opaque forever. Both reviewers
flagged it independently. The two now interleave in one bounded fixpoint.
- `rendersChildren` matched `/\bchildren\b/` against raw source text, which
counts the word in a comment or an unrelated string. It is an AST walk now.
- Six `gapSize*` props on `Columns.tsx` carried TWO JSDoc blocks — the
migration stacked the new one on top of the old instead of replacing it, so
dead comments shipped in the `.d.ts`. Collapsed, keeping the tie-break
clause the first block had and the page had lost.
- A small local interface named in a type cell now renders its object shape:
`icontext.md`'s `items` goes back to main's `{ iconProps: IconProps; text?:
string }[]` from a bare `IconTextItem`, and `slider.md`'s `marks` improves on
both to `{ value: number; label?: React.ReactNode }[]`. Neither interface is
exported from the barrel, so the bare name was unlookupable.
- Two pre-existing `helpers/` defects, relocated by this stack's reorder and
worth fixing while the pages are open: a stray four-backtick fence in
`theme.md` trapped the "Theme with Styling" heading and its example inside a
code block, and `usebulmaclasses.md` claimed a `className` the example never
passes and `has-text-info-mobile` for `color: 'link'`.
Both refactors are behaviour-preserving: the generator emits byte-identical
output for them today. Verified: generator idempotent, ten of ten conformance
checks green, `gen:catalog:check` exits 0, docs build clean, bulma-ui
typecheck/lint/tests pass, and the 87-page cell-by-cell diff against main is
unchanged at one lost prop row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): restore bestax-migrate's manifest and the lockfile from main
Rebasing the root and docs manifests left `bestax-migrate/package.json` behind,
and regenerating the lockfile against it reintroduced a stale entry: the branch
still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417
moved it to `devDependencies`, and is missing the `prepack`/`postpack`
pack-manifest hooks those PRs added.
The branch never modified that file — it only trails main — so taking main's
copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's
and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI
runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): restore bestax-migrate's manifest and the lockfile from main
Rebasing the root and docs manifests left `bestax-migrate/package.json` behind,
and regenerating the lockfile against it reintroduced a stale entry: the branch
still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417
moved it to `devDependencies`, and is missing the `prepack`/`postpack`
pack-manifest hooks those PRs added.
The branch never modified that file — it only trails main — so taking main's
copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's
and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI
runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): restore bestax-migrate's manifest and the lockfile from main
Rebasing the root and docs manifests left `bestax-migrate/package.json` behind,
and regenerating the lockfile against it reintroduced a stale entry: the branch
still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417
moved it to `devDependencies`, and is missing the `prepack`/`postpack`
pack-manifest hooks those PRs added.
The branch never modified that file — it only trails main — so taking main's
copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's
and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI
runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): restore bestax-migrate's manifest and the lockfile from main
Rebasing the root and docs manifests left `bestax-migrate/package.json` behind,
and regenerating the lockfile against it reintroduced a stale entry: the branch
still lists `@allxsmith/bestax-bulma` under `dependencies`, where #412/#417
moved it to `devDependencies`, and is missing the `prepack`/`postpack`
pack-manifest hooks those PRs added.
The branch never modified that file — it only trails main — so taking main's
copy is a clean fast-forward. `pnpm-lock.yaml` is now byte-identical to main's
and `pnpm install --frozen-lockfile` succeeds against it, which is the check CI
runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): fail the alias fixpoint instead of degrading to bare names
The deep review on #421 flagged that the alias-resolution fixpoint degrades
silently: if a chain needs more than the round bound, the survivors fall back
to a bare name plus a `**Types:**` footnote rather than the expansion, and
nothing says so.
Running out of rounds is a different condition from settling. An alias that
genuinely cannot expand stops making progress and the loop exits clean — that
path is unchanged. But if the bound cuts the loop off while it is still
resolving, the survivors render as bare identifiers, which is the exact
regression this generator exists to prevent. That case now throws, naming the
unresolved aliases.
The bound is a named constant (`ALIAS_FIXPOINT_ROUNDS`) so the error can point
at it. Chains in this repo settle in two rounds; the bound only exists to stop
a mutually-recursive pair spinning.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): make the marker stripper fail instead of no-opping silently
The deep review asked whether this step fails loudly when it matches nothing.
It did not — it logged "removed 0 marker(s) from 0 file(s)" and exited 0.
That is the one failure this step exists to prevent. Its own header records
why it is a build step and not a plugin: the first attempt was a `postBuild`
hook, `postBuild` runs under `Promise.all`, it raced ahead of
docusaurus-plugin-llms and silently found nothing to strip. A quiet no-op here
ships ~600 markers into llms-full.txt and every per-page `.md` twin with a
green build.
Stripping nothing is only correct when there was nothing to strip, so the
check compares against the SOURCE pages: zero stripped AND zero markers in
docs/docs/api is the legitimate "no managed categories yet" state (which is
this branch, with MANAGED_CATEGORIES empty). Zero stripped while the source
carries markers means the built markdown moved, the marker format changed, or
the ordering regressed — that now exits 1 and says which.
Also answers the other half of the question: the glob only ever reaches `.md`
files plus llms.txt / llms-full.txt, so it cannot touch built HTML.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): correct three defects the #420 deep review found
**The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested
`/BulmaClassesProps/` against the whole node's source text, so it fired for
`Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the
name appears as the omit KEY LIST, not as the base being omitted. It returned
before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`,
so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All
standard HTML attributes" half. That is the mechanism behind the menu.md (4
tables) and dropdown.md (2) regression against main. Matched structurally now:
the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose
first type argument does.
**`impliedFalse` applied to controlled-mode booleans.** An optional `boolean`
with no destructured default is documented as `false`, which is right for a
flag and wrong for a controlled prop, where `undefined` is what selects
UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its
own description says "If provided, component is controlled" — two statements
that cannot both hold. A prop whose description says it controls the component
now keeps an empty Default.
**`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint`
directly; `turbo.json` declares no `//#lint` root task and only bulma-ui,
create-bestax and bestax-migrate define one (each scoped to its own `src`), so
`eslint scripts docs/scripts` — which exists only in the ROOT `lint` script —
was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for
`format:check`, left in place for `lint`; CI caught it only because `ci.yml`
runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and
verified by planting an unused variable in `scripts/`.
Verified: generator idempotent, ten of ten conformance checks green,
`gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is
unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): correct three defects the #420 deep review found
**The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested
`/BulmaClassesProps/` against the whole node's source text, so it fired for
`Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the
name appears as the omit KEY LIST, not as the base being omitted. It returned
before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`,
so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All
standard HTML attributes" half. That is the mechanism behind the menu.md (4
tables) and dropdown.md (2) regression against main. Matched structurally now:
the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose
first type argument does.
**`impliedFalse` applied to controlled-mode booleans.** An optional `boolean`
with no destructured default is documented as `false`, which is right for a
flag and wrong for a controlled prop, where `undefined` is what selects
UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its
own description says "If provided, component is controlled" — two statements
that cannot both hold. A prop whose description says it controls the component
now keeps an empty Default.
**`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint`
directly; `turbo.json` declares no `//#lint` root task and only bulma-ui,
create-bestax and bestax-migrate define one (each scoped to its own `src`), so
`eslint scripts docs/scripts` — which exists only in the ROOT `lint` script —
was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for
`format:check`, left in place for `lint`; CI caught it only because `ci.yml`
runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and
verified by planting an unused variable in `scripts/`.
Verified: generator idempotent, ten of ten conformance checks green,
`gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is
unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): correct three defects the #420 deep review found
**The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested
`/BulmaClassesProps/` against the whole node's source text, so it fired for
`Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the
name appears as the omit KEY LIST, not as the base being omitted. It returned
before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`,
so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All
standard HTML attributes" half. That is the mechanism behind the menu.md (4
tables) and dropdown.md (2) regression against main. Matched structurally now:
the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose
first type argument does.
**`impliedFalse` applied to controlled-mode booleans.** An optional `boolean`
with no destructured default is documented as `false`, which is right for a
flag and wrong for a controlled prop, where `undefined` is what selects
UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its
own description says "If provided, component is controlled" — two statements
that cannot both hold. A prop whose description says it controls the component
now keeps an empty Default.
**`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint`
directly; `turbo.json` declares no `//#lint` root task and only bulma-ui,
create-bestax and bestax-migrate define one (each scoped to its own `src`), so
`eslint scripts docs/scripts` — which exists only in the ROOT `lint` script —
was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for
`format:check`, left in place for `lint`; CI caught it only because `ci.yml`
runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and
verified by planting an unused variable in `scripts/`.
Verified: generator idempotent, ten of ten conformance checks green,
`gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is
unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): correct three defects the #420 deep review found
**The helper-props shortcut swallowed the DOM base.** `classifyTypeNode` tested
`/BulmaClassesProps/` against the whole node's source text, so it fired for
`Omit<React.HTMLAttributes<HTMLElement>, keyof BulmaClassesProps>` — where the
name appears as the omit KEY LIST, not as the base being omitted. It returned
before the `Omit` branch below could unwrap `React.HTMLAttributes<HTMLElement>`,
so `HTMLElement` never reached `catchAllRow` and the catch-all lost its "All
standard HTML attributes" half. That is the mechanism behind the menu.md (4
tables) and dropdown.md (2) regression against main. Matched structurally now:
the node itself must name `BulmaClassesProps`, or be an `Omit`/`Pick` whose
first type argument does.
**`impliedFalse` applied to controlled-mode booleans.** An optional `boolean`
with no destructured default is documented as `false`, which is right for a
flag and wrong for a controlled prop, where `undefined` is what selects
UNCONTROLLED mode. `collapse.md` claimed `open` defaults to `false` while its
own description says "If provided, component is controlled" — two statements
that cannot both hold. A prop whose description says it controls the component
now keeps an empty Default.
**`pnpm all` never ran the widened lint.** `all` invoked `turbo run … lint`
directly; `turbo.json` declares no `//#lint` root task and only bulma-ui,
create-bestax and bestax-migrate define one (each scoped to its own `src`), so
`eslint scripts docs/scripts` — which exists only in the ROOT `lint` script —
was skipped. That is the same "pnpm all green, CI red" gap this stack fixed for
`format:check`, left in place for `lint`; CI caught it only because `ci.yml`
runs `pnpm run lint` as its own step. Routed through `pnpm run lint`, and
verified by planting an unused variable in `scripts/`.
Verified: generator idempotent, ten of ten conformance checks green,
`gen:catalog:check` exits 0, and the 87-page cell-by-cell diff against main is
unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(bulma-ui): correct the inverted Level.isMobile description
Bulma's `.level.is-mobile` sets `display: flex; flex-direction: row`
(`bulma/sass/layout/level.scss:27-30`) — it keeps the level HORIZONTAL on
mobile. A level without it stacks vertically below tablet. The description said
the opposite: "Enables mobile layout (stacks vertically on mobile)", and the
Usage prose repeated it.
The wording is pre-existing on main, in both the props table and the prose, so
this stack did not introduce it. It matters here because the migration seeds
TSDoc from the doc pages, which launders a page-level error into the shipped
`.d.ts` and users' editor tooltips — a wider blast radius than the page alone.
Corrected at the source, so the generated row follows, and in the hand-written
Usage paragraph.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* fix(docs): close the theme.md fence my earlier edit left open
CI caught this and it is my regression, not a pre-existing one: main's
`theme.md` passes `prettier --check` at its real path.
Removing the stray four-backtick fence pair around "Theme with Styling" took
out a fence that was load-bearing. The outer ```` closed the inner ```tsx
block (a longer fence closes a shorter one), so deleting the pair left the
StyledTheme example unterminated — it swallowed the "### Nested Themes"
heading and the block after it, and `prettier --check` failed on the file.
Closing the ```tsx block explicitly gives the structure the section was always
meant to have: the heading renders as a heading and each example is its own
closed block. `pnpm run format:check` is green across the repo.
Worth recording how this got through: I ran typecheck, lint, tests and the docs
build locally but not the ROOT `format:check` — which is exactly the "pnpm all
green, CI red" gap this stack exists to close, and the gap only closed for
`lint` two commits ago. The root format check is in my gate from here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* docs: restore the flatten-llms-tabs description this stack replaced
Fourth instance of the same silent-deletion pattern, found by the deep review
on #420. `docs/CLAUDE.md`'s "LLM docs pipeline" section documented
`scripts/flatten-llms-tabs.mjs`; this stack swapped in the
`strip-generated-markers.mjs` paragraph in its place rather than alongside it.
The script is not dead. It is still the FIRST step in docs' build chain
(`docusaurus build && node scripts/flatten-llms-tabs.mjs && node
scripts/strip-generated-markers.mjs`), still covered by
`flatten-llms-tabs.test.mjs`, and does something unrelated to what displaced
it — flattening `<PackageManagerTabs>`/`<Tabs>` MDX so the JSX does not land
verbatim in `llms.txt`/`llms-full.txt`. Nothing else in the repo documented it,
so a reader had no way to learn it exists or why it cannot be a Docusaurus
plugin.
Both steps are now described in the order the chain runs them.
Same mechanism as the `gen-component-catalog.mjs` preamble, the manifest
downgrades and the deleted `style-mapping-sync` check: a replacement where an
addition was meant, with no conflict to force a second look.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
* chore: remove the one-shot migration scripts now the migration has landed
Four scripts, 1,890 lines, invoked by nothing — not `package.json`, not
`ci.yml`, not `check-conformance.mjs`, and not any `.md`:
scripts/check-docs-parity.mjs 421
scripts/check-docs-wording.mjs 197
scripts/migrate-api-pages.mjs 490
scripts/codemod-property-tsdoc.mjs 782
`check-docs-parity.mjs` said so itself: "This is a migration tool, not
permanent CI: once the migration lands, `origin/main` becomes the new baseline
and `docs-generated` in check-conformance.mjs holds the line instead." With all
seven categories now migrated, that is exactly where things stand.
Two reasons this is more than tidying. #420 widened `lint` to cover `scripts/`,
so these would be linted and prettier-checked on every CI run and carried
through every eslint/prettier/Node upgrade for no ongoing value. And after
merge they would not fail, they would mislead: `check-docs-parity` defaults to
`--base=origin/main`, so it would diff main against itself and report "0 losses
across 87 pages" — a check that reads as evidence while proving nothing.
Ran one final time before deletion, as the last regression check on the
generated pages: 6 prose flags across 2 files, all previously reviewed and
intentional — navbar's `**Key Subcomponents:**` heading, superseded by the
generated list that now includes `Navbar.Divider`, and sidebar's five "Accepts
all standard <X> HTML attributes" lines, whose element names now live in the
generated sub-tables' catch-all rows.
Nothing else references them; git history keeps them if a future category ever
needs re-migrating.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019XkrYiLeAnyLBSa3KSweWJ
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Alex Smith <allxsmith@users.noreply.github.com>
|
🎉 This PR is included in version 4.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 2.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 5.8.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Unblocks #402. This is the LLM-pipeline half only — no docs pages change yet, and
PackageManagerTabsdoesn't exist yet. Landing the flattener first means the tabs migration can go page-by-page without regressing the machine-readable docs on the way.Why a build step and not a plugin
docusaurus-plugin-llmsreads the source markdown, not rendered HTML. Its only cleanup (cleanMarkdownContent,utils.ts:587) strips a fixed allowlist of plain HTML tags that doesn't includeTabs/TabItem, andPluginOptions(types.ts:69) exposes no transform hook. So tab JSX lands verbatim inllms.txt,llms-full.txtand every per-page.mdtwin — 112 tag occurrences today, from the skills pages alone.It can't be a sibling Docusaurus plugin either: core runs every plugin's
postBuildunderPromise.all(@docusaurus/core/lib/commands/build/buildLocale.js:106), so it would race the llms plugin nondeterministically. Hencedocusaurus build && node scripts/flatten-llms-tabs.mjs, which is also the pathdeploy.yml/test-deploy.ymlalready take viaturbo run build --filter=@allxsmith/bestax-docs.Two transforms, because there are two kinds of tabs
<PackageManagerTabs command="add …" />ProfileCard.tsx/_profilecard.scss/Usage####headings, keep every bodypnpm for the first because it's the default tab — what an agent copies out of
llms.txthas to match what a reader sees.commandis authored in pnpm's verb vocabulary so this stays a passthrough; npm/yarn/bun translation will live in the React component rather than being duplicated here.The second rule matters: my first draft of this kept only the first
TabItem, which would have silently deleted the skills pages' other two tabs.The trap worth reviewing
bestax-bulma has its own
Tabscomponent.<Tabs\bmatches<Tabs.List—\bsits between thesand the.. My first pass left 94 stragglers and would have corrupteddocs/api/components/tabs.md.Two mitigations, both required and both tested:
<Tabs(?=[\s>]), never<Tabs\b`<Tabs>`in prose. Mask rather than split, because a<Tabs>block wraps fences and the document has to stay contiguous for<Tabs>…</Tabs>to match across it.Verification
node --test— 13 tests, including the<Tabs.List>-in-fence regression, inline-span survival, idempotency, and placeholder-collision safety. Wired as the docs package'stestscript, sopnpm test/pnpm allpick it up through turbo with no CI changes.End-to-end against the current
build/:Known limitation
A braced-template prop —
command={`add foo`}— is deliberately unsupported: its backticks are indistinguishable from an inline code span, and masking has to run first. It's left untouched in the artifact rather than mangled, and there's a test pinning that behavior. Authors use a plain string.Not in scope
Converting the ~18 docs pages, building
PackageManagerTabs, and the homepage hero links — all still #402.Unrelated, noticed while running the gate:
pnpm format:checkfails locally on 133 files under.claude/worktrees/(the skill-loop eval worktree). That path is git-ignored so CI is unaffected, but Prettier only honors.prettierignore, so localpnpm allis noisy. Happy to add a.prettierignoreentry in a separate PR if you want it.Summary by CodeRabbit
New Features
Documentation
Tests