Skip to content

Next Version Bump#2186

Merged
KZeillmann merged 13 commits into
mainfrom
develop
May 27, 2026
Merged

Next Version Bump#2186
KZeillmann merged 13 commits into
mainfrom
develop

Conversation

@KZeillmann

@KZeillmann KZeillmann commented May 27, 2026

Copy link
Copy Markdown
Contributor

Note

Low Risk
Mostly additive API (Hero shadow), visual/token tweaks, and defensive JS; no auth or data-path changes, though broad focus-ring updates affect many components visually.

Overview
This release bundles design-system product changes with Claude review automation and small repo hygiene.

Hero shadow prop — React (Hero.jsx, configs.js), SCSS (_hero.scss BEM modifiers aligned with Pine box-shadow steps), Storybook/docs, and sage-rails (SageHero, tokens, preview/props) all gain optional shadow values (none, 050500).

Pine token / a11y styling — Many component SCSS files swap focus-ring color from --pine-color-purple-600 to --pine-color-focus-ring; avatar surfaces move to semantic Pine background tokens; progress-bar breadcrumbs get sage-focus-ring; heading stack drops Greet Standard in favor of Inter only.

sage-system — Accordion, input group, panel controls, popover, select, and tabs gain null-guards on DOM lookups to avoid throws when markup is missing.

Tooling — New .claude agents (code, design, a11y, existence) and skills including sage-run-gauntlet for parallel pre-PR review; Dependabot adds weekly github-actions grouping; CODEOWNERS for .github/; lefthook runs pinned local commitlint instead of npx; @pine-ds/icons bumps in the lockfile.

Reviewed by Cursor Bugbot for commit fb35905. Bugbot is set up for automated code reviews on this repo. Configure here.

dependabot Bot and others added 13 commits February 20, 2026 09:28
Bumps [@pine-ds/icons](https://github.com/Kajabi/pine-icons) from 9.16.0 to 9.17.0.
- [Release notes](https://github.com/Kajabi/pine-icons/releases)
- [Changelog](https://github.com/Kajabi/pine-icons/blob/main/CHANGELOG.md)
- [Commits](Kajabi/pine-icons@v9.16.0...v9.17.0)

---
updated-dependencies:
- dependency-name: "@pine-ds/icons"
  dependency-version: 9.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* style: replace hardcoded purple-600 with focus-ring token

* style(breadcrumbs): add focus ring to progressbar variant
* feat(hero): add shadow prop to React component and SCSS

* feat(hero): add shadow prop to Rails component

* docs(hero): add shadow prop documentation and preview examples

* fix(hero): quote 050 shadow key in SCSS map and fix alt text hyphenation
Removes Greet Standard as the primary heading font. The $-heading-font
stack now leads with Inter, matching the direction set in ds-tokens
FLEX-2659 (semantic heading tokens → inter).

Language-specific overrides (Hebrew, Arabic, Devanagari) are unchanged.

FLEX-2656
chore: configure Dependabot for GitHub Actions
* chore(claude): add review gauntlet skills and reviewer agents

Introduces a sage-tailored review gauntlet modeled on the kajabi-products
.claude/skills + .claude/agents pattern.

Lineup:
- sage-code-reviewer — React patterns (propTypes, forwardRef, classnames,
  configs.js), Jest spec coverage, story-file presence, XSS-via-
  dangerouslySetInnerHTML, conventional commit scoping
- sage-design-reviewer — sage helper discipline (sage-color, sage-spacing,
  sage-border), Pine token alignment for dark-mode work, BEM-style
  classnames, SCSS structure, Storybook story coverage
- sage-a11y-reviewer — keyboard, ARIA, focus management, semantic HTML,
  contrast (split from design so leaf-component a11y findings aren't
  diluted)
- sage-existence-reviewer — duplication checks scoped to packages/sage-
  react/lib, packages/sage-assets/lib/stylesheets, and style-dictionary

No security reviewer: sage-lib is a pure FE library with no auth surface;
the only relevant check (XSS via dangerouslySetInnerHTML) folds into the
code reviewer.

sage-run-gauntlet orchestrates: identifies changed files, classifies them
to determine which reviewers apply, launches the agents in parallel, then
consolidates feedback with sequential numbering across BLOCKERS / SHOULD
FIX / CONSIDER. Always applies the ran-gauntlet label to the PR.

All skills and agents target develop and respect the "Do not Squash and
Merge" convention so Lerna can build per-package changelogs.

Used --no-verify because the lefthook commit-msg step (npx commitlint
--edit) pulls @commitlint/cli@20.5.3, which throws "TypeError: format is
not a function" on Node 22 regardless of message content. Sage-lib has
@commitlint/cli@11.0.0 pinned locally; a follow-up PR can switch the
hook to ./node_modules/.bin/commitlint to use the pinned version.

* fix(claude): address Bugbot feedback on gauntlet skills

Use yarn test:prod:react instead of nonexistent yarn jest; route token
edits to design-only; add JSX className design review; scope existence
to new artifacts and new token keys; include icon paths in checklist.

* fix(claude): align gauntlet agents and lineup table with Step 2

Code reviewer runs lint and test:prod:react; existence table covers
tokens and icons; a11y table excludes story-only diffs.

* chore(lefthook): use local commitlint instead of npx in commit-msg (#2183)

* chore(lefthook): use local commitlint instead of npx in commit-msg

The `commit-msg` hook ran `npx commitlint --edit`, which fetches the
latest from the npm registry every invocation. The current latest
(@commitlint/cli@20.5.3) throws `TypeError: format is not a function`
on Node 22 regardless of message content, blocking every commit and
forcing contributors to use `--no-verify`.

Switch to the locally-installed CLI pinned in yarn.lock. Lerna's
hoisted layout doesn't create node_modules/.bin/commitlint at the
repo root, so the binary is invoked directly via
`node node_modules/@commitlint/cli/cli.js --edit`.

Verified locally: a conventional message exits 0, a malformed message
("missing type") correctly fails with subject-empty + type-empty.

This commit itself was authored through the patched hook (no
--no-verify) as the proof.

* chore(lefthook): trim commitlint hook comment

* chore(lefthook): update commitlint hook comment

* chore(claude): use sonnet for gauntlet reviewer agents

* chore(claude): delegate code reviewer checks to sub-agents
… review notes (#2184)

* chore(claude): classify sage-system in run-gauntlet + add sage-system review notes

Running the gauntlet on sage-lib PR #2181 surfaced a classification gap:
packages/sage-system/lib/*.js (vanilla-JS IIFE DOM helpers like
Sage.popover and Sage.accordion) wasn't in sage-run-gauntlet's file-type
rules. The closest existing rule, "React component files," scoped to
packages/sage-react/lib/, didn't apply. I had to make a judgment call to
route the PR to code-reviewer only.

This adds:

1. An explicit sage-system rule in sage-run-gauntlet — route to
   code-reviewer only, skip a11y / design / existence, and skip the
   "missing spec" finding because sage-system has zero specs by repo
   convention.

2. A sage-system specifics section in sage-review-code — calls out that
   React criteria (propTypes, forwardRef, classnames, hooks, stories)
   do NOT apply, and lays out the BLOCKER / SHOULD FIX / not-applicable
   shape tuned to vanilla-JS DOM helpers. Cites the established
   defensive-pattern prior art in dropdown.js and tooltip.js so future
   reviewers anchor to it.

3. .claude/**/*.md added to the docs-only classification list in
   sage-run-gauntlet, mirroring the Pine fix in pine PR #745.

Discovered while running the gauntlet on PR #2181
(fix(sage-system): null-guard popover + audit-driven cleanup).

* chore(claude): add optional post-to-PR mode to sage-run-gauntlet (#2185)

* chore(claude): add optional post-to-PR mode to sage-run-gauntlet

Until now, sage-run-gauntlet output stayed in the conversation between
the developer running it and the agent. That assumes the runner is also
the PR author. When the gauntlet is run on someone else's PR — like the
self-test against sage-lib PR #2181 — findings have nowhere to go that
the original author can see.

This adds a "Post results to the PR (optional)" section under After the
Gauntlet:

- Opt in when running on someone else's PR, when the user explicitly
  asks, or when findings include at least one BLOCKER / SHOULD FIX on
  an open PR
- Opt out (default) for the runner's own pre-push self-review and for
  empty/clean results
- Format: single `gh pr review --comment` body preserving sequential
  numbering and per-finding file:line refs; inline per-line comments
  would fragment the structured output
- Fall back to `gh pr comment` if `gh pr review` 403s on collaborator
  scopes
- Required footer line so human reviewers can tell it's machine-
  generated and find the criteria via the ran-gauntlet label

Label-apply behavior unchanged: always run, regardless of post-or-not.

* fix(claude): address Bugbot feedback on gauntlet post-to-PR mode

Add ordered posting decision tree, skip when no PR exists, use
--body-file with --repo on primary and fallback gh commands, and fix
footer backticks in the heredoc template.

* fix(claude): scope gauntlet PR fallback and add --repo to label

Only fall back to gh pr comment on 403/404-style errors; fail loudly
otherwise. Resolve PR_NUM once with --repo on label and post commands.

* fix(claude): address round 3 Bugbot feedback on gauntlet post mode

Distinguish gh pr view failures from missing PR, reorder posting rules
so third-party CONSIDER findings post, align post body with Output
Format, conditional footer via LABEL_APPLIED, and tighten fallback handling.
handleKeydown's `evt.target.closest('[data-js-popover]')` returns null
when keypresses bubble up from elements outside any popover, causing
`isExpanded(null)` to throw `Cannot read properties of null (reading
'classList')`. This is firing ~1.5k times/hour in Kajabi Admin RUM.

Also null-guards handleClick's `children[0]` lookup, removes a dead
`popoverPanel` local that was assigned but never read, and guards the
module-level `SELECTOR_LAST_FOCUSED` ref in closePopoverPanel for the
case where close fires without a prior open.
Audit of `closest()` / chained `querySelector()` usage in sage-system
turned up the same pattern (unguarded null deref) in 5 more places that
can throw `Cannot read properties of null` under transient DOM states.

- accordion-panel: guard `el.closest('.sage-expandable-card')`,
  `el.closest(SELECTOR_ACCORDION)`, and the per-item header lookup.
- select: guard `evt.target.closest(elSelectClass)` in handleChange and
  `el.querySelector('select')` in init.
- inputgroup: guard `parentElement` and `.sage-input__field` lookups in
  togglePasswordDisplay; guard `closest()` results in addButtonPadding
  and positionButtonOnError.
- tabs: guard `document.querySelector` results in eventHandlerChange
  and dispatchChange; require `elPane.parentElement` before reading it.
- panel-controls: guard `closest()` results in updateBulkActionsToggleState
  and handleExpandCollapseClick; guard `elRoot.querySelector` focus calls.

All early-return guards — no behavioral change when DOM is well-formed.
Per Bugbot review: the overlay-dismissal branch doesn't depend on
elChild, so the early-return was wider than the path it protected.
Moved the guard inside the trigger branch where elChild is actually
dereferenced.
Guard parentNode check in handleClick and querySelector results in
openPopoverPanel/closePopoverPanel to complete the audit cleanup.
@KZeillmann
KZeillmann requested a review from QuintonJason May 27, 2026 19:02

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fb35905. Configure here.

// Shadow options — aligned with Pine pds-box shadow values
// Keep in sync with `packages/sage-react/lib/Hero/configs.js`
$-sage-hero-shadows: (
none: 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hero shadow "none" generates invalid CSS value

Medium Severity

The none key in $-sage-hero-shadows maps to the numeric value 0. When the @each loop interpolates this into box-shadow: #{$-val}, it produces box-shadow: 0; which is invalid CSS — the box-shadow shorthand requires at minimum two length values (offset-x and offset-y). Browsers will ignore the invalid declaration, so the shadow="none" option won't actually reset any inherited box-shadow. The value for none needs to be the CSS keyword none (not the number 0) to generate valid box-shadow: none;.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fb35905. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-issue

// Keep in sync with `packages/sage-react/lib/Hero/configs.js`
$-sage-hero-shadows: (
none: 0,
"050": sage-shadow("050"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sass type mismatch breaks "050" shadow variant lookup

Medium Severity

sage-shadow("050") passes a quoted string "050" to map-get, but in the $sage-shadows map the key 050 is parsed by Sass as the number 50. Sass map-get requires both type and value to match, so string "050" ≠ number 50 returns null. This makes .sage-hero--shadow-050 emit an empty box-shadow value — the 050 shadow variant silently does nothing. The other entries like sage-shadow(100) work because both sides are numbers.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fb35905. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-issue

@QuintonJason QuintonJason left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go

@KZeillmann
KZeillmann merged commit 9fcd2cd into main May 27, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants