Repository navigation
Release new version - #3
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
May 24, 2025 00:54
0f6402c to
5ec4c4e
Compare
10 tasks done
Contributor
Author
|
🎉 This PR is included in version 1.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
allxsmith
added a commit
that referenced
this pull request
Jul 26, 2026
Resolves the 13 CodeRabbit + Claude deep-review threads on #366. Correctness of the agent protocols: - Point grader/improver prompts and the report artifact map at eval/skill-loop/, not the nonexistent experiment/skill-loop/ — a dispatched grader could not find the rubric, defeating the frozen-rubric-authority guardrail. - Name the frozen brief by its shipped path (briefs/skynet-saas.md); base-prompt.md exists nowhere in the harness, so the "never edit" guardrail protected nothing. - Grader transcript greps now target "$RUN/transcript.jsonl" instead of t.jsonl, so they work when copied as written. Runner robustness (bin/run-iteration.sh): - set -euo pipefail, so a failed tooling rebuild, scaffold, install, snapshot, or metrics collection cannot fall through to "done" and record a run built from stale tooling. The builder's own exit code stays captured-not-enforced. - Canonicalize the work-dir before the isolation check and compare on a path component boundary: a relative arg such as eval/work previously slipped past the raw prefix match and scaffolded inside the repo, where pnpm workspace-links the local library and invalidates the registry-package measurement. A sibling path sharing the repo prefix is no longer falsely rejected, and a rejected work-dir now leaves no directory behind. - Write metrics.json via a temp file so a collector crash cannot leave a truncated artifact that both looks like a datapoint and blocks the retry guard. Metrics (bin/collect-metrics.mjs + committed runs): - app_dir records only the trailing <run>/<app> segments; the absolute path leaked a local username and made the evidence non-portable. - Harvest skill paths from every tool input, not just file_path — builders that reached references with Bash cat/sed counted reads against an empty skill_files. - Add skill_files_complete so an empty skill_files is unambiguous: false marks the three historical runs (i05, i08, i10) whose paths were never recoverable. Historical record: - Record the disproven zero-CSS featured ring in the iteration log and in the i08, i09, and i10 scorecards, matching the correction already in report.md. Scores are left as graded; the notes record the error rather than restating the runs. - Correct the i10 Tag isLight finding to a retention/verification failure — theming SKILL.md carries the ban and was read in full, as iteration-log grader-noise correction #3 already states. - Wrap literal has-text-* and Card.* wildcards in code spans (markdownlint MD037).
allxsmith
pushed a commit
that referenced
this pull request
Jul 29, 2026
…Bulma post - Retitle to "Bulma Is the #3 CSS Framework — and AI Will Never Tell You About It", leading with the post's actual thesis instead of a ranking. Shorter sidebar_label and a matching description. The filename, slug and canonical_url are unchanged, so the published URL stays put. - Give the monoculture-vs-ecosystem cards equal heights via display="flex" on each Column and flexGrow="1" on the Box, so they line up regardless of text length, with a sentence naming the helper props. - Add two linked star buttons to the Rate demo, pointing at the bestax and Bulma repos, plus a line telling readers the demo stars are a toy and the ones that count are two clicks away. Verified on a production build: 37 live blocks, 0 react-live errors, both cards measure 216px, and the star buttons resolve to the right repos. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fghz3LG6ePvKig6JTjazLa
allxsmith
pushed a commit
that referenced
this pull request
Jul 29, 2026
…he title Em dashes read as an AI tell, so all 49 are gone from the post, title included. Each was replaced in context rather than swapped for a hyphen: colons where a definition followed, commas for parenthetical asides, parentheses where the aside was long, and full stops where the clause stood on its own. No em dashes remain in the rendered page's visible text. The title loses its dash and becomes "Bulma Is the #3 CSS Framework. AI Will Never Tell You About It", and the post is renamed so the published URL matches it: /blog/2026/07/28/bulma-3rd-most-used-css-framework /blog/2026/07/28/bulma-3-css-framework-ai-will-never-tell-you-about-it canonical_url is updated to match. Safe to move because the post has never been published (published: false, publish_to_devto: false) and the branch is unmerged, so no live URL breaks. Verified on a production build: page serves at the new path, h1 and document title carry the new wording, 37 live blocks, 0 react-live errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fghz3LG6ePvKig6JTjazLa
8 of 20 tasks
allxsmith
added a commit
that referenced
this pull request
Jul 29, 2026
… framework blog post (#401) Font Awesome icons rendered as blank boxes across the production docs site. Docusaurus minifies with @docusaurus/cssnano-preset, which enables discardUnused; that pass drops any @font-face whose family it cannot see in a font-family declaration. Font Awesome v7 resolves its family through var(), so all ten of its @font-face rules were stripped while the .fa-* class rules survived, leaving glyphs requested from a family the document never defined. Adds docs/plugins/preserve-font-face.js, which re-uses the preset path Docusaurus already configured and passes it discardUnused: { fontFace: false }. No new dependency; only font faces are exempted. Also adds the blog post "Bulma Is the #3 CSS Framework. AI Will Never Tell You About It" with 37 live examples, covering third place as a deliberate choice, scaffolding with create-bestax, why the examples are bestax components rather than raw Bulma, supporting open source, GitHub stars on legitimate projects, and AI-driven monoculture. The post ships drafted (published: false, publish_to_devto: false) so it does not syndicate on merge. Fixes #400
This was referenced Jul 29, 2026
7 of 16 tasks
bestax-release-bot Bot
pushed a commit
that referenced
this pull request
Aug 1, 2026
# [4.0.0](https://github.com/allxsmith/bestax/compare/create-bestax@3.8.0...create-bestax@4.0.0) (2026-08-01) ### Bug Fixes * **bestax-migrate:** give the kitchen-sink e2e a per-process scratch dir ([2211ea5](2211ea5)) * **bestax-migrate:** reject pnpm's workspace alias form instead of unwrapping it ([de6a900](de6a900)) * **bestax-migrate:** require the pack script to exist, not just be named ([5315efe](5315efe)) * **bestax-migrate:** resolve bare workspace: and guard the catalog: protocol ([7fda9db](7fda9db)), closes [#417](#417) [#412](#412) * **bestax-migrate:** resolve workspace: specifiers before publishing ([782829a](782829a)), closes [bestax-migrate#test](https://github.com/bestax-migrate/issues/test) [#412](#412) * **bestax-migrate:** stop the pack hooks excusing a catalog: devDependency ([4127ead](4127ead)), closes [#412-shaped](#412) * **docs:** stop cssnano stripping Font Awesome [@font-face](https://github.com/font-face), add [#3](#3) CSS framework blog post ([#401](#401)) ([5d114e1](5d114e1)), closes [#400](#400) ### chore * **deps:** consolidate the dependabot backlog, require Node 22 in both CLIs ([#447](#447)) ([e68148c](e68148c)), closes [#427](#427) [#428](#428) [#431](#431) [#432](#432) [#440](#440) [#393](#393) ### Features * **bestax-migrate:** require Node 22 and take chalk 6 ([#449](#449)) ([4c0e1e2](4c0e1e2)), closes [#447](#447) * **create-bestax:** require Node 22 and take chalk 6 ([#448](#448)) ([90fced2](90fced2)), closes [#447](#447) ### BREAKING CHANGES * **bestax-migrate:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. This applies to the runtime the codemod executes on, not to the app being migrated. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **create-bestax:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **deps:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * feat(bestax-migrate): require Node 22 and take chalk 6 chalk 6 drops support for Node below 22. The API surface this package uses is unchanged, so no calling code changes. The version guard in src/index.ts moves ahead of every import and no longer depends on anything: import declarations are hoisted and evaluated before any statement in the module, and chalk 6 itself requires Node >= 22, so a static import would fail to load on exactly the runtimes the guard exists to catch. ./cli.js is now imported dynamically for the same reason. @babel/parser deliberately stays on 7.x. Babel 8 removes the `deprecatedImportAssert` plugin with no replacement, and this package parses the legacy `import x from 'y' assert { type: 'json' }` form on purpose — a codemod that migrates older codebases must not crash on the syntax those codebases still contain. There is a regression test for it ("parses the legacy import-assert syntax"), which Babel 8 fails outright. jscodeshift 17 bundles its own Babel 7 regardless, so staying on 7 also keeps a single parser in the tree rather than two. * **deps:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh
bestax-release-bot Bot
pushed a commit
that referenced
this pull request
Aug 1, 2026
# [2.0.0](https://github.com/allxsmith/bestax/compare/bestax-migrate@1.0.0...bestax-migrate@2.0.0) (2026-08-01) ### Bug Fixes * **bestax-migrate:** give the kitchen-sink e2e a per-process scratch dir ([2211ea5](2211ea5)) * **bestax-migrate:** reject pnpm's workspace alias form instead of unwrapping it ([de6a900](de6a900)) * **bestax-migrate:** require the pack script to exist, not just be named ([5315efe](5315efe)) * **bestax-migrate:** resolve bare workspace: and guard the catalog: protocol ([7fda9db](7fda9db)), closes [#417](#417) [#412](#412) * **bestax-migrate:** resolve workspace: specifiers before publishing ([782829a](782829a)), closes [bestax-migrate#test](https://github.com/bestax-migrate/issues/test) [#412](#412) * **bestax-migrate:** stop the pack hooks excusing a catalog: devDependency ([4127ead](4127ead)), closes [#412-shaped](#412) * **create-bestax:** concrete inline-style → helper-prop mapping for the never-inline rule ([#357](#357)) ([5f72a90](5f72a90)), closes [#350](#350) [#350](#350) * **docs:** stop cssnano stripping Font Awesome [@font-face](https://github.com/font-face), add [#3](#3) CSS framework blog post ([#401](#401)) ([5d114e1](5d114e1)), closes [#400](#400) ### chore * **deps:** consolidate the dependabot backlog, require Node 22 in both CLIs ([#447](#447)) ([e68148c](e68148c)), closes [#427](#427) [#428](#428) [#431](#431) [#432](#432) [#440](#440) [#393](#393) ### Features * **bestax-migrate:** require Node 22 and take chalk 6 ([#449](#449)) ([4c0e1e2](4c0e1e2)), closes [#447](#447) * **bulma-ui:** ship agent-discovery files in the npm tarball ([#345](#345)) ([4b58739](4b58739)), closes [#344](#344) [#344](#344) [#344](#344) * **create-bestax:** add controlled-Burger Navbar to the landing archetype ([#355](#355)) ([36d4d09](36d4d09)), closes [#348](#348) * **create-bestax:** agent-validated guidance for skills, scaffold CLAUDE.md, and catalog ([#365](#365)) ([6fd06ae](6fd06ae)), closes [#2](#2) * **create-bestax:** require Node 22 and take chalk 6 ([#448](#448)) ([90fced2](90fced2)), closes [#447](#447) * **create-bestax:** scaffold .claude/launch.json with the AI skills opt-in ([#343](#343)) ([189135a](189135a)) * **create-bestax:** set scaffolded index.html title to the project name ([#356](#356)) ([3bfbea3](3bfbea3)), closes [#349](#349) [#349](#349) ### BREAKING CHANGES * **bestax-migrate:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. This applies to the runtime the codemod executes on, not to the app being migrated. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **create-bestax:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **deps:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * feat(bestax-migrate): require Node 22 and take chalk 6 chalk 6 drops support for Node below 22. The API surface this package uses is unchanged, so no calling code changes. The version guard in src/index.ts moves ahead of every import and no longer depends on anything: import declarations are hoisted and evaluated before any statement in the module, and chalk 6 itself requires Node >= 22, so a static import would fail to load on exactly the runtimes the guard exists to catch. ./cli.js is now imported dynamically for the same reason. @babel/parser deliberately stays on 7.x. Babel 8 removes the `deprecatedImportAssert` plugin with no replacement, and this package parses the legacy `import x from 'y' assert { type: 'json' }` form on purpose — a codemod that migrates older codebases must not crash on the syntax those codebases still contain. There is a regression test for it ("parses the legacy import-assert syntax"), which Babel 8 fails outright. jscodeshift 17 bundles its own Babel 7 regardless, so staying on 7 also keeps a single parser in the tree rather than two. * **deps:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh
11 of 19 tasks
bestax-release-bot Bot
pushed a commit
that referenced
this pull request
Aug 7, 2026
## [5.8.1](https://github.com/allxsmith/bestax/compare/@allxsmith/bestax-bulma@5.8.0...@allxsmith/bestax-bulma@5.8.1) (2026-08-07) ### Bug Fixes * **bestax-migrate:** give the kitchen-sink e2e a per-process scratch dir ([2211ea5](2211ea5)) * **bestax-migrate:** reject pnpm's workspace alias form instead of unwrapping it ([de6a900](de6a900)) * **bestax-migrate:** require the pack script to exist, not just be named ([5315efe](5315efe)) * **bestax-migrate:** resolve bare workspace: and guard the catalog: protocol ([7fda9db](7fda9db)), closes [#417](#417) [#412](#412) * **bestax-migrate:** resolve workspace: specifiers before publishing ([782829a](782829a)), closes [bestax-migrate#test](https://github.com/bestax-migrate/issues/test) [#412](#412) * **bestax-migrate:** stop the pack hooks excusing a catalog: devDependency ([4127ead](4127ead)), closes [#412-shaped](#412) * **bulma-ui:** deprecate CSS-less color values, warn in dev, fix has-text fall-through ([fb111eb](fb111eb)) * **bulma-ui:** fail closed on missing process and scope color guidance to real props ([117c0c0](117c0c0)) * **create-bestax:** concrete inline-style → helper-prop mapping for the never-inline rule ([#357](#357)) ([5f72a90](5f72a90)), closes [#350](#350) [#350](#350) * **create-bestax:** validate at submit in the bestax-form signup example ([0b9518f](0b9518f)) * **create-bestax:** wire labeled controls in the skill showcase story ([af49a16](af49a16)) * **docs:** announce the hero copy, and stop remounting the icons ([98e2cb0](98e2cb0)), closes [#434](#434) * **docs:** correct the frozen-install translation and reject leaked fences ([1883de3](1883de3)) * **docs:** drop dead nomodule ionicons fallback ([82be3e4](82be3e4)) * **docs:** harden PackageManagerTabs and document how to author it ([5b0d3e6](5b0d3e6)), closes [#434](#434) * **docs:** harden the hero copy button and share the tab storage key ([9e16cd7](9e16cd7)) * **docs:** make the hero package-manager switcher a real radiogroup ([aa14ff2](aa14ff2)), closes [#434](#434) * **docs:** stop cssnano stripping Font Awesome [@font-face](https://github.com/font-face), add [#3](#3) CSS framework blog post ([#401](#401)) ([5d114e1](5d114e1)), closes [#400](#400) ### chore * **deps:** consolidate the dependabot backlog, require Node 22 in both CLIs ([#447](#447)) ([e68148c](e68148c)), closes [#427](#427) [#428](#428) [#431](#431) [#432](#432) [#440](#440) [#393](#393) ### Features * **bestax-migrate:** require Node 22 and take chalk 6 ([#449](#449)) ([4c0e1e2](4c0e1e2)), closes [#447](#447) * **create-bestax:** add controlled-Burger Navbar to the landing archetype ([#355](#355)) ([36d4d09](36d4d09)), closes [#348](#348) * **create-bestax:** agent-validated guidance for skills, scaffold CLAUDE.md, and catalog ([#365](#365)) ([6fd06ae](6fd06ae)), closes [#2](#2) * **create-bestax:** require Node 22 and take chalk 6 ([#448](#448)) ([90fced2](90fced2)), closes [#447](#447) * **create-bestax:** set scaffolded index.html title to the project name ([#356](#356)) ([3bfbea3](3bfbea3)), closes [#349](#349) [#349](#349) * **docs:** add package-manager switches to the homepage hero ([374caf8](374caf8)) * **docs:** add PackageManagerTabs and register it globally ([23c9989](23c9989)) * **docs:** show all posts in the blog sidebar ([d29e9c6](d29e9c6)) ### Performance Improvements * **docs:** defer live previews until they scroll into view ([d6bf87b](d6bf87b)) * **docs:** share one parsed stylesheet set across every live preview ([feb993a](feb993a)) ### BREAKING CHANGES * **bestax-migrate:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. This applies to the runtime the codemod executes on, not to the app being migrated. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **create-bestax:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **deps:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * feat(bestax-migrate): require Node 22 and take chalk 6 chalk 6 drops support for Node below 22. The API surface this package uses is unchanged, so no calling code changes. The version guard in src/index.ts moves ahead of every import and no longer depends on anything: import declarations are hoisted and evaluated before any statement in the module, and chalk 6 itself requires Node >= 22, so a static import would fail to load on exactly the runtimes the guard exists to catch. ./cli.js is now imported dynamically for the same reason. @babel/parser deliberately stays on 7.x. Babel 8 removes the `deprecatedImportAssert` plugin with no replacement, and this package parses the legacy `import x from 'y' assert { type: 'json' }` form on purpose — a codemod that migrates older codebases must not crash on the syntax those codebases still contain. There is a regression test for it ("parses the legacy import-assert syntax"), which Babel 8 fails outright. jscodeshift 17 bundles its own Babel 7 regardless, so staying on 7 also keeps a single parser in the tree rather than two. * **deps:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh
bestax-release-bot Bot
pushed a commit
that referenced
this pull request
Aug 12, 2026
# 1.0.0 (2026-08-12) * feat(bulma-ui)!: remove bestax-bulma-prefixed CSS variant ([94baa34](94baa34)) * feat(create-bestax)!: require Node.js 18+ and align with bestax-bulma v2 ([#118](#118)) ([b22f183](b22f183)) ### Bug Fixes * add comprehensive rules to prevent bulma-ui versioning on non-bulma-ui commits ([#122](#122)) ([525ccfa](525ccfa)), closes [#119](#119) * **bestax-mcp:** derive the near-miss guidance from the skill, and only when it helps ([1141cca](1141cca)) * **bestax-mcp:** do not split a helper-prop table cell on an escaped pipe ([bdac820](bdac820)) * **bestax-mcp:** lead get_helper_props with the inline-style prohibition ([ffc627a](ffc627a)) * **bestax-mcp:** make list_components point at the next step ([8ddb2fd](8ddb2fd)) * **bestax-mcp:** make tests and cached builds work from a clean checkout ([6e63820](6e63820)), closes [bestax-mcp#build](https://github.com/bestax-mcp/issues/build) * **bestax-mcp:** name list_components as the entry point, not search_bestax ([206380b](206380b)) * **bestax-mcp:** name the three near-miss components in the list_components footer ([1c7af67](1c7af67)) * **bestax-mcp:** route helper questions to the tool that answers them ([cd6ce12](cd6ce12)) * **bestax-mcp:** validate the one input that is not ours, and bound the rest ([3e1adc9](3e1adc9)) * **bestax-migrate:** give the kitchen-sink e2e a per-process scratch dir ([2211ea5](2211ea5)) * **bestax-migrate:** reject pnpm's workspace alias form instead of unwrapping it ([de6a900](de6a900)) * **bestax-migrate:** require the pack script to exist, not just be named ([5315efe](5315efe)) * **bestax-migrate:** resolve bare workspace: and guard the catalog: protocol ([7fda9db](7fda9db)), closes [#417](#417) [#412](#412) * **bestax-migrate:** resolve workspace: specifiers before publishing ([782829a](782829a)), closes [bestax-migrate#test](https://github.com/bestax-migrate/issues/test) [#412](#412) * **bestax-migrate:** stop the pack hooks excusing a catalog: devDependency ([4127ead](4127ead)), closes [#412-shaped](#412) * **bulma-ui:** a11y + case-insensitive Taginput matching from PR review ([d576829](d576829)) * **bulma-ui:** accept router props like `to` on Navbar.Item without casts ([#311](#311)) ([b78856b](b78856b)), closes [#306](#306) * **bulma-ui:** Add build step to publish in ci.yml ([e3707fc](e3707fc)) * **bulma-ui:** add fontawesome-free as explicit devDependency ([a4a5389](a4a5389)) * **bulma-ui:** add missing exports ([0d16633](0d16633)) * **bulma-ui:** Add Skeleton to exports ([e481599](e481599)) * **bulma-ui:** another attempt to fix semantic release builds with ci.yml ([cc3a3e2](cc3a3e2)) * **bulma-ui:** another attempt to fix semantic release builds with ci.yml ([314bc39](314bc39)) * **bulma-ui:** another attempt to fix semantic release builds with ci.yml ([c930693](c930693)) * **bulma-ui:** associate Autocomplete and Taginput labels with their inner inputs ([7ae37d4](7ae37d4)) * **bulma-ui:** associate Autocomplete and Taginput labels with their inner inputs ([384bd38](384bd38)) * **bulma-ui:** associate the form label prop with its control via a generated id ([e6686af](e6686af)) * **bulma-ui:** complete domain migration and fix semantic-release configuration ([#64](#64)) ([f4cd71d](f4cd71d)) * **bulma-ui:** correct blog post examples and add Modal compound components ([#81](#81)) ([559c2e3](559c2e3)) * **bulma-ui:** correct NPM_TOKEN env variable in ci.yml ([94b48b4](94b48b4)) * **bulma-ui:** cover horizontal-layout group label association ([ef3ca9f](ef3ca9f)) * **bulma-ui:** deprecate CSS-less color values, warn in dev, fix has-text fall-through ([fb111eb](fb111eb)) * **bulma-ui:** fail closed on missing process and scope color guidance to real props ([117c0c0](117c0c0)) * **bulma-ui:** Fix release.config.js to include package-lock.json ([390da59](390da59)) * **bulma-ui:** fix standalone Badge pointer-events, pulse halo, and falsy content ([#295](#295)) ([a9db031](a9db031)), closes [#264](#264) * **bulma-ui:** full classPrefix support across layout/grid + prefix utils ([4ce0b53](4ce0b53)) * **bulma-ui:** honor the htmlFor opt-out in the convenience hook and tighten the association docs ([92aa622](92aa622)) * **bulma-ui:** improve npm package discoverability with optimized keywords and badges ([#72](#72)) ([8c7a696](8c7a696)) * **bulma-ui:** Initial semantic release changes ([b78d785](b78d785)) * **bulma-ui:** keep Taginput's fallback name unless the label targets its input ([73cec33](73cec33)) * **bulma-ui:** keep Taginput's fallback name unless the label targets its input ([ca5996a](ca5996a)) * **bulma-ui:** migrate domain from bestax.cc to bestax.io ([#64](#64)) ([4870b1e](4870b1e)) * **bulma-ui:** migrate ionicons to v8 to unblock publish and Storybook ([927a55b](927a55b)), closes [#142](#142) * **bulma-ui:** name Rate, Checkboxes, and Radios groups from their labels via aria-labelledby ([dce0ee7](dce0ee7)) * **bulma-ui:** name Rate, Checkboxes, and Radios groups from their labels via aria-labelledby ([#497](#497)) ([5c4222e](5c4222e)) * **bulma-ui:** name the three near-miss components in AGENTS.md ([c63f491](c63f491)), closes [#344](#344) * **bulma-ui:** never let labelProps.htmlFor wire a group label to a control ([3b3aaaf](3b3aaaf)) * **bulma-ui:** publish rewritten README to npm ([9810081](9810081)) * **bulma-ui:** publish with npm provenance attestation ([172da62](172da62)), closes [#180](#180) * **bulma-ui:** reference llms docs from README and package.json ([#198](#198)) ([db8aab3](db8aab3)) * **bulma-ui:** reject predicate-blocked values during manual entry ([a8f6e28](a8f6e28)) * **bulma-ui:** resolve flex item properties and Card compound component issues ([#55](#55)) ([e774da3](e774da3)) * **bulma-ui:** resolve flex item properties and Card compound component issues ([#55](#55)) ([7641a53](7641a53)) * **bulma-ui:** resolve react-hooks v7 and [@eslint-react](https://github.com/eslint-react) findings ([14caaaf](14caaaf)) * **bulma-ui:** resolve security vulnerabilities and update dependencies ([#128](#128)) ([112f6e4](112f6e4)), closes [#127](#127) * **bulma-ui:** restrict semantic-release to bulma-ui scoped commits only ([2d67bf9](2d67bf9)), closes [#62](#62) * **bulma-ui:** retry failed Avatar src, flatten Fragment children in Avatars, RTL-safe overlap ([#297](#297)) ([c00b9db](c00b9db)) * **bulma-ui:** route every hardcoded class through the prefix helpers; add classPrefix sweep test ([#301](#301)) ([a50b134](a50b134)), closes [#286](#286) * **bulma-ui:** setup gpg signing with semantic-release ([3e24722](3e24722)) * **bulma-ui:** strip redundant library prefix from Icon name ([#242](#242)) ([dbe3622](dbe3622)), closes [#189](#189) * **bulma-ui:** trigger release to publish via OIDC trusted publishing ([e2d09c5](e2d09c5)) * **bulma-ui:** update bundle size claims to accurate 21KB gzipped ([#66](#66)) ([6e381bd](6e381bd)) * **bulma-ui:** update package-lock.json ([853d585](853d585)) * **bulma-ui:** update package.json for better seo, exports, types, engines, funding, etc ([98cbc56](98cbc56)) * **bulma-ui:** use createRequire for ESM compatibility in Storybook 10 ([#130](#130)) ([b27e60e](b27e60e)), closes [#129](#129) * **ci:** collect screenshots as artifacts and commit in single batch to avoid conflicts ([27b259d](27b259d)) * **ci:** ensure npm install uses fresh downloads with --prefer-online ([1f2e15d](1f2e15d)) * **ci:** properly extract base path for recursive file search ([e0330ff](e0330ff)) * **ci:** use find command instead of glob module in verified-commit action ([0e2d159](0e2d159)) * **ci:** use npm ci for scaffolded app dependencies ([35652c8](35652c8)) * **create-bestax:** concrete inline-style → helper-prop mapping for the never-inline rule ([#357](#357)) ([5f72a90](5f72a90)), closes [#350](#350) [#350](#350) * **create-bestax:** correct browser title to prioritize Bestax branding ([#106](#106)) ([23aa535](23aa535)), closes [#105](#105) * **create-bestax:** correct template path resolution from ../../ to ../ ([65b4493](65b4493)), closes [#78](#78) * **create-bestax:** dark-mode contrast rules in theming/layout skills and docs ([#303](#303)) ([490bf21](490bf21)), closes [#194](#194) [#195](#195) * **create-bestax:** exclude templates directory from linting and typecheck ([18fec0b](18fec0b)) * **create-bestax:** fail fast with guidance instead of hanging when stdin is not a TTY ([#293](#293)) ([46a172d](46a172d)), closes [#192](#192) * **create-bestax:** move templates into package directory and update docs ([195bf01](195bf01)), closes [#78](#78) * **create-bestax:** point scaffolded CLAUDE.md at llms docs; document skills ([#198](#198)) ([b2e0514](b2e0514)) * **create-bestax:** publish with npm provenance attestation ([21ffe8f](21ffe8f)), closes [#180](#180) * **create-bestax:** put the near-miss guidance where every session sees it ([6db49f3](6db49f3)) * **create-bestax:** read version from package.json instead of hardcoded value ([#109](#109)) ([8605699](8605699)) * **create-bestax:** refresh README and bump scaffolded bestax-bulma to ^5 ([4e19e86](4e19e86)) * **create-bestax:** reject dot-only project names, pin icon versions, bundle bestax-icons skill ([#310](#310)) ([ddff8e5](ddff8e5)) * **create-bestax:** scaffold @allxsmith/bestax-bulma ^4.0.0 ([1d3b802](1d3b802)) * **create-bestax:** scaffold bundled bestax CSS flavors, not stock Bulma ([43621dc](43621dc)) * **create-bestax:** ship improved bundled skills + component catalog ([#199](#199)) ([a1515c2](a1515c2)) * **create-bestax:** shrink the near-miss block and pin the copies together ([d582da5](d582da5)) * **create-bestax:** skills-sync conformance gate + theming skill reference backfill ([#326](#326)) ([9584133](9584133)), closes [#285](#285) * **create-bestax:** stop the skills teaching a Theme call that does not compile ([2935bb2](2935bb2)) * **create-bestax:** synchronize version with bestax-bulma to 2.4.0 ([623ee79](623ee79)), closes [#96](#96) * **create-bestax:** teach the skills the three components Bulma hides ([22dcff7](22dcff7)) * **create-bestax:** update template dependency to ^2.4.0 ([200971d](200971d)) * **create-bestax:** use scenario-specific screenshot directories to prevent overwrites ([#108](#108)) ([c675957](c675957)), closes [#107](#107) * **create-bestax:** validate at submit in the bestax-form signup example ([0b9518f](0b9518f)) * **create-bestax:** wire labeled controls in the skill showcase story ([af49a16](af49a16)) * **docs:** announce the hero copy, and stop remounting the icons ([98e2cb0](98e2cb0)), closes [#434](#434) * **docs:** correct Content Signals syntax in robots.txt ([#134](#134)) ([85dd9de](85dd9de)) * **docs:** correct the frozen-install translation and reject leaked fences ([1883de3](1883de3)) * **docs:** drop dead nomodule ionicons fallback ([82be3e4](82be3e4)) * **docs:** emit per-page markdown so llms.txt links resolve ([#200](#200)) ([7877083](7877083)) * **docs:** escape apostrophe in QuickStart notification text ([25d6d72](25d6d72)) * **docs:** generate llms.txt so the advertised homepage link resolves ([9fae464](9fae464)), closes [#177](#177) * **docs:** give every batch run its own port — slot reuse was corrupting runs ([6ef1755](6ef1755)) * **docs:** harden PackageManagerTabs and document how to author it ([5b0d3e6](5b0d3e6)), closes [#434](#434) * **docs:** harden the hero copy button and share the tab storage key ([9e16cd7](9e16cd7)) * **docs:** improve homepage hero layout and button spacing ([5f7a5a7](5f7a5a7)) * **docs:** make the eval batch resumable after a container restart ([d56229e](d56229e)) * **docs:** make the hero package-manager switcher a real radiogroup ([aa14ff2](aa14ff2)), closes [#434](#434) * **docs:** move robots.txt to correct deployment location ([#90](#90)) ([1e2aeee](1e2aeee)) * **docs:** rebrand and reorganize Storybook ([#83](#83)) ([dfb9937](dfb9937)) * **docs:** remove Google Analytics and add robots.txt ([94776f7](94776f7)) * **docs:** stop cssnano stripping Font Awesome [@font-face](https://github.com/font-face), add [#3](#3) CSS framework blog post ([#401](#401)) ([5d114e1](5d114e1)), closes [#400](#400) * **docs:** update Storybook logo path to /img/logo.svg for deployed site ([bf59758](bf59758)) * **e2e:** correct notification CSS selectors to use contains instead of ends-with ([182acc1](182acc1)) * implement independent package versioning strategy ([#111](#111)) ([7819c73](7819c73)), closes [#110](#110) * prevent bulma-ui from versioning on create-bestax commits ([#120](#120)) ([4dfaf9c](4dfaf9c)), closes [#119](#119) * resolve React Hooks violations and ESLint configuration issues ([32d2931](32d2931)) * upgrade Turbo, Storybook, and Docusaurus dependencies ([5b4ebdd](5b4ebdd)), closes [#98](#98) ### chore * **deps:** consolidate the dependabot backlog, require Node 22 in both CLIs ([#447](#447)) ([e68148c](e68148c)), closes [#427](#427) [#428](#428) [#431](#431) [#432](#432) [#440](#440) [#393](#393) ### Documentation * fix stale versioning and coverage docs; drop CLAUDE.md stale-docs flags ([71c4583](71c4583)) ### Features * add theme system and config provider with comprehensive test coverage ([f3ca7f0](f3ca7f0)) * **bestax-mcp:** serve component docs, props, examples and skills over MCP ([c2abcc4](c2abcc4)) * **bestax-migrate:** react-bulma-components → bestax-bulma codemod CLI, skill, and docs ([#333](#333)) ([e04a12b](e04a12b)), closes [#1e6b99](https://github.com/allxsmith/bestax/issues/1e6b99) * **bestax-migrate:** require Node 22 and take chalk 6 ([#449](#449)) ([4c0e1e2](4c0e1e2)), closes [#447](#447) * **bulma-ui:** add Avatar, Avatars, and Badge components ([#257](#257)) ([0817018](0817018)), closes [#256](#256) * **bulma-ui:** add colorMode dark-mode prop to Theme ([4acc41e](4acc41e)), closes [#174](#174) * **bulma-ui:** add consistent gap prop to Columns, aliasing gapSize ([#300](#300)) ([6c36455](6c36455)), closes [#282](#282) * **bulma-ui:** add cursor helper, closeDelay prop, and polish Tooltip stories ([37945b5](37945b5)) * **bulma-ui:** add extra components, form elements, and SCSS styles ([59daf28](59daf28)) * **bulma-ui:** add HTML element wrapper components ([#135](#135)) ([#136](#136)) ([20fb16d](20fb16d)) * **bulma-ui:** add manual-entry stories for format, bounds, and blocked-value variations ([e93d51c](e93d51c)) * **bulma-ui:** add Reveal component for scroll-triggered animations ([#255](#255)) ([a89c574](a89c574)) * **bulma-ui:** Add skeletons ([6c46e4b](6c46e4b)) * **bulma-ui:** add themed Checkbox/Radio, convenience Field components, and Autocomplete cleanup ([3c57a5a](3c57a5a)) * **bulma-ui:** add typing-first story variants for all picker property variations ([078433f](078433f)) * **bulma-ui:** associate Field's label with a composed base control ([219f631](219f631)) * **bulma-ui:** avatar/badge a11y batch — decorative alt, accessible names, live region, button type, surplus i18n, focus ring ([#298](#298)) ([508477f](508477f)), closes [#266](#266) [#266](#266) * **bulma-ui:** change the default primary color to [#1](#1 ([8872620](8872620)), closes [#1e6b99](https://github.com/allxsmith/bestax/issues/1e6b99) [#1e6b99](https://github.com/allxsmith/bestax/issues/1e6b99) * **bulma-ui:** compound (dot-notation) sub-components for all parent/child families via shared withSubComponents helper ([#331](#331)) ([07516c5](07516c5)) * **bulma-ui:** dim and blur the calendar behind the Datetimepicker time wheels ([3d90619](3d90619)) * **bulma-ui:** finalize the 3.0 component set ([87ccc0e](87ccc0e)) * **bulma-ui:** make Button and Link as prop polymorphic (React.ElementType) ([#238](#238)) ([ce90304](ce90304)), closes [#188](#188) * **bulma-ui:** require React 18 as the minimum supported version ([c7251b0](c7251b0)) * **bulma-ui:** ship agent-discovery files in the npm tarball ([#345](#345)) ([4b58739](4b58739)), closes [#344](#344) [#344](#344) [#344](#344) * **ci:** add verified-commit action for GPG-signed commits ([d078dfa](d078dfa)) * **create-bestax:** add bestax-optimize skill for shrinking built CSS ([#329](#329)) ([f597b9f](f597b9f)) * **create-bestax:** add CLI tool with Vite templates and automated publishing ([9748c3d](9748c3d)) * **create-bestax:** add controlled-Burger Navbar to the landing archetype ([#355](#355)) ([36d4d09](36d4d09)), closes [#348](#348) * **create-bestax:** add cross-platform emoji support with figures ([#103](#103)) ([15567d9](15567d9)) * **create-bestax:** add README with templates location note ([8ddc73d](8ddc73d)) * **create-bestax:** add visual regression testing and synchronized versioning ([17e1e22](17e1e22)), closes [#94](#94) * **create-bestax:** agent-validated guidance for skills, scaffold CLAUDE.md, and catalog ([#365](#365)) ([6fd06ae](6fd06ae)), closes [#2](#2) * **create-bestax:** bestax-icons skill — teach agents the icon system ([#302](#302)) ([61c8ef2](61c8ef2)), closes [#287](#287) * **create-bestax:** improve favicon visibility and add distinct branding ([621590d](621590d)), closes [#100](#100) * **create-bestax:** modernize templates (Vite 8, ESLint 10, TS 6) + add working lint config ([4537629](4537629)), closes [#167](#167) * **create-bestax:** offer to install the bestax AI skills when scaffolding ([625b7bf](625b7bf)), closes [#174](#174) * **create-bestax:** require Node 22 and take chalk 6 ([#448](#448)) ([90fced2](90fced2)), closes [#447](#447) * **create-bestax:** scaffold .claude/launch.json with the AI skills opt-in ([#343](#343)) ([189135a](189135a)) * **create-bestax:** scaffold-aware CLAUDE.md with setup facts and house style ([#271](#271)) ([c1681b0](c1681b0)) * **create-bestax:** set scaffolded index.html title to the project name ([#356](#356)) ([3bfbea3](3bfbea3)), closes [#349](#349) [#349](#349) * **docs:** add Google Analytics tracking for usage insights ([#68](#68)) ([90ab951](90ab951)) * **docs:** add package-manager switches to the homepage hero ([374caf8](374caf8)) * **docs:** add PackageManagerTabs and register it globally ([23c9989](23c9989)) * **docs:** add pronunciation guide and dark mode support ([#58](#58)) ([48a8916](48a8916)) * **docs:** aggregate-runs.mjs — distribution stats across a runs directory ([5de9c07](5de9c07)) * **docs:** batch runner for the eval harness, with the concurrency fixes it needed ([f8e268c](f8e268c)) * **docs:** migrate from GitHub Pages to Cloudflare Pages ([#132](#132)) ([2154672](2154672)), closes [#131](#131) * **docs:** rubric v2 and a brief that demands the components beyond Bulma ([e6047be](e6047be)) * **docs:** show all posts in the blog sidebar ([d29e9c6](d29e9c6)) * **form:** add Datepicker, Timepicker, and Datetimepicker components ([c6684e6](c6684e6)) ### Performance Improvements * **bestax-mcp:** stop get_helper_props costing half the session ([b865651](b865651)) * **docs:** defer live previews until they scroll into view ([d6bf87b](d6bf87b)) * **docs:** share one parsed stylesheet set across every live preview ([feb993a](feb993a)) ### BREAKING CHANGES * **bestax-migrate:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. This applies to the runtime the codemod executes on, not to the app being migrated. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **create-bestax:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * **deps:** create-bestax now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * feat(bestax-migrate): require Node 22 and take chalk 6 chalk 6 drops support for Node below 22. The API surface this package uses is unchanged, so no calling code changes. The version guard in src/index.ts moves ahead of every import and no longer depends on anything: import declarations are hoisted and evaluated before any statement in the module, and chalk 6 itself requires Node >= 22, so a static import would fail to load on exactly the runtimes the guard exists to catch. ./cli.js is now imported dynamically for the same reason. @babel/parser deliberately stays on 7.x. Babel 8 removes the `deprecatedImportAssert` plugin with no replacement, and this package parses the legacy `import x from 'y' assert { type: 'json' }` form on purpose — a codemod that migrates older codebases must not crash on the syntax those codebases still contain. There is a regression test for it ("parses the legacy import-assert syntax"), which Babel 8 fails outright. jscodeshift 17 bundles its own Babel 7 regardless, so staying on 7 also keeps a single parser in the tree rather than two. * **deps:** bestax-migrate now requires Node.js 22 or newer. Node 18 and 20 are both past end-of-life. Running it on an older runtime prints an explicit upgrade message and exits 1. Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh * footer requirement, and the commitlint scope rule - CONTRIBUTING.md: replace the type-less commit example with a commitlint-valid conventional format (verified against commitlint); correct all four coverage mentions to the real jest thresholds (bulma-ui 99%, create-bestax 95%/78% branches); fix the npm package name (@allxsmith/bestax-bulma, plus create-bestax) and link VERSIONING.md - CLAUDE.md: remove the stale-docs warning and asides now that the underlying docs are correct; point at VERSIONING.md again Closes #206. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0131uD6QKmAij7Byk3SByyLh * the @allxsmith/bestax-bulma/versions/bestax-bulma-prefixed.css export is removed. Use versions/bestax-prefixed.css with classPrefix="bestax-". * **bulma-ui:** React 16 and 17 are no longer supported; the minimum supported React version is now 18. * **bulma-ui:** Snackbar has been removed and merged into Toast; use Toast with its positioning and queue props instead. * **bulma-ui:** form controls now auto-wrap in Field/Control, and Checkbox and Radio ship new themed visuals. See the 2.x -> 3.x migration guide. * This version requires Node.js 18.0.0 or higher. The CLI now enforces this requirement and will exit with an error message if running on older Node.js versions. This aligns create-bestax with the bestax-bulma v2.x ecosystem. * fix(create-bestax): correct Prettier formatting in index.ts * None - all changes are additive and backward compatible
Merged
11 of 20 tasks
8 tasks done
9 of 15 tasks
allxsmith
added a commit
that referenced
this pull request
Aug 29, 2026
Deep review finding #1 on #594, and it was right. sbom-action generates and uploads in one step, so the artifact existed before anything had inspected it — and `sign-sbom`/`attach-sbom` deliberately do not require `consumer-sbom` to have succeeded. On a real release that meant a document the guard had already rejected still got signed and permanently attached to the release. The run went red and the bad SBOM shipped anyway, which is most of the value of the guard gone. `upload-artifact: false` on both generators plus one explicit upload after the assertion closes it: nothing is published unless both documents check out. This preserves the degradation #529 designed for rather than trading it away. A leg that fails for any reason now produces no artifact at all, the globs in the downstream jobs expand to nothing, and `attach-sbom` emits its ::warning:: while the repository SBOMs still ship. The alternative — gating those jobs on `needs.consumer-sbom.result` — would have been worse: the matrix is fail-fast: false precisely so one bad leg cannot take the other three with it, and a job-level gate would have done exactly that. Also makes the non-registry message say what to do when the entry is a legitimate git/tarball/alias runtime dependency rather than a leak (finding #3), so the next person to hit it decides about the dependency instead of reflexively widening the check.
allxsmith
added a commit
that referenced
this pull request
Aug 29, 2026
* ci: pin, guard and de-duplicate the consumer-closure SBOM Four of the five follow-ups #529 deferred (#530). Item 3 is deliberately partial; see below. 1. Pin the released package to its release tag. `release.tag_name` is `<pkg>@X.Y.Z`, so the one package a release names is now installed at that exact version instead of resolving `latest`. That closes two real failures: the event fires immediately after `npm publish`, so a CDN-cached packument could stamp release 5.12.0 with an SBOM describing 5.11.1; and a re-run days later resolved a newer `latest` and produced a filename `--clobber` cannot replace, leaving the release carrying two contradictory documents for the same package. The other three legs stay on `latest` — a release says nothing about them — which is why the decision is a script matching the tag to its matrix leg rather than an expression. The pin also buys a free assertion: `--expect` fails the job if the tree does not carry the version that was asked for. Pinning is retried, because it trades "resolves the wrong version" for "may not resolve at all" while the packument propagates. Without that, the released leg — the one the release cares about — would be the one most likely to ship no SBOM. 2. Assert the document is still a consumer closure. Nothing checked, so a syft, cataloger or exporter change shipped silently green. Deliberately not a count: 48c57d5 reverted exactly that, because live registry closures drift on somebody else's release and a false-red generator is the cry-wolf failure #391 and #525 already fixed. Instead every catalogued package must resolve to registry.npmjs.org, plus a floor. That catches inflation by its cause rather than its size, and replays both #529 regressions in the test sibling. 3. Move the install/stamp shell into scripts/ with node --test siblings (rule 9). The semver validation guards a value out of a published tarball that flows into $GITHUB_OUTPUT and syft's config heredoc, and there was no way to test it where it lived. This job now needs a checkout; it takes the event ref rather than `main`, because it asserts a property of a document it just generated rather than a third party's claim, and because `main` would make the only available verification — a dispatch on a branch — impossible. NOT done: extracting `attach-sbom`'s upload branching. That job has no checkout and runs no repository code, which is the whole reason it is safe to give it `contents: write`. Adding one to gain a unit test is a worse trade than the test is worth. #530 stays open for it. 4. Build the artifact name once. Four occurrences collapse to one step output. `sign-sbom` and `attach-sbom` glob these names from their own jobs and cannot read a step output across a job boundary, so this is four to one, not eight — what keeps those globs honest is that the prefix is now one exported constant pinned by a test. 5. Scan the lockfile rather than the tree. syft walked every file under node_modules — thousands per leg, twice — to satisfy a cataloger that only reads the root lockfile. The node_modules exclusion stays and is now structurally unnecessary rather than merely correct. None of this is exercised by a PR: the job runs on release, schedule and workflow_dispatch only. Verified by dispatch and by reading the generated documents, which is how every defect in #529 was found. * ci: pass the lockfile as sbom-action's file input, not its path input sbom-action prefixes `path` with syft's `dir:` scheme, so the lockfile pointed at it failed with `not a directory source` on every matrix leg. `file` is the input that takes one. Found by dispatch (33260609524), which is the only way this job is exercised at all. * ci: re-measure the consumer closure sizes from a real run bestax-mcp read 94 against 95 entries, which two structural entries can never produce. The measured closure is 93. Numbers now cite the run they came from and say plainly that they drift, so the next reader re-measures instead of trusting them. * ci: read both SBOM formats, and stop the scan source leaking a runner path Addresses three review findings on #594, one of which turned out to be a live defect this branch introduced. The scan source is a lockfile-only DIRECTORY, not the lockfile as a file source. Passing it as sbom-action's `file` input worked and did remove the node_modules walk, but a file source makes syft emit the scanned file as a component: every CycloneDX document came out carrying `/home/runner/work/_temp/consumer/package-lock.json`. That is the runner-path leak #529 fixed for SPDX, reintroduced in the other format — and it was signed and attached. Copying the lockfile into an otherwise empty directory keeps the document shape #529 measured while still removing the walk. The guard now reads BOTH documents. An SPDX-only guard is what passed the leak above: the two come from separate sbom-action invocations, every .spdx.json was clean, and nothing looked at the .cdx.json that was not. CycloneDX states the same claims in its own vocabulary, so `pkg:npm/` purls stand in for registry.npmjs.org download locations and metadata.component carries the subject. The guard also asserts the target package is in its own closure at the stamped version. Without it, a wrong install spec or a cataloger dropping the direct dependency produces a well-formed closure OF SOMETHING ELSE and passes every other assertion. Missing required flags now exit 2 rather than 1. The header promised the codes stay distinct so a mistyped invocation is not reported as a supply-chain failure, and the checks were on the assertion path; parseArgs owns them now. The test asserting the old behaviour asserted it against a comment that said the opposite. * ci: stop syft cataloging the scan file, and correct the leak's provenance The runner-path leak in the CycloneDX documents is NOT something this branch introduced, and the previous commit message said it was. Run 32706731377 — a scheduled run on main from before this branch existed — carries `/home/runner/work/_temp/consumer/package-lock.json` in all four .cdx.json files. It has been shipping in every release since #529, which fixed the same leak for SPDX and never looked at the other format. Switching to a file source moved which path was leaked; it did not create the leak. The cause is that file metadata is cataloged independently of `default-catalogers`, so restricting that to the lock cataloger never touched it. `file.metadata.selection: none` is what turns it off. The exporters disagreed about naming, which is why only one format showed it: SPDX writes a relative `package-lock.json` into its `files` array, CycloneDX writes a `type: file` component with the absolute path. Also drops backticks from the syft heredoc. It is unquoted — it interpolates $PACKAGE and $VERSION — so the existing "`files:`" in a comment was being run as a command and substituted away, leaving "publishing without field" in the generated config. Inert inside a YAML comment; not inert in general. * ci: make the closure guard a gate rather than a detector Deep review finding #1 on #594, and it was right. sbom-action generates and uploads in one step, so the artifact existed before anything had inspected it — and `sign-sbom`/`attach-sbom` deliberately do not require `consumer-sbom` to have succeeded. On a real release that meant a document the guard had already rejected still got signed and permanently attached to the release. The run went red and the bad SBOM shipped anyway, which is most of the value of the guard gone. `upload-artifact: false` on both generators plus one explicit upload after the assertion closes it: nothing is published unless both documents check out. This preserves the degradation #529 designed for rather than trading it away. A leg that fails for any reason now produces no artifact at all, the globs in the downstream jobs expand to nothing, and `attach-sbom` emits its ::warning:: while the repository SBOMs still ship. The alternative — gating those jobs on `needs.consumer-sbom.result` — would have been worse: the matrix is fail-fast: false precisely so one bad leg cannot take the other three with it, and a job-level gate would have done exactly that. Also makes the non-registry message say what to do when the entry is a legitimate git/tarball/alias runtime dependency rather than a leak (finding #3), so the next person to hit it decides about the dependency instead of reflexively widening the check. * ci: reject an absolute path in SPDX's files array too The origin loop reads packages and components; SPDX keeps file entries in a separate `files` array that it never saw. syft writes those relative today — a bare `package-lock.json`, which leaks nothing and still passes — but that array is exactly where the absolute path would land if the file config changed, and an absolute path there is the SPDX shape of the leak that shipped in every .cdx.json from #529 until this branch. Checked rather than trusted to stay relative. * ci: anchor the semver check at both ends Copilot was right: the shape test was `/^\d+\.\d+\.\d+/`, a PREFIX match, so `1.2.3garbage`, `1.2.3.4` and `01.2.3` all passed a function whose error message says "is not a semver version". A validator that accepts what it claims to reject is the failure .github/CLAUDE.md's checklist ends on. Replaced with semver.org's anchored grammar, which still admits prerelease and build metadata — a regex that reds a real release would be worse than the loose one it replaces. The two checks are now ordered character-test-first, and that order is load-bearing rather than incidental. The anchored grammar rejects everything the character test rejects, so running it first would leave the character test unreachable — dead code wearing the label of the control that matters most here. Running the character test first keeps it live and makes each error name the real problem: a value carrying a newline is reported as an injection attempt rather than as a formatting quibble. Tests assert which check catches what, rather than only that something threw. Rejected-for-the-wrong-reason is how the prefix bug survived the first review. * ci: validate the SPDX subject too, and stop overstating the syft run Two more Copilot findings, both right. The SPDX subject was exempted by name and never validated, while the CycloneDX one was checked. So an SPDX document could name the wrong version — or omit the claim entirely — while every dependency entry in it was correct, and the guard would pass it. The subject is the document's identity, not a structural detail to skip past, so both formats are now checked the same way and the tests assert the symmetry rather than one side of it. The normalize() comment claimed both documents come from "the same syft run". They do not: they are two separate sbom-action invocations sharing a scan directory and a config. That distinction is the entire reason each document is inspected independently — the leak that shipped from #529 until this branch was in every CycloneDX document and in none of the SPDX ones, which cannot happen if they are two renderings of one result. A comment that overstates its mechanism is worse than no comment, because the next reader stops checking. * ci: cross-check the two documents, and stop calling a purl provenance Copilot again, and the finding lands on something I wrote rather than inherited: the guard claimed CycloneDX asserts registry provenance "with a pkg:npm/ purl". It does not. syft's lock cataloger builds pkg:npm/name@version from the name and version alone and records `resolved` nowhere in a CycloneDX document — not in externalReferences, not in properties. Verified against a real component from run 33262407242. So that test is an ECOSYSTEM test: it catches a github-actions entry or a bare file component, which is the leak class it was added for, but a git, tarball, private-registry or aliased dependency passes it untouched. Overstating it was the more serious half. A comment that claims a control it does not have is worse than no comment, because the next reader stops checking — and this is the second time on this branch I have written one. The gap itself is closed by a fourth assertion: the two documents must list the same name@version set. Nothing made them agree by construction — they are separate syft runs, which is exactly how the #529 leak lived in every CycloneDX document and no SPDX one — so this is a real check, and it carries the registry claim across: anything the weak purl test would admit has to appear in SPDX too, where downloadLocation and the strong test are waiting. That means both documents in one invocation. Two separate ones could each pass while disagreeing with each other, which is the whole case being closed. * ci: enforce that each file's contents match the flag it was passed as The --spdx and --cdx flags were a claim about each file; normalize() detects the format from the document's own shape. Nothing compared the two, which made the flags decoration: hand the checker the CycloneDX file twice and every per-document assertion passes, the same-closure assertion trivially agrees with itself, and the release ships an asset named `.spdx.json` that is not SPDX. A `format:` typo on either sbom-action step produces precisely that, and it is the kind of defect the rest of this guard exists to catch. Detected format is now compared against the flag before any other assertion runs. Tests cover the same file passed twice, in both formats, and the swapped pair — each individually well-formed, all three rejected. * ci: compare the two closures as multisets, and warn on duplicates Deep review advisory #1: crossCheck used Array.includes, which asks only whether an identity appears at all. A package listed twice in one document and once in the other therefore looked identical to both listing it once, so asymmetric duplicate inflation passed silently. Counting is the same work and answers the question the function claims to answer. Duplicates within a single document are a WARNING rather than a failure, and the split is deliberate. The doubled-catalogers regression (#529) reds today only because the second copy carried no registry origin; a future duplication that kept a valid origin in both documents would pass everything here, since growth is explicitly allowed. But npm can legitimately place the same name@version at two paths when it cannot hoist, and failing on that would red somebody else's release for a tree shape nobody chose — the false-red generator 48c57d5 reverted and #391/#525 are about. A warning names the count and the offenders, which is enough for a human reading a dispatch to recognise "every package is listed twice" at a glance, and cannot cost a release. Measured before choosing: zero duplicates across all 208 catalogued entries in the four closures of run 33263381732. * ci: detect formats by their own markers, and stop a multi-version false red Round 3 of review, four findings, all real. The target check took the FIRST entry matching the package name. npm can carry more than one version of a package in a closure and nothing orders the document by depth, so a nested older copy emitted ahead of the direct one reported a mismatch while the stamped version sat further down the list. That is a false red on a good release — the failure this whole guard is written to avoid. Any matching entry now satisfies it, and the message lists every version actually found rather than just the first. normalize() keyed on array shape alone, so any object with a `packages` array read as SPDX. An exporter result that had lost `spdxVersion` is no longer a valid SPDX document and is not what the asset's name promises, yet it satisfied the --spdx/--cdx role check and would have shipped. Detection now requires each format's own top-level marker as well as its array, and the fixtures carry the markers a real document has. The SPDX files-array check rejected only ABSOLUTE names. `work/_temp/scan/…` discloses the same layout as `/home/runner/work/_temp/scan/…`, so it now rejects any name carrying a path separator; the scan directory holds one file, so the only legitimate entry is a bare `package-lock.json` (`./` prefix tolerated). Recorded what this guard does NOT defend, per the deep review: the registry-origin assertion is the second line against inflation, not the first. A genuine npm package resolving from registry.npmjs.org and present in both documents passes every assertion here. What prevents that class is the scan shape — a directory holding only the root lockfile — so weakening the scan back to the installed tree on the grounds that the guard will catch it would be wrong. It would not. * ci: stop the rejection message re-introducing what it rejected Copilot, round 4, and it is the sharpest finding on this PR. assertVersion blocks a version carrying a newline from reaching $GITHUB_OUTPUT — and then interpolated that same value verbatim into its own `::error::` complaint. A GitHub Actions workflow command is terminated by a newline, so the message ::error::version contains unexpected characters: "1.0.0 ::error::FORGED COMMAND" emits a second, attacker-authored workflow command. The guard was performing the attack in the course of reporting it. Reproduced before fixing. Both scripts now render untrusted values through a `forLog` helper — JSON.stringify, which escapes newlines, carriage returns, quotes and control characters so the value can only ever be one inert line. Applied to every value that came out of a published tarball, a generated document, or a release tag: version strings, JSON parser messages, SBOM entry names and versions, download locations, purls, file names, subject names, duplicate identities. The same class was live in check-consumer-sbom.mjs, which reports entry names straight out of a document whose contents are tarball-derived. Fixed there in the same pass rather than waiting for it to be found separately. Tests assert the property rather than the wording: no message may contain a raw newline or a line beginning `::`. * ci: sweep the remaining log-injection sites, and require --event Round 5. My round-4 commit claimed forLog was applied to every value out of a tarball or a generated document, and specifically that check-consumer-sbom.mjs had been swept. That was wrong — Copilot found two live sites in that file and both reproduce: readDocument: SyntaxError.message embeds a snippet of the offending source, raw newlines included, and it is printed under ::error::. Identical to the defect fixed in the sibling script one commit earlier. crossCheck: excess() builds `name@version` strings out of document values and main prints each discrepancy, so a CycloneDX-only entry named `evil\n::error::FORGED` reached the log intact. Both now go through forLog. More usefully, the coverage is a SWEEP rather than another list: a test poisons every string-valued field a document can carry — names, versions, downloadLocations, purls, metadata.component, files — drives both entry points and all three crossCheck orderings, and asserts no resulting message contains a raw newline or carriage return. Eyeballing the call sites is what missed these two after a pass that declared the file clean, so the test now does the enumerating. Separately: `spec` now requires --event. installSpec treats an absent event as "not a release", so a malformed invocation exited 0 and resolved `latest` — silently turning OFF the release pin this script exists to apply. A workflow edit dropping the flag would have disabled item 1 with nothing reporting it. --tag stays optional; schedule and dispatch legitimately have none. * ci: count distinct names for the floor, and require an entry to identify itself Round 6, two Copilot findings, both real. The floor counted ENTRIES. Duplicates are only warned about — npm can legitimately place a version at two paths — so three copies of the target package cleared a floor of three while the closure had in fact collapsed to a single package. Worse, symmetric copies also satisfy origin, target, subject and the multiset cross-check, so nothing else would have caught it. Distinct names is what "the document collapsed" actually means. Measured before tightening: the smallest real closure carries 5 distinct names, so the floor of 3 keeps its headroom. A catalogued entry must now carry a non-empty name and version. Without that, an entry with a plausible origin but no identity produced no problem at all, and identities() rendered a missing version as `?` — so when both syft runs omitted the same metadata the floor and the cross-check agreed with each other and a malformed document shipped. Measured before requiring it: 428 entries across the four closures, none missing either field. Deep review's three advisories need no change: the origin check's false-red exposure, the release-only paths being unexercisable, and the three unpinned legs are all trades already argued in the PR body and deliberately kept. * ci: reject unknown flags instead of ignoring them Round 7. Both parsers accepted any dashed option and silently ignored it, which in this script is not a usability wart but another silent-disable path — the third found on this branch. Every optional flag here turns a safeguard OFF by being absent, so a typo that is ignored exits 0 having disabled it: --tagg a release leg resolves `latest` — the pin item 1 exists to add simply does not happen --exepct the installed-version assertion is skipped Both now fail, naming the flags the mode does accept. A flag valid for the other mode is rejected too, since `spec --dir` is as wrong as `spec --tagg`. Fixed in check-consumer-sbom.mjs in the same pass, though Copilot only named the sibling. No flag there is optional today, so a typo would currently be caught by the required check — but that is a property of today's flag list rather than a guarantee, and the first optional flag added would reopen it silently. Round 5 is the precedent for sweeping the sibling rather than waiting for it to be reported separately. Deep review's three advisories need no change; all are documented trades. Its #2 is worth a maintainer's explicit nod rather than a diff read: this job went from running no repository code to executing event-ref code, which is the one genuinely new trust surface in this PR.
This was referenced Sep 6, 2026
This was referenced Oct 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@allxsmith/bestax-lib@1.0.23
Patch Changes