Repository navigation
Conversation
The existing Vitest report showed 98.21% statement coverage while
omitting 113 unimported `src` files and counting test helpers. Python
coverage also counted test code. Make both denominators explicit and add
168 behavioral test cases for the highest-risk gaps found during a
repository-wide coverage audit.
- Add 48 TypeScript cases for automation biometrics
freshness/query/conversion rules, temperature-dial commands and drag
lifecycle, power-button optimistic/linked failure behavior, and
diagnostic chart data/formatting.
- Add 120 Python cases for RAW CBOR framing and truncation, calibration
trigger files, sensor baseline algorithms, and vital-validation
boundaries.
- Include unimported application files in Vitest coverage, exclude test
helpers, export JSON summaries, and enable Python branch coverage with
test modules excluded.
- Record the audit, per-area coverage, reproduction commands, and
remaining gaps in `docs/testing/coverage-audit-2026-09-06.md`.
Validation: 3,828 Vitest tests passed (one pre-existing skip), 353
Python tests passed across all six CI scopes, TypeScript typecheck
passed, and changed-file ESLint passed. Biometrics signal reading, RAW
framing, and both diagnostic charts now have 100% line/branch coverage;
the dial and power button have 100% line coverage. Shared Python
calibration coverage rises from 19% to 80% (statements + branches).
The corrected overall TypeScript report is 64.44% statements / 53.09%
branches / 65.49% lines. This drop reflects the expanded denominator,
not removed tests or a runtime regression. The audit documents the
substantial remaining UI and Python lifecycle coverage backlog; it does
not claim complete application coverage.
CodeRabbit follow-up: fixed calibration trigger FIFO ordering. Both
reads and deletions now compare numeric timestamp/PID/counter suffixes,
so counter 9 precedes 10, including existing unpadded queued files.
Strengthened tests fail against the original lexical ordering and pass
with the fix; 194 common-module tests pass. Mixed legacy, padded and
unpadded filenames are also covered.
<!-- sleepypod:install-start -->
<!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block.
Hello fellow agent / curious human! Anything between the
sleepypod:install-start and sleepypod:install-end markers
gets overwritten on the next CI run. Want to change what's
here? Edit .github/workflows/build.yml instead. 💤 -->
<details open>
<summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary>
Pick the snippet that matches your pod. **Already-installed pods can't
use the curl bootstraps below** — sleepypod's egress firewall DROPs
github.com, and the script that knows how to unblock WAN is the very
file curl is trying to fetch. Use `sp-update` instead; it's on disk and
opens WAN as its first step.
🔄 **Already on a sleepypod** (most common — review a PR on a running
pod):
```bash
sudo sp-update test/dev-coverage-audit
```
🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every
push:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/test/dev-coverage-audit/scripts/install \
| sudo bash -s -- --branch test/dev-coverage-audit
```
🎯 **Pin to this exact build** ([run
34047208411](https://github.com/sleepypod/core/actions/runs/34047208411)
· `329a19c`) — fresh-pod path, locked to one CI artifact for
reproducible review:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/329a19ca41826b55de709d6771deec83f2234745/scripts/install \
| sudo bash -s -- \
--branch test/dev-coverage-audit \
--artifact-url 'https://nightly.link/sleepypod/core/actions/runs/34047208411/sleepypod-core.zip'
```
🧊 Artifact:
[`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/34047208411/artifacts/9993481568)
· self-destructs in 30 days 💥
</details>
<!-- sleepypod:install-end -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Calibration trigger requests are now processed in the correct
chronological order, including requests created at the same time and
older-format filenames.
- **Tests**
- Expanded automated coverage for calibration, signal reading,
biometrics, temperature controls, power controls, trend charts, and data
validation.
- Improved coverage reporting to include production code and branch
coverage while excluding test artifacts.
- **Documentation**
- Added a detailed test coverage audit with results, inventories, and
follow-up priorities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
> ℹ️ **Note**
>
> This PR body was truncated due to platform limits.
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update | Pending |
|---|---|---|---|---|---|---|
| [@base-ui/react](https://base-ui.com)
([source](https://redirect.github.com/mui/base-ui/tree/HEAD/packages/react))
| [`1.7.0` →
`1.8.0`](https://renovatebot.com/diffs/npm/@base-ui%2freact/1.7.0/1.8.0)
|

|

| dependencies | minor | |
|
[@types/react-dom](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dom)
([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom))
| [`19.2.5` →
`19.2.7`](https://renovatebot.com/diffs/npm/@types%2freact-dom/19.2.5/19.2.7)
|

|

| devDependencies | patch | |
| [@xyflow/react](https://reactflow.dev)
([source](https://redirect.github.com/xyflow/xyflow/tree/HEAD/packages/react))
| [`12.11.5` →
`12.11.6`](https://renovatebot.com/diffs/npm/@xyflow%2freact/12.11.5/12.11.6)
|

|

| dependencies | patch | |
|
[eslint-config-next](https://nextjs.org/docs/app/api-reference/config/eslint)
([source](https://redirect.github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next))
| [`16.3.3` →
`16.3.4`](https://renovatebot.com/diffs/npm/eslint-config-next/16.3.3/16.3.4)
|

|

| devDependencies | patch | |
| [globals](https://redirect.github.com/sindresorhus/globals) |
[`17.11.0` →
`17.12.0`](https://renovatebot.com/diffs/npm/globals/17.11.0/17.12.0) |

|

| devDependencies | minor | |
| [lucide-react](https://lucide.dev)
([source](https://redirect.github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react))
| [`1.35.0` →
`1.41.0`](https://renovatebot.com/diffs/npm/lucide-react/1.35.0/1.41.0)
|

|

| dependencies | minor | `1.42.0` |
| [next](https://nextjs.org)
([source](https://redirect.github.com/vercel/next.js)) | [`16.3.3` →
`16.3.4`](https://renovatebot.com/diffs/npm/next/16.3.3/16.3.4) |

|

| dependencies | patch | |
| [python](https://redirect.github.com/actions/python-versions) | `3.10`
→ `3.14` |

|

| uses-with | minor | |
| [shadcn](https://ui.shadcn.com)
([source](https://redirect.github.com/shadcn-ui/ui/tree/HEAD/packages/shadcn))
| [`4.19.0` →
`4.21.0`](https://renovatebot.com/diffs/npm/shadcn/4.19.0/4.21.0) |

|

| dependencies | minor | |
| [tsx](https://tsx.hirok.io)
([source](https://redirect.github.com/privatenumber/tsx)) | [`4.23.12` →
`4.23.13`](https://renovatebot.com/diffs/npm/tsx/4.23.12/4.23.13) |

|

| devDependencies | patch | |
|
[typescript-eslint](https://typescript-eslint.io/packages/typescript-eslint)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint))
| [`8.68.0` →
`8.69.0`](https://renovatebot.com/diffs/npm/typescript-eslint/8.68.0/8.69.0)
|

|

| devDependencies | minor | |
| [zod](https://zod.dev)
([source](https://redirect.github.com/colinhacks/zod)) | [`4.4.3` →
`4.5.4`](https://renovatebot.com/diffs/npm/zod/4.4.3/4.5.4) |

|

| dependencies | minor | |
---
### Release Notes
<details>
<summary>mui/base-ui (@​base-ui/react)</summary>
###
[`v1.8.0`](https://redirect.github.com/mui/base-ui/blob/HEAD/CHANGELOG.md#v180)
[Compare
Source](https://redirect.github.com/mui/base-ui/compare/v1.7.0...v1.8.0)
<!-- generated comparing v1.7.0...master -->
*Sep 4, 2026*
##### General changes
- Fix label association when a control unregisters
([#​5456](https://redirect.github.com/mui/base-ui/issues/5456)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Reduce animation completion work
([#​5535](https://redirect.github.com/mui/base-ui/issues/5535)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix prop and ref merging for lazy render elements
([#​5562](https://redirect.github.com/mui/base-ui/issues/5562)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix roving focus when items are added or removed
([#​5447](https://redirect.github.com/mui/base-ui/issues/5447)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix hover and focus interaction issues
([#​5037](https://redirect.github.com/mui/base-ui/issues/5037)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Improve trigger mount performance
([#​5426](https://redirect.github.com/mui/base-ui/issues/5426)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix disabled anchor tracking on scroll
([#​5478](https://redirect.github.com/mui/base-ui/issues/5478)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix transform origin for start/end alignment
([#​5015](https://redirect.github.com/mui/base-ui/issues/5015)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Remove duplicate options key from arrow middleware
([#​5606](https://redirect.github.com/mui/base-ui/issues/5606)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Register passive touch listeners
([#​5572](https://redirect.github.com/mui/base-ui/issues/5572)) by
[@​wanxiankai](https://redirect.github.com/wanxiankai)
##### Alert Dialog
- Ignore outside clicks from presses that began before open
([#​5378](https://redirect.github.com/mui/base-ui/issues/5378)) by
[@​michaldudak](https://redirect.github.com/michaldudak)
##### Autocomplete
- Render groups with the rowgroup role in grid mode
([#​5564](https://redirect.github.com/mui/base-ui/issues/5564)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Allow opening and browsing the popup while `readOnly`
([#​5541](https://redirect.github.com/mui/base-ui/issues/5541)) by
[@​vikuscz](https://redirect.github.com/vikuscz)
- Move `aria-orientation` to the role owners
([#​5551](https://redirect.github.com/mui/base-ui/issues/5551)) by
[@​gonzoblasco](https://redirect.github.com/gonzoblasco)
- Hide group labels and scrollbars from the accessibility tree
([#​5598](https://redirect.github.com/mui/base-ui/issues/5598)) by
[@​azeng4499](https://redirect.github.com/azeng4499)
##### Avatar
- Add `keepMounted` prop to `<Avatar.Image>`
([#​5536](https://redirect.github.com/mui/base-ui/issues/5536)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Checkbox
- Fix stale and duplicated control IDs
([#​5457](https://redirect.github.com/mui/base-ui/issues/5457)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix controlled blur validation and stale filled state
([#​5563](https://redirect.github.com/mui/base-ui/issues/5563)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Checkbox Group
- Fix stale and duplicated control IDs
([#​5457](https://redirect.github.com/mui/base-ui/issues/5457)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Combobox
- Fix applying `data-readonly` to `<Combobox.Trigger>`
([#​5418](https://redirect.github.com/mui/base-ui/issues/5418)) by
[@​sai6855](https://redirect.github.com/sai6855)
- Add `createItems` collection API
([#​5326](https://redirect.github.com/mui/base-ui/issues/5326)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix cancellation when preserving the filter after selection
([#​5362](https://redirect.github.com/mui/base-ui/issues/5362)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Render groups with the rowgroup role in grid mode
([#​5564](https://redirect.github.com/mui/base-ui/issues/5564)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Allow opening and browsing the popup while `readOnly`
([#​5541](https://redirect.github.com/mui/base-ui/issues/5541)) by
[@​vikuscz](https://redirect.github.com/vikuscz)
- Move `aria-orientation` to the role owners
([#​5551](https://redirect.github.com/mui/base-ui/issues/5551)) by
[@​gonzoblasco](https://redirect.github.com/gonzoblasco)
- Fix record item label lookup reading from `Object.prototype`
([#​5518](https://redirect.github.com/mui/base-ui/issues/5518)) by
[@​XionWCFM](https://redirect.github.com/XionWCFM)
- Anchor multiple selection to the first selected item with a linear
lookup
([#​5573](https://redirect.github.com/mui/base-ui/issues/5573),
[#​5613](https://redirect.github.com/mui/base-ui/issues/5613)) by
[@​atomiks](https://redirect.github.com/atomiks) and
[@​michaldudak](https://redirect.github.com/michaldudak)
- Hide group labels and scrollbars from the accessibility tree
([#​5598](https://redirect.github.com/mui/base-ui/issues/5598)) by
[@​azeng4499](https://redirect.github.com/azeng4499)
##### Dialog
- Ignore outside clicks from presses that began before open
([#​5378](https://redirect.github.com/mui/base-ui/issues/5378)) by
[@​michaldudak](https://redirect.github.com/michaldudak)
##### Drawer
- Ignore swipes without an attributed direction when using snap points
([#​5477](https://redirect.github.com/mui/base-ui/issues/5477)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Respect canceled snap point dismissal
([#​5571](https://redirect.github.com/mui/base-ui/issues/5571)) by
[@​radist2s](https://redirect.github.com/radist2s)
- Ignore the page scroller when starting a swipe
([#​5567](https://redirect.github.com/mui/base-ui/issues/5567)) by
[@​flaviendelangle](https://redirect.github.com/flaviendelangle)
##### Field
- Fix custom validity ownership and validation lifecycle
([#​5449](https://redirect.github.com/mui/base-ui/issues/5449)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Sync controlled value changes with field state
([#​5460](https://redirect.github.com/mui/base-ui/issues/5460)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix stale and duplicated control IDs
([#​5457](https://redirect.github.com/mui/base-ui/issues/5457)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Validate once on Enter inside a Form
([#​5459](https://redirect.github.com/mui/base-ui/issues/5459)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Publish neutral validity while async validation is in flight
([#​5600](https://redirect.github.com/mui/base-ui/issues/5600)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Fix controlled blur validation and stale filled state
([#​5563](https://redirect.github.com/mui/base-ui/issues/5563)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Form
- Fix `clearErrors` dropping updates when multiple fields change at once
([#​5446](https://redirect.github.com/mui/base-ui/issues/5446)) by
[@​sai6855](https://redirect.github.com/sai6855)
##### Menu
- Play the enter transition for an initially open submenu
([#​4383](https://redirect.github.com/mui/base-ui/issues/4383)) by
[@​michaldudak](https://redirect.github.com/michaldudak)
- Move `aria-orientation` to the role owners
([#​5551](https://redirect.github.com/mui/base-ui/issues/5551)) by
[@​gonzoblasco](https://redirect.github.com/gonzoblasco)
- Hide group labels and scrollbars from the accessibility tree
([#​5598](https://redirect.github.com/mui/base-ui/issues/5598)) by
[@​azeng4499](https://redirect.github.com/azeng4499)
##### Menubar
- Fix menubar accessibility tree to satisfy `aria-required-children`
([#​5058](https://redirect.github.com/mui/base-ui/issues/5058)) by
[@​michaldudak](https://redirect.github.com/michaldudak)
##### Navigation Menu
- Fix pointer-events lock when sweeping quickly across a trigger
([#​5454](https://redirect.github.com/mui/base-ui/issues/5454)) by
[@​edwardhorsey](https://redirect.github.com/edwardhorsey)
- Keep focus on trigger when opening
([#​5479](https://redirect.github.com/mui/base-ui/issues/5479)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Add disabled data attribute to `<NavigationMenu.Trigger>`
([#​5521](https://redirect.github.com/mui/base-ui/issues/5521)) by
[@​sai6855](https://redirect.github.com/sai6855)
##### Number Field
- Stop incrementing when disabled during press-and-hold
([#​5435](https://redirect.github.com/mui/base-ui/issues/5435)) by
[@​alex-js-ltd](https://redirect.github.com/alex-js-ltd)
- Prevent scrubbing on horizontal wheel events
([#​5463](https://redirect.github.com/mui/base-ui/issues/5463)) by
[@​sai6855](https://redirect.github.com/sai6855)
- Preserve native and consumer-controlled selection when focusing the
input
([#​5578](https://redirect.github.com/mui/base-ui/issues/5578),
[#​5619](https://redirect.github.com/mui/base-ui/issues/5619)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Popover
- Ignore outside clicks from presses that began before open
([#​5378](https://redirect.github.com/mui/base-ui/issues/5378)) by
[@​michaldudak](https://redirect.github.com/michaldudak)
- Fix detached trigger store migration
([#​5442](https://redirect.github.com/mui/base-ui/issues/5442)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Scroll Area
- Hide group labels and scrollbars from the accessibility tree
([#​5598](https://redirect.github.com/mui/base-ui/issues/5598)) by
[@​azeng4499](https://redirect.github.com/azeng4499)
- Prevent scrollbars from stealing focus
([#​5430](https://redirect.github.com/mui/base-ui/issues/5430)) by
[@​marwenilo](https://redirect.github.com/marwenilo)
##### Select
- Move `aria-orientation` to the role owners
([#​5551](https://redirect.github.com/mui/base-ui/issues/5551)) by
[@​gonzoblasco](https://redirect.github.com/gonzoblasco)
- Anchor multiple selection to the first selected item with a linear
lookup
([#​5573](https://redirect.github.com/mui/base-ui/issues/5573),
[#​5613](https://redirect.github.com/mui/base-ui/issues/5613)) by
[@​atomiks](https://redirect.github.com/atomiks) and
[@​michaldudak](https://redirect.github.com/michaldudak)
- Hide group labels and scrollbars from the accessibility tree
([#​5598](https://redirect.github.com/mui/base-ui/issues/5598)) by
[@​azeng4499](https://redirect.github.com/azeng4499)
- Fix the root ID being ignored inside a Field
([#​5461](https://redirect.github.com/mui/base-ui/issues/5461)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Remove redundant size check in `<Select.Positioner>`
([#​5469](https://redirect.github.com/mui/base-ui/issues/5469)) by
[@​sai6855](https://redirect.github.com/sai6855)
- Allow opening and browsing the popup while `readOnly`
([#​5531](https://redirect.github.com/mui/base-ui/issues/5531)) by
[@​vikuscz](https://redirect.github.com/vikuscz)
- Fix record item label lookup reading from `Object.prototype`
([#​5518](https://redirect.github.com/mui/base-ui/issues/5518)) by
[@​XionWCFM](https://redirect.github.com/XionWCFM)
##### Slider
- Prevent update loops from unstable refs
([#​5441](https://redirect.github.com/mui/base-ui/issues/5441)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Switch
- Fix controlled blur validation and stale filled state
([#​5563](https://redirect.github.com/mui/base-ui/issues/5563)) by
[@​atomiks](https://redirect.github.com/atomiks)
##### Tabs
- Prevent update loops from unstable refs
([#​5441](https://redirect.github.com/mui/base-ui/issues/5441)) by
[@​atomiks](https://redirect.github.com/atomiks)
- Consider 3D transforms when positioning the indicator
([#​4852](https://redirect.github.com/mui/base-ui/issues/4852)) by
[@​michaldudak](https://redirect.github.com/michaldudak)
##### Toast
- Allow functional toast updates derived from the current toast
([#​5464](https://redirect.github.com/mui/base-ui/issues/5464),
[#​5611](https://redirect.github.com/mui/base-ui/issues/5611),
[#​5629](https://redirect.github.com/mui/base-ui/issues/5629)) by
[@​chuganzy](https://redirect.github.com/chuganzy) and
[@​michaldudak](https://redirect.github.com/michaldudak)
##### Tooltip
- Respect trigger delay with zero provider delay
([#​5444](https://redirect.github.com/mui/base-ui/issues/5444)) by
[@​atomiks](https://redirect.github.com/atomiks)
All contributors of this release in alphabetical order:
[@​alex-js-ltd](https://redirect.github.com/alex-js-ltd),
[@​atomiks](https://redirect.github.com/atomiks),
[@​azeng4499](https://redirect.github.com/azeng4499),
[@​chuganzy](https://redirect.github.com/chuganzy),
[@​edwardhorsey](https://redirect.github.com/edwardhorsey),
[@​flaviendelangle](https://redirect.github.com/flaviendelangle),
[@​gilsmt](https://redirect.github.com/gilsmt),
[@​gonzoblasco](https://redirect.github.com/gonzoblasco),
[@​jjenzz](https://redirect.github.com/jjenzz),
[@​marwenilo](https://redirect.github.com/marwenilo),
[@​michaldudak](https://redirect.github.com/michaldudak),
[@​radist2s](https://redirect.github.com/radist2s),
[@​sai6855](https://redirect.github.com/sai6855),
[@​vikuscz](https://redirect.github.com/vikuscz),
[@​wanxiankai](https://redirect.github.com/wanxiankai),
[@​XionWCFM](https://redirect.github.com/XionWCFM)
</details>
<details>
<summary>xyflow/xyflow (@​xyflow/react)</summary>
###
[`v12.11.6`](https://redirect.github.com/xyflow/xyflow/blob/HEAD/packages/react/CHANGELOG.md#12116)
[Compare
Source](https://redirect.github.com/xyflow/xyflow/compare/@xyflow/react@12.11.5...@xyflow/react@12.11.6)
##### Patch Changes
- [#​5994](https://redirect.github.com/xyflow/xyflow/pull/5994)
[`2350158`](https://redirect.github.com/xyflow/xyflow/commit/2350158ba91e82823477b0a1160c0319e79d6172)
- Reset all properties in provider store when unmounting flow
- [#​5997](https://redirect.github.com/xyflow/xyflow/pull/5997)
[`0117cc9`](https://redirect.github.com/xyflow/xyflow/commit/0117cc9447b082369f238243d15327a5ac9c3dc3)
- Prevent attribution warning when pane is not rendered either
- Updated dependencies
\[[`0117cc9`](https://redirect.github.com/xyflow/xyflow/commit/0117cc9447b082369f238243d15327a5ac9c3dc3)]:
-
[@​xyflow/system](https://redirect.github.com/xyflow/system)@0.0.82
</details>
<details>
<summary>vercel/next.js (eslint-config-next)</summary>
###
[`v16.3.4`](https://redirect.github.com/vercel/next.js/compare/v16.3.3...v16.3.4)
[Compare
Source](https://redirect.github.com/vercel/next.js/compare/v16.3.3...v16.3.4)
</details>
<details>
<summary>sindresorhus/globals (globals)</summary>
###
[`v17.12.0`](https://redirect.github.com/sindresorhus/globals/releases/tag/v17.12.0)
[Compare
Source](https://redirect.github.com/sindresorhus/globals/compare/v17.11.0...v17.12.0)
- Update globals (2026-09-01)
([#​353](https://redirect.github.com/sindresorhus/globals/issues/353))
[`50a2119`](https://redirect.github.com/sindresorhus/globals/commit/50a2119)
- Add `__webpack_layer__` global
([#​351](https://redirect.github.com/sindresorhus/globals/issues/351))
[`779a11a`](https://redirect.github.com/sindresorhus/globals/commit/779a11a)
***
</details>
<details>
<summary>lucide-icons/lucide (lucide-react)</summary>
###
[`v1.41.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.41.0):
Version 1.41.0
[Compare
Source](https://redirect.github.com/lucide-icons/lucide/compare/1.40.0...1.41.0)
#### What's Changed
- feat(icons): Add new icons `germ` and `germ-off` by
[@​rrod497](https://redirect.github.com/rrod497) in
[#​4056](https://redirect.github.com/lucide-icons/lucide/pull/4056)
- feat(icons): added `door-stairwell` icon & updated `door-*` icons by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3554](https://redirect.github.com/lucide-icons/lucide/pull/3554)
- feat(icons): added `credit-card-reader` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​4616](https://redirect.github.com/lucide-icons/lucide/pull/4616)
- feat(icons): added 'engine' icon by
[@​benhaube](https://redirect.github.com/benhaube) in
[#​4598](https://redirect.github.com/lucide-icons/lucide/pull/4598)
- feat(icons): fixed `germ` & `germ-off` by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4789](https://redirect.github.com/lucide-icons/lucide/pull/4789)
- chore(deps): bump the vue-deps group with 2 updates by
[@​dependabot](https://redirect.github.com/dependabot)\[bot] in
[#​4771](https://redirect.github.com/lucide-icons/lucide/pull/4771)
- feat(icons): added `virus`/`virus-off` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4765](https://redirect.github.com/lucide-icons/lucide/pull/4765)
- chore(copilot-reviews): Improve use-cases description. by
[@​ericfennis](https://redirect.github.com/ericfennis) in
[#​4558](https://redirect.github.com/lucide-icons/lucide/pull/4558)
- feat(icons): add `can-soda` icon by
[@​jaynewey](https://redirect.github.com/jaynewey) in
[#​4718](https://redirect.github.com/lucide-icons/lucide/pull/4718)
- feat(icons): added `square-alert` Icon by
[@​viralcodex](https://redirect.github.com/viralcodex) in
[#​3687](https://redirect.github.com/lucide-icons/lucide/pull/3687)
- chore(lab): Add label for lab icons by
[@​ericfennis](https://redirect.github.com/ericfennis) in
[#​4793](https://redirect.github.com/lucide-icons/lucide/pull/4793)
- feat(icons): changed `lab/bottle-toothbrush-comb` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4756](https://redirect.github.com/lucide-icons/lucide/pull/4756)
- ci([@​lucide/lab](https://redirect.github.com/lucide/lab)):
Create automatic release flow for `@lucide/lab` by
[@​ericfennis](https://redirect.github.com/ericfennis) in
[#​4792](https://redirect.github.com/lucide-icons/lucide/pull/4792)
- fix(icons): removed `trash` icon in favour of `trash-2` by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3141](https://redirect.github.com/lucide-icons/lucide/pull/3141)
- feat(icons): changed `leaf` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4801](https://redirect.github.com/lucide-icons/lucide/pull/4801)
#### New Contributors
- [@​jaynewey](https://redirect.github.com/jaynewey) made their
first contribution in
[#​4718](https://redirect.github.com/lucide-icons/lucide/pull/4718)
- [@​viralcodex](https://redirect.github.com/viralcodex) made
their first contribution in
[#​3687](https://redirect.github.com/lucide-icons/lucide/pull/3687)
**Full Changelog**:
<https://github.com/lucide-icons/lucide/compare/1.40.0...1.41.0>
###
[`v1.40.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.40.0):
Version 1.40.0
[Compare
Source](https://redirect.github.com/lucide-icons/lucide/compare/1.39.0...1.40.0)
#### What's Changed
- feat(icons): added `can` icon by
[@​l0uisgrange](https://redirect.github.com/l0uisgrange) in
[#​4767](https://redirect.github.com/lucide-icons/lucide/pull/4767)
- feat(icons): added `bridge` icon by
[@​Nykoula](https://redirect.github.com/Nykoula) in
[#​3949](https://redirect.github.com/lucide-icons/lucide/pull/3949)
- feat(icons): added `shrimp-off` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3613](https://redirect.github.com/lucide-icons/lucide/pull/3613)
- feat(icons): added `shopping-cart-plus` & `shopping-cart-minus` icons
by [@​Ajay199210](https://redirect.github.com/Ajay199210) in
[#​4248](https://redirect.github.com/lucide-icons/lucide/pull/4248)
- fix(docs): updated artboard name on illustrator template by
[@​EthanHazel](https://redirect.github.com/EthanHazel) in
[#​4724](https://redirect.github.com/lucide-icons/lucide/pull/4724)
- chore(deps): bump softprops/action-gh-release from 3.0.2 to 3.0.3 in
the github-actions group by
[@​dependabot](https://redirect.github.com/dependabot)\[bot] in
[#​4781](https://redirect.github.com/lucide-icons/lucide/pull/4781)
- feat(icons): added `lighthouse` icon by
[@​Xougui](https://redirect.github.com/Xougui) in
[#​4507](https://redirect.github.com/lucide-icons/lucide/pull/4507)
#### New Contributors
- [@​Nykoula](https://redirect.github.com/Nykoula) made their
first contribution in
[#​3949](https://redirect.github.com/lucide-icons/lucide/pull/3949)
- [@​Ajay199210](https://redirect.github.com/Ajay199210) made
their first contribution in
[#​4248](https://redirect.github.com/lucide-icons/lucide/pull/4248)
- [@​Xougui](https://redirect.github.com/Xougui) made their first
contribution in
[#​4507](https://redirect.github.com/lucide-icons/lucide/pull/4507)
**Full Changelog**:
<https://github.com/lucide-icons/lucide/compare/1.39.0...1.40.0>
###
[`v1.39.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.39.0):
Version 1.39.0
[Compare
Source](https://redirect.github.com/lucide-icons/lucide/compare/1.38.0...1.39.0)
#### What's Changed
- fix(icons): added RWD metadata to monitor, tablet & smartphone icons
by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere)
in
[#​4775](https://redirect.github.com/lucide-icons/lucide/pull/4775)
- feat(icons): add `whistle` icon by
[@​timmy471](https://redirect.github.com/timmy471) in
[#​3006](https://redirect.github.com/lucide-icons/lucide/pull/3006)
- feat(icons): Add `mail-pen` icon by
[@​jennieboops](https://redirect.github.com/jennieboops) in
[#​4399](https://redirect.github.com/lucide-icons/lucide/pull/4399)
**Full Changelog**:
<https://github.com/lucide-icons/lucide/compare/1.38.0...1.39.0>
###
[`v1.38.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.38.0):
Version 1.38.0
[Compare
Source](https://redirect.github.com/lucide-icons/lucide/compare/1.37.0...1.38.0)
#### What's Changed
- feat(icons): Add polygon icon by
[@​timmy471](https://redirect.github.com/timmy471) in
[#​3007](https://redirect.github.com/lucide-icons/lucide/pull/3007)
**Full Changelog**:
<https://github.com/lucide-icons/lucide/compare/1.36.0...1.38.0>
###
[`v1.37.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.37.0):
Version 1.37.0
[Compare
Source](https://redirect.github.com/lucide-icons/lucide/compare/1.36.0...1.37.0)
#### What's Changed
- feat(icons): added 'robot-vacuum' icon by
[@​benhaube](https://redirect.github.com/benhaube) in
[#​4599](https://redirect.github.com/lucide-icons/lucide/pull/4599)
- feat(icons): made `face-angry` even more angry by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4708](https://redirect.github.com/lucide-icons/lucide/pull/4708)
- fix(icons): changed `swords` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​4707](https://redirect.github.com/lucide-icons/lucide/pull/4707)
- fix(icons): changed `shopping-cart` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​2909](https://redirect.github.com/lucide-icons/lucide/pull/2909)
- docs(icon-description): added old and empty tags/use case for ghost by
[@​TFJ5183](https://redirect.github.com/TFJ5183) in
[#​4762](https://redirect.github.com/lucide-icons/lucide/pull/4762)
- feat(icons): add `playing-card`, `playing-cards`, and
`playing-cards-fan` by
[@​Barakudum](https://redirect.github.com/Barakudum) in
[#​4258](https://redirect.github.com/lucide-icons/lucide/pull/4258)
- fix(icons): changed `beef` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3457](https://redirect.github.com/lucide-icons/lucide/pull/3457)
- chore(tags): added music disc related tags to `circle-dot` by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3909](https://redirect.github.com/lucide-icons/lucide/pull/3909)
- fix(icons): changed `panda` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3187](https://redirect.github.com/lucide-icons/lucide/pull/3187)
- fix(icons): rotate `blend` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3105](https://redirect.github.com/lucide-icons/lucide/pull/3105)
- fix(icons): rotate `key` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3111](https://redirect.github.com/lucide-icons/lucide/pull/3111)
- fix(icons): changed `square-split-vertical` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3939](https://redirect.github.com/lucide-icons/lucide/pull/3939)
- fix(icons): changed `asterisk` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3906](https://redirect.github.com/lucide-icons/lucide/pull/3906)
- fix(icons): unify check, cross and plus sizes across icons by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​3875](https://redirect.github.com/lucide-icons/lucide/pull/3875)
- feat(icons): added `message-circle-dashed-check` icon by
[@​aliyasirnac](https://redirect.github.com/aliyasirnac) in
[#​3678](https://redirect.github.com/lucide-icons/lucide/pull/3678)
- fix(icons): changed `piano` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4766](https://redirect.github.com/lucide-icons/lucide/pull/4766)
- feat(icons): added `credit-card-x` & `credit-card-check` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4763](https://redirect.github.com/lucide-icons/lucide/pull/4763)
- feat(icons): added `credit-card-minus` icon by
[@​ameniti-mx](https://redirect.github.com/ameniti-mx) in
[#​4440](https://redirect.github.com/lucide-icons/lucide/pull/4440)
- feat(icons): added `credit-card-plus` icon by
[@​ameniti-mx](https://redirect.github.com/ameniti-mx) in
[#​4441](https://redirect.github.com/lucide-icons/lucide/pull/4441)
#### New Contributors
- [@​benhaube](https://redirect.github.com/benhaube) made their
first contribution in
[#​4599](https://redirect.github.com/lucide-icons/lucide/pull/4599)
- [@​TFJ5183](https://redirect.github.com/TFJ5183) made their
first contribution in
[#​4762](https://redirect.github.com/lucide-icons/lucide/pull/4762)
- [@​aliyasirnac](https://redirect.github.com/aliyasirnac) made
their first contribution in
[#​3678](https://redirect.github.com/lucide-icons/lucide/pull/3678)
- [@​ameniti-mx](https://redirect.github.com/ameniti-mx) made
their first contribution in
[#​4440](https://redirect.github.com/lucide-icons/lucide/pull/4440)
**Full Changelog**:
<https://github.com/lucide-icons/lucide/compare/1.35.0...1.37.0>
###
[`v1.36.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.36.0):
Version 1.36.0
[Compare
Source](https://redirect.github.com/lucide-icons/lucide/compare/1.35.0...1.36.0)
#### What's Changed
- feat(icons): added 'robot-vacuum' icon by
[@​benhaube](https://redirect.github.com/benhaube) in
[#​4599](https://redirect.github.com/lucide-icons/lucide/pull/4599)
- feat(icons): made `face-angry` even more angry by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4708](https://redirect.github.com/lucide-icons/lucide/pull/4708)
- fix(icons): changed `swords` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​4707](https://redirect.github.com/lucide-icons/lucide/pull/4707)
- fix(icons): changed `shopping-cart` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​2909](https://redirect.github.com/lucide-icons/lucide/pull/2909)
- docs(icon-description): added old and empty tags/use case for ghost by
[@​TFJ5183](https://redirect.github.com/TFJ5183) in
[#​4762](https://redirect.github.com/lucide-icons/lucide/pull/4762)
- feat(icons): add `playing-card`, `playing-cards`, and
`playing-cards-fan` by
[@​Barakudum](https://redirect.github.com/Barakudum) in
[#​4258](https://redirect.github.com/lucide-icons/lucide/pull/4258)
- fix(icons): changed `beef` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3457](https://redirect.github.com/lucide-icons/lucide/pull/3457)
- chore(tags): added music disc related tags to `circle-dot` by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3909](https://redirect.github.com/lucide-icons/lucide/pull/3909)
- fix(icons): changed `panda` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3187](https://redirect.github.com/lucide-icons/lucide/pull/3187)
- fix(icons): rotate `blend` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3105](https://redirect.github.com/lucide-icons/lucide/pull/3105)
- fix(icons): rotate `key` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3111](https://redirect.github.com/lucide-icons/lucide/pull/3111)
- fix(icons): changed `square-split-vertical` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3939](https://redirect.github.com/lucide-icons/lucide/pull/3939)
- fix(icons): changed `asterisk` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3906](https://redirect.github.com/lucide-icons/lucide/pull/3906)
- fix(icons): unify check, cross and plus sizes across icons by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​3875](https://redirect.github.com/lucide-icons/lucide/pull/3875)
- feat(icons): added `message-circle-dashed-check` icon by
[@​aliyasirnac](https://redirect.github.com/aliyasirnac) in
[#​3678](https://redirect.github.com/lucide-icons/lucide/pull/3678)
- fix(icons): changed `piano` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4766](https://redirect.github.com/lucide-icons/lucide/pull/4766)
- feat(icons): added `credit-card-x` & `credit-card-check` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4763](https://redirect.github.com/lucide-icons/lucide/pull/4763)
- feat(icons): added `credit-card-minus` icon by
[@​ameniti-mx](https://redirect.github.com/ameniti-mx) in
[#​4440](https://redirect.github.com/lucide-icons/lucide/pull/4440)
- feat(icons): added `credit-card-plus` icon by
[@​ameniti-mx](https://redirect.github.com/ameniti-mx) in
[#​4441](https://redirect.github.com/lucide-icons/lucide/pull/4441)
#### New Contributors
- [@​benhaube](https://redirect.github.com/benhaube) made their
first contribution in
[#​4599](https://redirect.github.com/lucide-icons/lucide/pull/4599)
- [@​TFJ5183](https://redirect.github.com/TFJ5183) made their
first contribution in
[#​4762](https://redirect.github.com/lucide-icons/lucide/pull/4762)
- [@​aliyasirnac](https://redirect.github.com/aliyasirnac) made
their first contribution in
[#​3678](https://redirect.github.com/lucide-icons/lucide/pull/3678)
- [@​ameniti-mx](https://redirect.github.com/ameniti-mx) made
their first contribution in
[#​4440](https://redirect.github.com/lucide-icons/lucide/pull/4440)
**Full Changelog**:
<https://github.com/lucide-icons/lucide/compare/1.35.0...1.36.0>
#### What's Changed
- feat(icons): added 'robot-vacuum' icon by
[@​benhaube](https://redirect.github.com/benhaube) in
[#​4599](https://redirect.github.com/lucide-icons/lucide/pull/4599)
- feat(icons): made `face-angry` even more angry by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4708](https://redirect.github.com/lucide-icons/lucide/pull/4708)
- fix(icons): changed `swords` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​4707](https://redirect.github.com/lucide-icons/lucide/pull/4707)
- fix(icons): changed `shopping-cart` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​2909](https://redirect.github.com/lucide-icons/lucide/pull/2909)
- docs(icon-description): added old and empty tags/use case for ghost by
[@​TFJ5183](https://redirect.github.com/TFJ5183) in
[#​4762](https://redirect.github.com/lucide-icons/lucide/pull/4762)
- feat(icons): add `playing-card`, `playing-cards`, and
`playing-cards-fan` by
[@​Barakudum](https://redirect.github.com/Barakudum) in
[#​4258](https://redirect.github.com/lucide-icons/lucide/pull/4258)
- fix(icons): changed `beef` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3457](https://redirect.github.com/lucide-icons/lucide/pull/3457)
- chore(tags): added music disc related tags to `circle-dot` by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3909](https://redirect.github.com/lucide-icons/lucide/pull/3909)
- fix(icons): changed `panda` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3187](https://redirect.github.com/lucide-icons/lucide/pull/3187)
- fix(icons): rotate `blend` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3105](https://redirect.github.com/lucide-icons/lucide/pull/3105)
- fix(icons): rotate `key` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3111](https://redirect.github.com/lucide-icons/lucide/pull/3111)
- fix(icons): changed `square-split-vertical` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3939](https://redirect.github.com/lucide-icons/lucide/pull/3939)
- fix(icons): changed `asterisk` icon by
[@​jguddas](https://redirect.github.com/jguddas) in
[#​3906](https://redirect.github.com/lucide-icons/lucide/pull/3906)
- fix(icons): unify check, cross and plus sizes across icons by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​3875](https://redirect.github.com/lucide-icons/lucide/pull/3875)
- feat(icons): added `message-circle-dashed-check` icon by
[@​aliyasirnac](https://redirect.github.com/aliyasirnac) in
[#​3678](https://redirect.github.com/lucide-icons/lucide/pull/3678)
- fix(icons): changed `piano` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4766](https://redirect.github.com/lucide-icons/lucide/pull/4766)
- feat(icons): added `credit-card-x` & `credit-card-check` icon by
[@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in
[#​4763](https://redirect.github.com/lucide-icons/lucide/pull/4763)
- feat(icons): added `credit-card-minus` icon by
[@​ameniti-mx](https://redirect.github.com/ameniti-mx) in
[#​4440](https://redirect.github.com/lucide-icons/lucide/pull/4440)
- feat(icons): added `credit-card-plus` icon by
[@​ameniti-mx](https://redirect.github.com/ameniti-mx) in
[#​4441](https://redirect.github.com/lucide-icons/lucide/pull/4441)
#### New Contributors
- [@​benhaube](https://redirect.github.com/benhaube) made their
first contribution in
[#​4599](https://redirect.github.com/lucide-icons/lucide/pull/4599)
- [@​TFJ5183](https://redirect.github.com/TFJ5183) made their
first contribution in
[#​4762](https://redirect.github.com/lucide-icons/lucide/pull/4762)
- [@​aliyasirnac](https://redirect.github.com/aliyasirnac) made
their first contribution in
[#​3678](https://redirect.github.com/lucide-icons/lucide/pull/3678)
- [@​ameniti-mx](https://redirect.github.com/ameniti-mx) made
their first contribution in
[#​4440](https://redirect.github.com/lucide-icons/lucide/pull/4440)
**Full Changelog**:
<https://github.com/lucide-icons/lucide/compare/1.35.0...1.36.0>
</details>
<details>
<summary>vercel/next.js (next)</summary>
###
[`v16.3.4`](https://redirect.github.com/vercel/next.js/compare/v16.3.3...v16.3.4)
[Compare
Source](https://redirect.github.com/vercel/next.js/compare/v16.3.3...v16.3.4)
</details>
<details>
<summary>actions/python-versions (python)</summary>
###
[`v3.14.7`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.7-31064857500):
3.14.7
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.14.6-27283001424...3.14.7-31064857500)
Python 3.14.7
###
[`v3.14.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.6-27283001424):
3.14.6
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.14.5-25647354415...3.14.6-27283001424)
Python 3.14.6
###
[`v3.14.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.5-25647354415):
3.14.5
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.14.4-25113653268...3.14.5-25647354415)
Python 3.14.5
###
[`v3.14.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.4-25113653268):
3.14.4
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.14.3-21673711214...3.14.4-25113653268)
Python 3.14.4
###
[`v3.14.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.3-21673711214):
3.14.3
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.14.2-20014991423...3.14.3-21673711214)
Python 3.14.3
###
[`v3.14.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.2-20014991423):
3.14.2
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.14.1-19879739908...3.14.2-20014991423)
Python 3.14.2
###
[`v3.14.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.1-19879739908):
3.14.1
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.14.0-18313368925...3.14.1-19879739908)
Python 3.14.1
###
[`v3.14.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.14.0-18313368925):
3.14.0
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.15-31064747964...3.14.0-18313368925)
Python 3.14.0
###
[`v3.13.15`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.15-31064747964):
3.13.15
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.14-27320626148...3.13.15-31064747964)
Python 3.13.15
###
[`v3.13.14`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.14-27320626148):
3.13.14
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.13-27225391538...3.13.14-27320626148)
Python 3.13.14
###
[`v3.13.13`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.13-27225391538):
3.13.13
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.12-21673645133...3.13.13-27225391538)
Python 3.13.13
###
[`v3.13.12`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.12-21673645133):
3.13.12
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.11-20014977833...3.13.12-21673645133)
Python 3.13.12
###
[`v3.13.11`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.11-20014977833):
3.13.11
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.10-19879712315...3.13.11-20014977833)
Python 3.13.11
###
[`v3.13.10`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.10-19879712315):
3.13.10
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.9-18515951191...3.13.10-19879712315)
Python 3.13.10
###
[`v3.13.9`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.9-18515951191):
3.13.9
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.8-18331000654...3.13.9-18515951191)
Python 3.13.9
###
[`v3.13.8`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.8-18331000654):
3.13.8
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.7-16980743123...3.13.8-18331000654)
Python 3.13.8
###
[`v3.13.7`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.7-16980743123):
3.13.7
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.6-16792117939...3.13.7-16980743123)
Python 3.13.7
###
[`v3.13.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.6-16792117939):
3.13.6
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.5-15601068749...3.13.6-16792117939)
Python 3.13.6
###
[`v3.13.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.5-15601068749):
3.13.5
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.4-15433317575...3.13.5-15601068749)
Python 3.13.5
###
[`v3.13.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.4-15433317575):
3.13.4
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.3-14344076652...3.13.4-15433317575)
Python 3.13.4
###
[`v3.13.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.3-14344076652):
3.13.3
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.2-13708744326...3.13.3-14344076652)
Python 3.13.3
###
[`v3.13.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.2-13708744326):
3.13.2
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.1-13437882550...3.13.2-13708744326)
Python 3.13.2
###
[`v3.13.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.1-13437882550):
3.13.1
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.13.0-13707372259...3.13.1-13437882550)
Python 3.13.1
###
[`v3.13.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.13.0-13707372259):
3.13.0
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.14-31661455385...3.13.0-13707372259)
Python 3.13.0
###
[`v3.12.14`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.14-31661455385):
3.12.14
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.13-27650778726...3.12.14-31661455385)
Python 3.12.14
###
[`v3.12.13`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.13-27650778726):
3.12.13
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.12-18393146713...3.12.13-27650778726)
Python 3.12.13
###
[`v3.12.12`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.12-18393146713):
3.12.12
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.11-15433310049...3.12.12-18393146713)
Python 3.12.12
###
[`v3.12.11`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.11-15433310049):
3.12.11
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.10-14343898437...3.12.11-15433310049)
Python 3.12.11
###
[`v3.12.10`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.10-14343898437):
3.12.10
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.9-13149478207...3.12.10-14343898437)
Python 3.12.10
###
[`v3.12.9`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.9-13149478207):
3.12.9
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.8-12154062663...3.12.9-13149478207)
Python 3.12.9
###
[`v3.12.8`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.8-12154062663):
3.12.8
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.7-11128208086...3.12.8-12154062663)
Python 3.12.8
###
[`v3.12.7`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.7-11128208086):
3.12.7
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.6-10765725458...3.12.7-11128208086)
Python 3.12.7
###
[`v3.12.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.6-10765725458):
3.12.6
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.5-10375840348...3.12.6-10765725458)
Python 3.12.6
###
[`v3.12.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.5-10375840348):
3.12.5
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.4-9947065640...3.12.5-10375840348)
Python 3.12.5
###
[`v3.12.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.4-9947065640):
3.12.4
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.3-11057844995...3.12.4-9947065640)
Python 3.12.4
###
[`v3.12.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.3-11057844995):
3.12.3
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.2-11057786931...3.12.3-11057844995)
Python 3.12.3
###
[`v3.12.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.2-11057786931):
3.12.2
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.1-11057762749...3.12.2-11057786931)
Python 3.12.2
###
[`v3.12.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.1-11057762749):
3.12.1
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.12.0-11057302691...3.12.1-11057762749)
Python 3.12.1
###
[`v3.12.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.12.0-11057302691):
3.12.0
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.16-31661435402...3.12.0-11057302691)
Python 3.12.0
###
[`v3.11.16`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.16-31661435402):
3.11.16
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.15-27649667267...3.11.16-31661435402)
Python 3.11.16
###
[`v3.11.15`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.15-27649667267):
3.11.15
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.14-18393181605...3.11.15-27649667267)
Python 3.11.15
###
[`v3.11.14`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.14-18393181605):
3.11.14
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.13-15433298024...3.11.14-18393181605)
Python 3.11.14
###
[`v3.11.13`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.13-15433298024):
3.11.13
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.12-14343939122...3.11.13-15433298024)
Python 3.11.13
###
[`v3.11.12`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.12-14343939122):
3.11.12
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.11-12160100664...3.11.12-14343939122)
Python 3.11.12
###
[`v3.11.11`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.11-12160100664):
3.11.11
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.10-10765870205...3.11.11-12160100664)
Python 3.11.11
###
[`v3.11.10`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.10-10765870205):
3.11.10
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.9-9947079978...3.11.10-10765870205)
Python 3.11.10
###
[`v3.11.9`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.9-9947079978):
3.11.9
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.8-11113201752...3.11.9-9947079978)
Python 3.11.9
###
[`v3.11.8`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.8-11113201752):
3.11.8
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.7-11113197120...3.11.8-11113201752)
Python 3.11.8
###
[`v3.11.7`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.7-11113197120):
3.11.7
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.6-11113179737...3.11.7-11113197120)
Python 3.11.7
###
[`v3.11.6`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.6-11113179737):
3.11.6
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.5-11113174019...3.11.6-11113179737)
Python 3.11.6
###
[`v3.11.5`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.5-11113174019):
3.11.5
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.4-11113170699...3.11.5-11113174019)
Python 3.11.5
###
[`v3.11.4`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.4-11113170699):
3.11.4
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.3-11059198104...3.11.4-11113170699)
Python 3.11.4
###
[`v3.11.3`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.3-11059198104):
3.11.3
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.2-11059137522...3.11.3-11059198104)
Python 3.11.3
###
[`v3.11.2`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.2-11059137522):
3.11.2
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.1-11058799881...3.11.2-11059137522)
Python 3.11.2
###
[`v3.11.1`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.1-11058799881):
3.11.1
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.11.0-11058707212...3.11.1-11058799881)
Python 3.11.1
###
[`v3.11.0`](https://redirect.github.com/actions/python-versions/releases/tag/3.11.0-11058707212):
3.11.0
[Compare
Source](https://redirect.github.com/actions/python-versions/compare/3.10.21-31661269155...3.11.0-11058707212)
Python 3.11.0
</details>
<details>
<summary>shadcn-ui/ui (shadcn)</summary>
###
[`v4.21.0`](https://redirect.github.com/shadcn-ui/ui/blob/HEAD/packages/shadcn/CHANGELOG.md#4210)
[Compare
Source](https://redirect.github.com/shadcn-ui/ui/compare/shadcn@4.20.1...shadcn@4.21.0)
##### Minor Changes
- [#​11758](https://redirect.github.com/shadcn-ui/ui/pull/11758)
[`c257f688cf4de7ec10cc1be84cad29cd4631182c`](https://redirect.github.com/shadcn-ui/ui/commit/c257f688cf4de7ec10cc1be84cad29cd4631182c)
Thanks [@​shadcn](https://redirect.github.com/shadcn)! - install
`cn` and generate `export { cn } from "cn"` for `lib/utils` on init.
Registry components now import `cn` from the `cn` package.
> ✂ **Note**
>
> PR body was truncated to here.
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 6am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/sleepypod/core).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC42OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNjkuMSIsInRhcmdldEJyYW5jaCI6ImRldiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiLCJmaXgtcGF0Y2giLCJub24tbWFqb3IiXX0=-->
<!-- sleepypod:install-start -->
<!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block.
Hello fellow agent / curious human! Anything between the
sleepypod:install-start and sleepypod:install-end markers
gets overwritten on the next CI run. Want to change what's
here? Edit .github/workflows/build.yml instead. 💤 -->
<details open>
<summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary>
Pick the snippet that matches your pod. **Already-installed pods can't
use the curl bootstraps below** — sleepypod's egress firewall DROPs
github.com, and the script that knows how to unblock WAN is the very
file curl is trying to fetch. Use `sp-update` instead; it's on disk and
opens WAN as its first step.
🔄 **Already on a sleepypod** (most common — review a PR on a running
pod):
```bash
sudo sp-update renovate/all-minor-patch
```
🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every
push:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/renovate/all-minor-patch/scripts/install \
| sudo bash -s -- --branch renovate/all-minor-patch
```
🎯 **Pin to this exact build** ([run
34140743682](https://github.com/sleepypod/core/actions/runs/34140743682)
· `aad5dd8`) — fresh-pod path, locked to one CI artifact for
reproducible review:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/aad5dd83196449cb8511851d423c763ac97701b2/scripts/install \
| sudo bash -s -- \
--branch renovate/all-minor-patch \
--artifact-url 'https://nightly.link/sleepypod/core/actions/runs/34140743682/sleepypod-core.zip'
```
🧊 Artifact:
[`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/34140743682/artifacts/10025822811)
· self-destructs in 30 days 💥
</details>
<!-- sleepypod:install-end -->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
LPS frames on Pod 4 currently produce an unknown-type warning and cannot be requested through an explicit sensor subscription. Recognize `lps` on the server and in the typed client hook, preserving the nested byte payload, channel names, temperature placeholders, and sentinel values. This adds diagnostic access only; no hardware controls, biometric ingestion, calibration, or database behavior changes. Document the reported occupancy trial and outstanding questions. Field JSONL data remains pending; regression fixtures are synthetic. Validation: 1,703 related tests, TypeScript, ESLint, and both Drizzle schema checks passed. Branch Release and Build & Package succeeded for 77f255b. <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update feat/trinity-lps ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/feat/trinity-lps/scripts/install \ | sudo bash -s -- --branch feat/trinity-lps ``` 🎯 **Pin to this exact build** ([run 34166313032](https://github.com/sleepypod/core/actions/runs/34166313032) · `296ad50`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/296ad507f1b81d85eba484cacdc7ef6eedbadb77/scripts/install \ | sudo bash -s -- \ --branch feat/trinity-lps \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/34166313032/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/34166313032/artifacts/10034280217) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for subscribing to the LPS diagnostic sensor stream. - LPS frames are available through sensor-frame access with their raw channel data, byte buffers, and temperature values preserved. - LPS diagnostic subscriptions retain channel labels and support round-trip delivery without affecting other sensor data. - **Documentation** - Added documentation covering LPS frame fields, observed occupancy sequences, and current diagnostic limitations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…#703) A branch without a GitHub release previously made `sp-update` replace the installed app and attempt a full dependency install and Next.js build on the Pod. Updates now require a validated prebuilt bundle and report missing releases, API errors, and failed downloads without falling back to source. Staging and rollback use persistent storage, backups must succeed, and symlinked installation paths are resolved before stale files are removed. From a clone on macOS or Linux, `./scripts/deploy POD_IP [branch]` installs locked dependencies, builds locally with isolated in-memory databases, and uploads a complete archive before invoking `sp-update --archive`. Optional origin branches build in a temporary worktree without changing the user's checkout. The archive retains the standalone runtime and build metadata while excluding environment files, Git metadata, and workstation node_modules. The Pod installs production dependencies for its own architecture. Validation: - Deployed commit `9544003` to Pod 88 through the local build/upload/update path. The running version API reports that exact commit; UI, version API, and OpenAPI return HTTP 200. Firmware connection, sensor frames, and 329 scheduled jobs initialized, with zero service restarts after startup. The Pod's `.env` checksum is unchanged. - Live testing exercised code/database rollback after a failed start and exposed the SSH PATH, build-worker SQLite contention, symlink cleanup, and standalone-runtime issues fixed in this PR. - 24 targeted deployment/firewall tests pass, along with TypeScript, ESLint, both Drizzle schema checks, Bash syntax, and ShellCheck at warning severity. GitHub CI builds and unit tests pass. ESLint has one existing warning in stryker.config.mjs. - CodeRabbit reviewed the initial commit; all five findings were addressed. Its follow-up review of the latest changes was rate-limited for 45 minutes. Limits: the Pod still needs WAN access for production/native dependencies. Rollback restores code/database but does not restore node_modules or module/systemd changes. First-time installations continue to use scripts/install.
Deploying `fix/pod3-frozen-heartbeat` from an upstream clone fails because that branch exists in `Kovbo/core`, while the local deploy script fetches only from `origin`. Add `./scripts/deploy --repo Kovbo/core POD_IP fix/pod3-frozen-heartbeat` to fetch the fork branch into the existing temporary worktree without changing remotes, the current branch, or local edits. `SLEEPYPOD_GITHUB_REPO` selects the same repository; `--repo` takes precedence. Explicit repositories require a branch. With no selector, existing origin/current-checkout behavior is preserved. Validation: 30 targeted deployment/firewall tests, including real Git fixtures where the branch exists only in a separate fork, environment selection, flag precedence, and invalid arguments rejected before SSH. Also ran the CLI against the actual `Kovbo/core` fork with SSH/install stubs: it fetched `f46b3892e577d89a7eb32f1e108422c44aa69c5c` and verified the heartbeat staging change before stopping at dependency installation. Bash syntax and ShellCheck pass. No additional Pod deployment is needed for this source-selection change; the archive/update path is unchanged from the live-tested PR #703.
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`25.9.5` → `25.9.6`](https://renovatebot.com/diffs/npm/@types%2fnode/25.9.5/25.9.6) |  |  | | [@types/react](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react)) | [`19.2.18` → `19.3.0`](https://renovatebot.com/diffs/npm/@types%2freact/19.2.18/19.3.0) |  |  | | [@types/react-dom](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dom) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom)) | [`19.2.7` → `19.3.0`](https://renovatebot.com/diffs/npm/@types%2freact-dom/19.2.7/19.3.0) |  |  | | [lucide-react](https://lucide.dev) ([source](https://redirect.github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react)) | [`1.41.0` → `1.44.0`](https://renovatebot.com/diffs/npm/lucide-react/1.41.0/1.44.0) |  |  | | [react](https://react.dev/) ([source](https://redirect.github.com/react/react/tree/HEAD/packages/react)) | [`19.2.8` → `19.3.0`](https://renovatebot.com/diffs/npm/react/19.2.8/19.3.0) |  |  | | [react-dom](https://react.dev/) ([source](https://redirect.github.com/react/react/tree/HEAD/packages/react-dom)) | [`19.2.8` → `19.3.0`](https://renovatebot.com/diffs/npm/react-dom/19.2.8/19.3.0) |  |  | | [typescript-eslint](https://typescript-eslint.io/packages/typescript-eslint) ([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint)) | [`8.69.0` → `8.70.0`](https://renovatebot.com/diffs/npm/typescript-eslint/8.69.0/8.70.0) |  |  | | [zod](https://zod.dev) ([source](https://redirect.github.com/colinhacks/zod)) | [`4.5.4` → `4.6.2`](https://renovatebot.com/diffs/npm/zod/4.5.4/4.6.2) |  |  | --- ### Release Notes <details> <summary>lucide-icons/lucide (lucide-react)</summary> ### [`v1.44.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.44.0): Version 1.44.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/1.43.0...1.44.0) #### What's Changed - feat(packages/icons): fixed [@​lucide/icons](https://redirect.github.com/lucide/icons) rollup config by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4824](https://redirect.github.com/lucide-icons/lucide/pull/4824) - fix(icons): changed `door-open` by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4826](https://redirect.github.com/lucide-icons/lucide/pull/4826) - fix(docs): replace missing LucideIcon icon names in guides by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4827](https://redirect.github.com/lucide-icons/lucide/pull/4827) - feat(docs): fixed fuse js search by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4825](https://redirect.github.com/lucide-icons/lucide/pull/4825) - fix(icons): changed `satellite-dish` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4813](https://redirect.github.com/lucide-icons/lucide/pull/4813) - fix(icons): arcified flip icons & renamed them by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4833](https://redirect.github.com/lucide-icons/lucide/pull/4833) - fix(icons): improve legibility of credit card icons by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4831](https://redirect.github.com/lucide-icons/lucide/pull/4831) - feat(lab): add check-x icon by [@​ishanbagra18](https://redirect.github.com/ishanbagra18) in [#​4737](https://redirect.github.com/lucide-icons/lucide/pull/4737) - fix(icons): correct compromised tags in shield icons by [@​decknamec](https://redirect.github.com/decknamec) in [#​4835](https://redirect.github.com/lucide-icons/lucide/pull/4835) - feat(icons): added `toothbrush` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4755](https://redirect.github.com/lucide-icons/lucide/pull/4755) #### New Contributors - [@​ishanbagra18](https://redirect.github.com/ishanbagra18) made their first contribution in [#​4737](https://redirect.github.com/lucide-icons/lucide/pull/4737) - [@​decknamec](https://redirect.github.com/decknamec) made their first contribution in [#​4835](https://redirect.github.com/lucide-icons/lucide/pull/4835) **Full Changelog**: <lucide-icons/lucide@1.43.0...1.44.0> ### [`v1.43.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.43.0): Version 1.43.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/1.42.0...1.43.0) #### What's Changed - feat(icons): added `tic-tac-toe` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4772](https://redirect.github.com/lucide-icons/lucide/pull/4772) - fix(icons): changed `id-card` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4820](https://redirect.github.com/lucide-icons/lucide/pull/4820) - fix(icons): changed `id-card-lanyard` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4819](https://redirect.github.com/lucide-icons/lucide/pull/4819) - feat(icons): delegate `carton`/`carton-off` from lab by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4818](https://redirect.github.com/lucide-icons/lucide/pull/4818) **Full Changelog**: <lucide-icons/lucide@1.42.0...1.43.0> ### [`v1.42.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.42.0): Version 1.42.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/1.41.0...1.42.0) #### What's Changed - feat(docs): added better contribution guide by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4746](https://redirect.github.com/lucide-icons/lucide/pull/4746) - fix(site): fix HomeHeroIconsCard.data.ts by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4805](https://redirect.github.com/lucide-icons/lucide/pull/4805) - fix(icons): deprecated swiss franc icons by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4799](https://redirect.github.com/lucide-icons/lucide/pull/4799) - feat(icons): add gap-horizontal and gap-vertical by [@​samuelalake](https://redirect.github.com/samuelalake) in [#​4544](https://redirect.github.com/lucide-icons/lucide/pull/4544) - feat(icons): added `trash-off` icon by [@​lx3133584](https://redirect.github.com/lx3133584) in [#​4788](https://redirect.github.com/lucide-icons/lucide/pull/4788) - feat(icons): added `circle-dashed-check` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4796](https://redirect.github.com/lucide-icons/lucide/pull/4796) - feat(icons): added `equal-approximately-not` icon by [@​ryck](https://redirect.github.com/ryck) in [#​4802](https://redirect.github.com/lucide-icons/lucide/pull/4802) - feat(icons): added dome icons by [@​swastik7805](https://redirect.github.com/swastik7805) in [#​4261](https://redirect.github.com/lucide-icons/lucide/pull/4261) - chore(lucide-react): Add Lucide React integration tests by [@​ericfennis](https://redirect.github.com/ericfennis) in [#​4804](https://redirect.github.com/lucide-icons/lucide/pull/4804) - feat(packages): extract icon build logic into `@lucide/shared` by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4409](https://redirect.github.com/lucide-icons/lucide/pull/4409) - fix(icons): changed `computer` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4607](https://redirect.github.com/lucide-icons/lucide/pull/4607) - chore(dependencies): Update dependencies by [@​ericfennis](https://redirect.github.com/ericfennis) with [@​Copilot](https://redirect.github.com/Copilot) in [#​4806](https://redirect.github.com/lucide-icons/lucide/pull/4806) - fix(icons): changed `table-2` icon by [@​jguddas](https://redirect.github.com/jguddas) in [#​4810](https://redirect.github.com/lucide-icons/lucide/pull/4810) - feat(icons): added `user-group` icons by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4782](https://redirect.github.com/lucide-icons/lucide/pull/4782) #### New Contributors - [@​lx3133584](https://redirect.github.com/lx3133584) made their first contribution in [#​4788](https://redirect.github.com/lucide-icons/lucide/pull/4788) - [@​ryck](https://redirect.github.com/ryck) made their first contribution in [#​4802](https://redirect.github.com/lucide-icons/lucide/pull/4802) **Full Changelog**: <lucide-icons/lucide@1.41.0...1.42.0> </details> <details> <summary>react/react (react)</summary> ### [`v19.3.0`](https://redirect.github.com/react/react/compare/v19.2.8...1d34f91dfde6bba84d08b683aaba164c7194dacb) [Compare Source](https://redirect.github.com/react/react/compare/v19.2.8...v19.3.0) </details> <details> <summary>react/react (react-dom)</summary> ### [`v19.3.0`](https://redirect.github.com/react/react/compare/v19.2.8...1d34f91dfde6bba84d08b683aaba164c7194dacb) [Compare Source](https://redirect.github.com/react/react/compare/v19.2.8...v19.3.0) </details> <details> <summary>typescript-eslint/typescript-eslint (typescript-eslint)</summary> ### [`v8.70.0`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/typescript-eslint/CHANGELOG.md#8700-2026-09-07) [Compare Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.69.0...v8.70.0) ##### 🩹 Fixes - **eslint-plugin:** \[no-deprecated] report deprecated imported values used in object shorthand properties ([#​12780](https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12780)) ##### ❤️ Thank You - Ulrich Stark [@​ulrichstark](https://redirect.github.com/ulrichstark) See [GitHub Releases](https://redirect.github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.0) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. </details> <details> <summary>colinhacks/zod (zod)</summary> ### [`v4.6.2`](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.2) [Compare Source](https://redirect.github.com/colinhacks/zod/compare/v4.6.1...v4.6.2) #### Commits: - [`a00c3f3`](https://redirect.github.com/colinhacks/zod/commit/a00c3f348abb8b56ccb6281ac4c946141e2728d5) docs: use Trigger.dev's brand-kit lockups for the platinum card - [`0c483c5`](https://redirect.github.com/colinhacks/zod/commit/0c483c58849fdb6445aea4f54b1bac6b57ab3d22) docs: Zod 4.6 announcement post ([#​6546](https://redirect.github.com/colinhacks/zod/issues/6546)) - [`9446b5c`](https://redirect.github.com/colinhacks/zod/commit/9446b5cc14c5bf137790f1f66abf602044871223) fix: preserve undefined prefault outputs and object keys ([#​6587](https://redirect.github.com/colinhacks/zod/issues/6587)) - [`e359f73`](https://redirect.github.com/colinhacks/zod/commit/e359f7378fe56d695134701cda1e9055a08892dc) 4.6.2 ### [`v4.6.1`](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.1) [Compare Source](https://redirect.github.com/colinhacks/zod/compare/v4.6.0...v4.6.1) A patch on top of [4.6.0](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.0). - [`b12aa523`](https://redirect.github.com/colinhacks/zod/commit/b12aa523) fix: preserve unique tags with defaulted discriminators ([#​6582](https://redirect.github.com/colinhacks/zod/pull/6582)) — closes [#​6577](https://redirect.github.com/colinhacks/zod/issues/6577) - [`dd9c36fa`](https://redirect.github.com/colinhacks/zod/commit/dd9c36fa) fix(v4): defer recursive object index inference ([#​6580](https://redirect.github.com/colinhacks/zod/pull/6580)) - [`3b154992`](https://redirect.github.com/colinhacks/zod/commit/3b154992) feat(lang): add Tajik (`tg`) locale ([#​6581](https://redirect.github.com/colinhacks/zod/pull/6581)) by [@​ismoil77](https://redirect.github.com/ismoil77) - [`2efa8b80`](https://redirect.github.com/colinhacks/zod/commit/2efa8b80) ci: give the npm wait a real budget and drop the back-publish path ([#​6583](https://redirect.github.com/colinhacks/zod/pull/6583)) ### [`v4.6.0`](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.0) [Compare Source](https://redirect.github.com/colinhacks/zod/compare/v4.5.4...v4.6.0) #### Commits: - [`908c9e1`](https://redirect.github.com/colinhacks/zod/commit/908c9e1771befea20962b6eb2ea591983f45d725) fix(docs): retry the GitHub stars fetch and log the real status - [`93186ca`](https://redirect.github.com/colinhacks/zod/commit/93186cab5340fba6ed9b91768628099cd5801495) docs(wiki): drop the zod-compiler benchmark ([#​6505](https://redirect.github.com/colinhacks/zod/issues/6505)) - [`8ce9e8d`](https://redirect.github.com/colinhacks/zod/commit/8ce9e8d56b1f408c445db6f2d7542696c6d20326) feat(mini): publish Zod Mini as the standalone [@​zod/mini](https://redirect.github.com/zod/mini) package ([#​6491](https://redirect.github.com/colinhacks/zod/issues/6491)) - [`2956c4c`](https://redirect.github.com/colinhacks/zod/commit/2956c4c2fc5af83408368907e88a3bd20e8af12e) chore(mini): sync [@​zod/mini](https://redirect.github.com/zod/mini) to 4.5.4 - [`abd41ad`](https://redirect.github.com/colinhacks/zod/commit/abd41adbedbe92ade811809b2cea94cea60103d0) docs(wiki): move plans and comparisons into a gitignored internal/ ([#​6506](https://redirect.github.com/colinhacks/zod/issues/6506)) - [`8106573`](https://redirect.github.com/colinhacks/zod/commit/810657394de9df48744463beb21e103a3ad04eef) chore(docs): build with Turbopack - [`c7ec94d`](https://redirect.github.com/colinhacks/zod/commit/c7ec94d309ee8acb25113e3db738b3e03816a424) ci: check zod and [@​zod/mini](https://redirect.github.com/zod/mini) lockstep on npm after every publish ([#​6507](https://redirect.github.com/colinhacks/zod/issues/6507)) - [`c46eeff`](https://redirect.github.com/colinhacks/zod/commit/c46eeff050f2a6a2ba8db1d14c2c8247bd7623f2) chore: narrow blanket biome-ignore comments ([#​6504](https://redirect.github.com/colinhacks/zod/issues/6504)) - [`a0898b4`](https://redirect.github.com/colinhacks/zod/commit/a0898b4b7ff35b056b9c773b8b3ec629d413ac53) ci: wait hours for npm to serve a publish, not ten minutes ([#​6502](https://redirect.github.com/colinhacks/zod/issues/6502)) - [`1c0bce0`](https://redirect.github.com/colinhacks/zod/commit/1c0bce0c363e5fbd4589669ae89884fd2df77d14) docs: bring the 4.5 charts and worked examples into the docs pages - [`70eb2c0`](https://redirect.github.com/colinhacks/zod/commit/70eb2c0722c98aa0c81bcb68693eda2c668c1f86) docs: drop the traits section and the compilation feature bullet - [`43b9bfc`](https://redirect.github.com/colinhacks/zod/commit/43b9bfc5a59167b42c897eb9c74129fc8b4c31e4) docs: drop the bound-methods section from the Zod package page - [`cd4f9a6`](https://redirect.github.com/colinhacks/zod/commit/cd4f9a679238a5470620f74c2cc568e8865b3f01) perf(v4): report Standard Schema issues without constructing a ZodError ([#​6509](https://redirect.github.com/colinhacks/zod/issues/6509)) - [`f3cb364`](https://redirect.github.com/colinhacks/zod/commit/f3cb3644eb0b3b14b9e8e78b4e04ef221d6ef169) docs: surface the blog on the home page and in the sidebar - [`f3e7c72`](https://redirect.github.com/colinhacks/zod/commit/f3e7c72e8ce66bb1d5113aeb5e1c0c658981d2f7) fix(docs): render the docs 404 page inside the (doc) layout once - [`f412178`](https://redirect.github.com/colinhacks/zod/commit/f412178defd4348872fa581db952f6713677502a) ci: publish [@​zod/mini](https://redirect.github.com/zod/mini) to JSR in lockstep with npm ([#​6510](https://redirect.github.com/colinhacks/zod/issues/6510)) - [`5ff9566`](https://redirect.github.com/colinhacks/zod/commit/5ff9566508e6c95873d2648a5bdcc3a371f1b757) Stop re-exporting the compile internals from zod/v4/core ([#​6511](https://redirect.github.com/colinhacks/zod/issues/6511)) - [`40b4d0b`](https://redirect.github.com/colinhacks/zod/commit/40b4d0b34d09b151505cf726581c908172368e35) fix(ci): read zod's latest version with npm view when picking the backfill dist-tag - [`a6b4939`](https://redirect.github.com/colinhacks/zod/commit/a6b493907066003e28f8b7e967faf4d19f2174f4) Mark the compile internals [@​internal](https://redirect.github.com/internal) instead of hiding them ([#​6518](https://redirect.github.com/colinhacks/zod/issues/6518)) - [`3195ed0`](https://redirect.github.com/colinhacks/zod/commit/3195ed01073d2156fd750effa60c191d2fa048d4) docs: label the memory chart like the compile chart - [`8cd1250`](https://redirect.github.com/colinhacks/zod/commit/8cd1250f0e8e54ee8d269d69b1ee93357b661886) docs: center the memory chart callout labels - [`ff56a55`](https://redirect.github.com/colinhacks/zod/commit/ff56a551b5665aeead40936a8f941d08185109f4) docs: center the memory chart callout labels and pad them off the number - [`fb2fedf`](https://redirect.github.com/colinhacks/zod/commit/fb2fedfd98abd0203626b0d07b114739dca01e8a) docs: tighten the memory chart callout, pad the canvas, say "less memory" - [`93f3ab3`](https://redirect.github.com/colinhacks/zod/commit/93f3ab321211f52f1963b63d5d3503c645c38371) docs: replace the blog navbar's GitHub icon with a star-count pill - [`7ae49d6`](https://redirect.github.com/colinhacks/zod/commit/7ae49d64d5fafee675049cd3dc44e98a41c27e2f) docs: drop the circle around the star pill's GitHub mark and center it on the pill's arc - [`b801439`](https://redirect.github.com/colinhacks/zod/commit/b801439b5fb160d651084ff7d7c27a61e24a7334) bench: add typebox (compiled and dynamic) to the moltar cross-library harness - [`1ec6b7c`](https://redirect.github.com/colinhacks/zod/commit/1ec6b7c5ec58d0e58f6b582c96e7cabfc5cb2f95) docs: add an RSS feed to the blog at /blog/rss.xml - [`08ba069`](https://redirect.github.com/colinhacks/zod/commit/08ba069e29d67b2f7b13b98dc9e70b1d99aeab5d) perf(v4): read Luhn digits with charCodeAt instead of string indexing ([#​6529](https://redirect.github.com/colinhacks/zod/issues/6529)) - [`319f47f`](https://redirect.github.com/colinhacks/zod/commit/319f47f44f40755773a9aa83b6fdaaad1ff01d77) Emit a length-aware base64url pattern in toJSONSchema ([#​6527](https://redirect.github.com/colinhacks/zod/issues/6527)) - [`a2a019a`](https://redirect.github.com/colinhacks/zod/commit/a2a019a57e4444f61bf677ca79c1352823e2b7c8) Accept enum-typed targets in z.toZod ([#​6528](https://redirect.github.com/colinhacks/zod/issues/6528)) - [`74f9a6d`](https://redirect.github.com/colinhacks/zod/commit/74f9a6d3f2818f24e6df5394b8b6e60acb176463) docs: drop the toZod enum block from basics and pin the page's curation rule in a comment - [`f83ab51`](https://redirect.github.com/colinhacks/zod/commit/f83ab51129038a2f868ee11dda906ed78ae2e478) fix(v4): reject component-only strings from z.emoji() ([#​6532](https://redirect.github.com/colinhacks/zod/issues/6532)) - [`84dd3b0`](https://redirect.github.com/colinhacks/zod/commit/84dd3b0fc6cb5a24f773cb6db66bc934338b44ea) perf: build literal and enum pattern regexes lazily ([#​6531](https://redirect.github.com/colinhacks/zod/issues/6531)) - [`0227e53`](https://redirect.github.com/colinhacks/zod/commit/0227e53d03b5652be75422b6d10e0dd9bf340f0b) docs: bump the star pill's GitHub mark to 20px - [`68a609a`](https://redirect.github.com/colinhacks/zod/commit/68a609ac9b9516e6fe84e3558f17ce16b1032bef) Widen literal inputs in property check types ([#​6520](https://redirect.github.com/colinhacks/zod/issues/6520)) - [`2ec972e`](https://redirect.github.com/colinhacks/zod/commit/2ec972ecc5a8af8135ed072d2d5b5363c27bb46f) refactor: collapse toZod's enum leaf normalizer to a dummy union ([#​6533](https://redirect.github.com/colinhacks/zod/issues/6533)) - [`bc1157e`](https://redirect.github.com/colinhacks/zod/commit/bc1157e717c69168ab0b0d0ace27149f6dc3c138) docs: use a Response example for z.properties() - [`07c43e2`](https://redirect.github.com/colinhacks/zod/commit/07c43e2a08f67082bd35c158d528d571a36094e0) Keep the runtime base64 regexes linear so composed parse paths cannot overflow ([#​6534](https://redirect.github.com/colinhacks/zod/issues/6534)) - [`bec73be`](https://redirect.github.com/colinhacks/zod/commit/bec73bea5a8ed19daec15655598fe5501bb797e7) perf(v4): build the safeParse error on first read ([#​6519](https://redirect.github.com/colinhacks/zod/issues/6519)) - [`bf99021`](https://redirect.github.com/colinhacks/zod/commit/bf9902164b5396449540415c2221a24bc9fd0e22) perf: move util.cached's accessor to a prototype ([#​6537](https://redirect.github.com/colinhacks/zod/issues/6537)) - [`69f2a7f`](https://redirect.github.com/colinhacks/zod/commit/69f2a7ff203e546e3a1350fb9152ace03ba0a6e6) Collapse toZod's normalizer and move its docs to the API reference ([#​6539](https://redirect.github.com/colinhacks/zod/issues/6539)) - [`abfb389`](https://redirect.github.com/colinhacks/zod/commit/abfb38977dc28dc9993e3fa6a2ecc2f52a8d0c4c) feat(v4): make z.properties() a schema, and give z.instanceof() a .properties() method ([#​6536](https://redirect.github.com/colinhacks/zod/issues/6536)) - [`51caf01`](https://redirect.github.com/colinhacks/zod/commit/51caf01038d3339d7b9db94b959482c549aba2e3) refactor: collapse cachedInternal back into cached ([#​6540](https://redirect.github.com/colinhacks/zod/issues/6540)) - [`81ded99`](https://redirect.github.com/colinhacks/zod/commit/81ded9918fe39d51b232be72f23ba0d5fb6a837e) perf: answer z.validate from the compiled fast path on invalid input ([#​6538](https://redirect.github.com/colinhacks/zod/issues/6538)) - [`eca9687`](https://redirect.github.com/colinhacks/zod/commit/eca96871536603076bdd00b8779b947144133aa4) fix(v4): enforce the six JSON Schema keywords fromJSONSchema silently dropped ([#​6535](https://redirect.github.com/colinhacks/zod/issues/6535)) - [`68aca3d`](https://redirect.github.com/colinhacks/zod/commit/68aca3dc402c59f5c4e30322e83028389e44b8b5) docs: cover the 4.5 API surface that never made it into the reference - [`18e71c7`](https://redirect.github.com/colinhacks/zod/commit/18e71c71daff6e79ab8f00a48db252ccb340da1f) Rename the JSON Schema `process` helper so bundler polyfills cannot collide ([#​6541](https://redirect.github.com/colinhacks/zod/issues/6541)) - [`90269c6`](https://redirect.github.com/colinhacks/zod/commit/90269c601aae25499ee87e78a07508564825867f) Keep a numeric TS enum's reverse-mapping keys out of `.options` ([#​6542](https://redirect.github.com/colinhacks/zod/issues/6542)) - [`4d73088`](https://redirect.github.com/colinhacks/zod/commit/4d7308828b1a56768741d7cfd6cf476eaead45af) Release the parsed input once a failing safeParse builds its error ([#​6543](https://redirect.github.com/colinhacks/zod/issues/6543)) - [`cafbee4`](https://redirect.github.com/colinhacks/zod/commit/cafbee4772ef481611fef6f5fb9ed9a1b9597069) fix(v4): parse recursive schemas built by a factory ([#​6530](https://redirect.github.com/colinhacks/zod/issues/6530)) - [`62e6624`](https://redirect.github.com/colinhacks/zod/commit/62e6624bc72c4882968ac4ea2c4281b1a99a7ad4) feat(v4): add .validate() and .validateAsync() to Zod Classic ([#​6547](https://redirect.github.com/colinhacks/zod/issues/6547)) - [`07917f4`](https://redirect.github.com/colinhacks/zod/commit/07917f4cb396646dff7a1364f9032a668b0d7eb4) test(v4): pin the lazy safeParse error's stack behavior ([#​6548](https://redirect.github.com/colinhacks/zod/issues/6548)) - [`764ac59`](https://redirect.github.com/colinhacks/zod/commit/764ac59f1afc1a46e7ecc2d5e8e86c57b7d66164) perf(v4): settle z.validate on the first failure in parse order ([#​6544](https://redirect.github.com/colinhacks/zod/issues/6544)) - [`e760471`](https://redirect.github.com/colinhacks/zod/commit/e7604717801fe5cc17c525fb92abe0abeae0ae6e) docs: attribute the compiled failure cost to the fallback, not the double pass - [`5489a53`](https://redirect.github.com/colinhacks/zod/commit/5489a532e65ee3f5ab0959ff8a5da346ced96f2a) test(v4): pin the check-chain case that keeps compiled validate's definite guard ([#​6551](https://redirect.github.com/colinhacks/zod/issues/6551)) - [`7a00236`](https://redirect.github.com/colinhacks/zod/commit/7a00236683c79000dbab0d92f6faf0b7fba39f59) fix(v4): don't let format checks overwrite tighter min/max bounds ([#​6553](https://redirect.github.com/colinhacks/zod/issues/6553)) - [`e4d67f3`](https://redirect.github.com/colinhacks/zod/commit/e4d67f3ea42de031f1f644a1efd7bd0a306dde56) Migrate development and CI to Nub ([#​6562](https://redirect.github.com/colinhacks/zod/issues/6562)) - [`6f04836`](https://redirect.github.com/colinhacks/zod/commit/6f0483671e622d5d01a1e17f69af0825137c7830) fix(v4): derive JSON Schema constraints by folding checks in the converter ([#​6554](https://redirect.github.com/colinhacks/zod/issues/6554)) - [`804e0f5`](https://redirect.github.com/colinhacks/zod/commit/804e0f522747345d6b37581888899be420baa3e9) perf: seal the CommonJS exports so require("zod") stops reading through a getter ([#​6564](https://redirect.github.com/colinhacks/zod/issues/6564)) - [`741981f`](https://redirect.github.com/colinhacks/zod/commit/741981ffdbc8772cffc38cc4eaf84d5a45fa7384) perf(compile): for-in record walk, cheaper issue finalization, and a generative compile differential ([#​6567](https://redirect.github.com/colinhacks/zod/issues/6567)) - [`eb1c108`](https://redirect.github.com/colinhacks/zod/commit/eb1c1089f7f9469078839f6e443830f7e3fdc06b) ci: release only on workflow\_dispatch behind the npm environment ([#​6569](https://redirect.github.com/colinhacks/zod/issues/6569)) - [`277613a`](https://redirect.github.com/colinhacks/zod/commit/277613a61a11f299d9897ddd88490002a995f746) docs: move the release procedure to the maintainer-local notes - [`dcbcf05`](https://redirect.github.com/colinhacks/zod/commit/dcbcf0529ca1e5424541d5d4b58e0a7b79507f51) fix(compile): unwind the doc indent when a child generator throws ([#​6570](https://redirect.github.com/colinhacks/zod/issues/6570)) - [`e54716c`](https://redirect.github.com/colinhacks/zod/commit/e54716cb5676f812cefea686eb07e2df961a0cf8) docs(ecosystem): add [@​apical-ts/craft](https://redirect.github.com/apical-ts/craft) ([#​5946](https://redirect.github.com/colinhacks/zod/issues/5946)) - [`c5b9bcb`](https://redirect.github.com/colinhacks/zod/commit/c5b9bcb39b7953c6a984849c6527863acdae0af7) bench: measure what a runtime island's leaked indent cost the generated source - [`22bed61`](https://redirect.github.com/colinhacks/zod/commit/22bed613281143efa3815fe26ab666c6c56586af) feat(v4): add z.iban() string format with mod-97 checksum ([#​6571](https://redirect.github.com/colinhacks/zod/issues/6571)) - [`36f1796`](https://redirect.github.com/colinhacks/zod/commit/36f17960d1defca5d0896d9424f4e1059fbbf081) fix(v4): stop the memoizer from pinning a finished parse ([#​6572](https://redirect.github.com/colinhacks/zod/issues/6572)) - [`f7fd554`](https://redirect.github.com/colinhacks/zod/commit/f7fd5548063f55a74847b2711630c9b3f8ea00d4) perf(v4): drop the lookaheads from the email regex ([#​6573](https://redirect.github.com/colinhacks/zod/issues/6573)) - [`f9465d4`](https://redirect.github.com/colinhacks/zod/commit/f9465d4e6497f92fe85ef7118ea64d7f86c28676) docs: reconcile the sponsor listings with active sponsorships ([#​6576](https://redirect.github.com/colinhacks/zod/issues/6576)) - [`213ee75`](https://redirect.github.com/colinhacks/zod/commit/213ee75d2ad6e41508587c3fab8246967f006993) feat(compile): add z.withParser for externally generated parsers ([#​6575](https://redirect.github.com/colinhacks/zod/issues/6575)) - [`6de10dc`](https://redirect.github.com/colinhacks/zod/commit/6de10dce1afaba7160d1c7ca810cc40bc4a7a733) docs: reconcile the sponsor listings against every active sponsorship ([#​6579](https://redirect.github.com/colinhacks/zod/issues/6579)) - [`661673a`](https://redirect.github.com/colinhacks/zod/commit/661673ae3ed42b15e8f835ae1bd0223b1db044a4) docs: make the 9thCO logo visible on the light theme - [`1c51cbe`](https://redirect.github.com/colinhacks/zod/commit/1c51cbe0fe23d09f8d520b31487d50a01588fae5) 4.6.0 - [`3b15499`](https://redirect.github.com/colinhacks/zod/commit/3b1549922b56ec17c9c47a521963f820930068e1) feat(lang): add Tajik (`tg`) locale - [`c532d76`](https://redirect.github.com/colinhacks/zod/commit/c532d766de54e13aea5d03698980bf7d976fc544) test(locales): cover Tajik error branches - [`574d480`](https://redirect.github.com/colinhacks/zod/commit/574d480fb443d3fd396efbcb8230f3dfee4a2cd2) fix(locales): clarify Tajik discriminator value message - [`dd9c36f`](https://redirect.github.com/colinhacks/zod/commit/dd9c36fa2ddcee79f71d19f9635586c7b65ead86) fix(v4): defer recursive object index inference ([#​6580](https://redirect.github.com/colinhacks/zod/issues/6580)) - [`b12aa52`](https://redirect.github.com/colinhacks/zod/commit/b12aa523e7e2617c4296cccf9b24d6558ed23e95) fix: preserve unique tags with defaulted discriminators ([#​6582](https://redirect.github.com/colinhacks/zod/issues/6582)) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "before 6am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/sleepypod/core). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzkuMSIsInRhcmdldEJyYW5jaCI6ImRldiIsImxhYmVscyI6WyJhdXRvLW1lcmdlIiwiZGVwZW5kZW5jaWVzIiwiZGV2RGVwZW5kZW5jaWVzIiwiZml4LXBhdGNoIiwibm9uLW1ham9yIl19--> <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update renovate/all-minor-patch ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/renovate/all-minor-patch/scripts/install \ | sudo bash -s -- --branch renovate/all-minor-patch ``` 🎯 **Pin to this exact build** ([run 34806385321](https://github.com/sleepypod/core/actions/runs/34806385321) · `c0ebe1d`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/c0ebe1d1079c8d3a9038f79ff04f74f3f4a5b0ad/scripts/install \ | sudo bash -s -- \ --branch renovate/all-minor-patch \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/34806385321/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/34806385321/artifacts/10332059610) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…encies (#711) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@formatjs/intl-localematcher](https://redirect.github.com/formatjs/formatjs#readme) | [`^0.8.0` → `^0.9.0`](https://renovatebot.com/diffs/npm/@formatjs%2fintl-localematcher/0.8.13/0.9.0) |  |  | | [@tanstack/react-query](https://tanstack.com/query) ([source](https://redirect.github.com/TanStack/query/tree/HEAD/packages/react-query)) | [`5.102.8` → `5.103.1`](https://renovatebot.com/diffs/npm/@tanstack%2freact-query/5.102.8/5.103.1) |  |  | | [@testing-library/dom](https://redirect.github.com/testing-library/dom-testing-library) | [`10.4.1` → `10.4.2`](https://renovatebot.com/diffs/npm/@testing-library%2fdom/10.4.1/10.4.2) |  |  | | [@trpc/client](https://trpc.io) ([source](https://redirect.github.com/trpc/trpc/tree/HEAD/packages/client)) | [`11.18.0` → `11.19.0`](https://renovatebot.com/diffs/npm/@trpc%2fclient/11.18.0/11.19.0) |  |  | | [@trpc/next](https://trpc.io) ([source](https://redirect.github.com/trpc/trpc/tree/HEAD/packages/next)) | [`11.18.0` → `11.19.0`](https://renovatebot.com/diffs/npm/@trpc%2fnext/11.18.0/11.19.0) |  |  | | [@trpc/react-query](https://trpc.io) ([source](https://redirect.github.com/trpc/trpc/tree/HEAD/packages/react)) | [`11.18.0` → `11.19.0`](https://renovatebot.com/diffs/npm/@trpc%2freact-query/11.18.0/11.19.0) |  |  | | [@trpc/server](https://trpc.io) ([source](https://redirect.github.com/trpc/trpc/tree/HEAD/packages/server)) | [`11.18.0` → `11.19.0`](https://renovatebot.com/diffs/npm/@trpc%2fserver/11.18.0/11.19.0) |  |  | | [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`25.9.6` → `25.9.7`](https://renovatebot.com/diffs/npm/@types%2fnode/25.9.6/25.9.7) |  |  | | [eslint-config-next](https://nextjs.org/docs/app/api-reference/config/eslint) ([source](https://redirect.github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next)) | [`16.3.4` → `16.3.5`](https://renovatebot.com/diffs/npm/eslint-config-next/16.3.4/16.3.5) |  |  | | [lucide-react](https://lucide.dev) ([source](https://redirect.github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react)) | [`1.44.0` → `1.47.0`](https://renovatebot.com/diffs/npm/lucide-react/1.44.0/1.47.0) |  |  | | [mqtt](https://redirect.github.com/mqttjs/MQTT.js) | [`5.15.2` → `5.16.0`](https://renovatebot.com/diffs/npm/mqtt/5.15.2/5.16.0) |  |  | | [next](https://nextjs.org) ([source](https://redirect.github.com/vercel/next.js)) | [`16.3.4` → `16.3.5`](https://renovatebot.com/diffs/npm/next/16.3.4/16.3.5) |  |  | | [tailwind-merge](https://redirect.github.com/dcastil/tailwind-merge) ([source](https://redirect.github.com/dcastil/tailwind-merge/tree/HEAD/packages/tailwind-merge)) | [`3.6.0` → `3.7.0`](https://renovatebot.com/diffs/npm/tailwind-merge/3.6.0/3.7.0) |  |  | | [zod](https://zod.dev) ([source](https://redirect.github.com/colinhacks/zod)) | [`4.6.2` → `4.6.5`](https://renovatebot.com/diffs/npm/zod/4.6.2/4.6.5) |  |  | --- ### Release Notes <details> <summary>TanStack/query (@​tanstack/react-query)</summary> ### [`v5.103.1`](https://redirect.github.com/TanStack/query/blob/HEAD/packages/react-query/CHANGELOG.md#51031) [Compare Source](https://redirect.github.com/TanStack/query/compare/@tanstack/react-query@5.103.0...@tanstack/react-query@5.103.1) ##### Patch Changes - Updated dependencies \[[`8330b2f`](https://redirect.github.com/TanStack/query/commit/8330b2f2d431f226d7bb95eb727c637be9d4c6d8), [`3212966`](https://redirect.github.com/TanStack/query/commit/3212966a3681e02d14eb79e9cce0d3265b046877)]: - [@​tanstack/query-core](https://redirect.github.com/tanstack/query-core)@5.103.1 ### [`v5.103.0`](https://redirect.github.com/TanStack/query/blob/HEAD/packages/react-query/CHANGELOG.md#51030) [Compare Source](https://redirect.github.com/TanStack/query/compare/@tanstack/react-query@5.102.8...@tanstack/react-query@5.103.0) ##### Patch Changes - Updated dependencies \[[`e57f816`](https://redirect.github.com/TanStack/query/commit/e57f8163b18ab3abfe52ad69cdbdda4d31453393), [`fdae2ce`](https://redirect.github.com/TanStack/query/commit/fdae2ce4e5382af7326c7c38ce5d5a12751ada12), [`a1119e5`](https://redirect.github.com/TanStack/query/commit/a1119e5a3ffa52534de7390f17c7183d17658051), [`cbf77bf`](https://redirect.github.com/TanStack/query/commit/cbf77bf8e1a9ce09caef0cdd9a8ed10fef57edf0)]: - [@​tanstack/query-core](https://redirect.github.com/tanstack/query-core)@5.103.0 </details> <details> <summary>testing-library/dom-testing-library (@​testing-library/dom)</summary> ### [`v10.4.2`](https://redirect.github.com/testing-library/dom-testing-library/releases/tag/v10.4.2) [Compare Source](https://redirect.github.com/testing-library/dom-testing-library/compare/v10.4.1...v10.4.2) ##### Bug Fixes - **deps:** pin [@​types/node](https://redirect.github.com/types/node) to a TypeScript 4-compatible version ([#​1386](https://redirect.github.com/testing-library/dom-testing-library/issues/1386)) ([6049cc0](https://redirect.github.com/testing-library/dom-testing-library/commit/6049cc0bc7cf2201625c476c95fa6299d0e2fa8b)) </details> <details> <summary>trpc/trpc (@​trpc/client)</summary> ### [`v11.19.0`](https://redirect.github.com/trpc/trpc/releases/tag/v11.19.0) [Compare Source](https://redirect.github.com/trpc/trpc/compare/v11.18.0...v11.19.0) #### What's Changed - ci(example): fix Playwright e2e ffmpeg failures by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) with [@​Copilot](https://redirect.github.com/Copilot) in [#​7412](https://redirect.github.com/trpc/trpc/pull/7412) - fix(upgrade): clear the query cache on every fixture render by [@​noahisdai](https://redirect.github.com/noahisdai) in [#​7448](https://redirect.github.com/trpc/trpc/pull/7448) - fix(client): abort JSONL stream on httpBatchStreamLink unsubscribe by [@​vinamra1102](https://redirect.github.com/vinamra1102) in [#​7390](https://redirect.github.com/trpc/trpc/pull/7390) - fix(deps): update dependency srvx to v0.12.5 by [@​renovate](https://redirect.github.com/renovate)\[bot] in [#​7437](https://redirect.github.com/trpc/trpc/pull/7437) - docs: fix broken star history chart by [@​OctoBored](https://redirect.github.com/OctoBored) in [#​7470](https://redirect.github.com/trpc/trpc/pull/7470) - fix(server): keep middleware factory declarations portable by [@​1988jimi](https://redirect.github.com/1988jimi) in [#​7557](https://redirect.github.com/trpc/trpc/pull/7557) - fix(server): preserve `this` when chaining to a pre-existing dispose method by [@​raine-works](https://redirect.github.com/raine-works) in [#​7457](https://redirect.github.com/trpc/trpc/pull/7457) - chore: approve esbuild by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7570](https://redirect.github.com/trpc/trpc/pull/7570) - fix(server): correct assertIsRequestId boolean logic — non-NaN invalid types passed silently by [@​okxint](https://redirect.github.com/okxint) in [#​7477](https://redirect.github.com/trpc/trpc/pull/7477) - fix(docs): fix 19 dead links in llms.txt — use permalink, absolute URLs, and H1 title by [@​okxint](https://redirect.github.com/okxint) in [#​7465](https://redirect.github.com/trpc/trpc/pull/7465) - fix(tanstack-react-query): subscriptionOptions(skipToken).enabled incorrectly returns true by [@​okxint](https://redirect.github.com/okxint) in [#​7464](https://redirect.github.com/trpc/trpc/pull/7464) - docs: add observable deprecation note to subscriptions reference projects by [@​algojogacor](https://redirect.github.com/algojogacor) in [#​7375](https://redirect.github.com/trpc/trpc/pull/7375) - fix(client): wire the operation AbortSignal in the WebSocket link by [@​ATKasem](https://redirect.github.com/ATKasem) in [#​7434](https://redirect.github.com/trpc/trpc/pull/7434) - docs: add vscode-toolkit to awesome-trpc by [@​wszgrcy](https://redirect.github.com/wszgrcy) in [#​7363](https://redirect.github.com/trpc/trpc/pull/7363) - docs(www): add Sury to validator library integrations by [@​DZakh](https://redirect.github.com/DZakh) in [#​7466](https://redirect.github.com/trpc/trpc/pull/7466) - docs: add Authier to awesome tRPC projects by [@​capaj](https://redirect.github.com/capaj) in [#​7558](https://redirect.github.com/trpc/trpc/pull/7558) - fix(server): delete stale clientSubscriptions entry on subscription task rejection by [@​Adit-Jain-srm](https://redirect.github.com/Adit-Jain-srm) in [#​7401](https://redirect.github.com/trpc/trpc/pull/7401) - fix(server): match lazy router keys on a path boundary in getProcedureAtPath by [@​contactjawad](https://redirect.github.com/contactjawad) in [#​7474](https://redirect.github.com/trpc/trpc/pull/7474) - fix(client): abort httpLink/httpBatchLink requests on unsubscribe by [@​SnowingFox](https://redirect.github.com/SnowingFox) in [#​7469](https://redirect.github.com/trpc/trpc/pull/7469) - chore(deps): bump vitest from 4.0.18 to 4.1.11 by [@​dependabot](https://redirect.github.com/dependabot)\[bot] in [#​7584](https://redirect.github.com/trpc/trpc/pull/7584) - build(deps): upgrade node to 24.21.0 and [@​types/node](https://redirect.github.com/types/node) to v24 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7571](https://redirect.github.com/trpc/trpc/pull/7571) - build(deps): upgrade pnpm to v12 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7572](https://redirect.github.com/trpc/trpc/pull/7572) - build(deps): upgrade tsdown to 0.23.0 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7574](https://redirect.github.com/trpc/trpc/pull/7574) - build(deps): upgrade typescript to v7 for packages/\* by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7575](https://redirect.github.com/trpc/trpc/pull/7575) - build(deps): upgrade eslint to v10 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7576](https://redirect.github.com/trpc/trpc/pull/7576) - build(deps): upgrade prettier to 3.9.6 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7577](https://redirect.github.com/trpc/trpc/pull/7577) - build(deps): upgrade vitest to v5 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7578](https://redirect.github.com/trpc/trpc/pull/7578) - build(deps): upgrade turbo to 2.10.12 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7579](https://redirect.github.com/trpc/trpc/pull/7579) - build(deps): patch security advisories in packages/\* by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7580](https://redirect.github.com/trpc/trpc/pull/7580) - build(deps): upgrade [@​hey-api/openapi-ts](https://redirect.github.com/hey-api/openapi-ts) to 0.99.0 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7586](https://redirect.github.com/trpc/trpc/pull/7586) - build(deps): patch security advisories in www and examples by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7581](https://redirect.github.com/trpc/trpc/pull/7581) - build(deps): upgrade lerna to v10 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7582](https://redirect.github.com/trpc/trpc/pull/7582) - build(deps): upgrade docusaurus to 3.10.2 by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7583](https://redirect.github.com/trpc/trpc/pull/7583) - fix(openapi): drop function-valued properties from generated schemas by [@​Nick-Lucas](https://redirect.github.com/Nick-Lucas) in [#​7588](https://redirect.github.com/trpc/trpc/pull/7588) #### New Contributors - [@​noahisdai](https://redirect.github.com/noahisdai) made their first contribution in [#​7448](https://redirect.github.com/trpc/trpc/pull/7448) - [@​vinamra1102](https://redirect.github.com/vinamra1102) made their first contribution in [#​7390](https://redirect.github.com/trpc/trpc/pull/7390) - [@​OctoBored](https://redirect.github.com/OctoBored) made their first contribution in [#​7470](https://redirect.github.com/trpc/trpc/pull/7470) - [@​1988jimi](https://redirect.github.com/1988jimi) made their first contribution in [#​7557](https://redirect.github.com/trpc/trpc/pull/7557) - [@​raine-works](https://redirect.github.com/raine-works) made their first contribution in [#​7457](https://redirect.github.com/trpc/trpc/pull/7457) - [@​okxint](https://redirect.github.com/okxint) made their first contribution in [#​7477](https://redirect.github.com/trpc/trpc/pull/7477) - [@​algojogacor](https://redirect.github.com/algojogacor) made their first contribution in [#​7375](https://redirect.github.com/trpc/trpc/pull/7375) - [@​ATKasem](https://redirect.github.com/ATKasem) made their first contribution in [#​7434](https://redirect.github.com/trpc/trpc/pull/7434) - [@​DZakh](https://redirect.github.com/DZakh) made their first contribution in [#​7466](https://redirect.github.com/trpc/trpc/pull/7466) - [@​capaj](https://redirect.github.com/capaj) made their first contribution in [#​7558](https://redirect.github.com/trpc/trpc/pull/7558) - [@​Adit-Jain-srm](https://redirect.github.com/Adit-Jain-srm) made their first contribution in [#​7401](https://redirect.github.com/trpc/trpc/pull/7401) - [@​contactjawad](https://redirect.github.com/contactjawad) made their first contribution in [#​7474](https://redirect.github.com/trpc/trpc/pull/7474) - [@​SnowingFox](https://redirect.github.com/SnowingFox) made their first contribution in [#​7469](https://redirect.github.com/trpc/trpc/pull/7469) **Full Changelog**: <trpc/trpc@v11.18.0...v11.19.0> </details> <details> <summary>vercel/next.js (eslint-config-next)</summary> ### [`v16.3.5`](https://redirect.github.com/vercel/next.js/compare/v16.3.4...v16.3.5) [Compare Source](https://redirect.github.com/vercel/next.js/compare/v16.3.4...v16.3.5) </details> <details> <summary>lucide-icons/lucide (lucide-react)</summary> ### [`v1.47.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.47.0): Version 1.47.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/1.46.0...1.47.0) #### What's Changed - feat(icons): add lambda icon by [@​UbaidUllah9962](https://redirect.github.com/UbaidUllah9962) in [#​4017](https://redirect.github.com/lucide-icons/lucide/pull/4017) - feat(icons): delegated `faucet` icon from lab by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4764](https://redirect.github.com/lucide-icons/lucide/pull/4764) - feat(icons): added `door-closed-package` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4814](https://redirect.github.com/lucide-icons/lucide/pull/4814) - feat(icons): added 'nepali-rupee' icon by [@​sarajdhakal](https://redirect.github.com/sarajdhakal) in [#​4608](https://redirect.github.com/lucide-icons/lucide/pull/4608) - feat(icons): added `tube-lotion` icon by [@​AlecRust](https://redirect.github.com/AlecRust) in [#​4029](https://redirect.github.com/lucide-icons/lucide/pull/4029) - feat(icons): Added cupcake icon by [@​briz123](https://redirect.github.com/briz123) in [#​3000](https://redirect.github.com/lucide-icons/lucide/pull/3000) - feat(icons): added square-dashed-x icon by [@​EthanHazel](https://redirect.github.com/EthanHazel) in [#​4535](https://redirect.github.com/lucide-icons/lucide/pull/4535) - feat(icons): add `rotate-cw-clock` icon by [@​gkkconan](https://redirect.github.com/gkkconan) in [#​3979](https://redirect.github.com/lucide-icons/lucide/pull/3979) - fix(icons): remove path from save-off by [@​HPRILLER](https://redirect.github.com/HPRILLER) in [#​4848](https://redirect.github.com/lucide-icons/lucide/pull/4848) - fix(icons): changed `calendar-chevrons-right` by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4865](https://redirect.github.com/lucide-icons/lucide/pull/4865) - fix(icons): changed `broccoli` icon by [@​jguddas](https://redirect.github.com/jguddas) in [#​4871](https://redirect.github.com/lucide-icons/lucide/pull/4871) - feat(icons): added square-dashed-plus by [@​psjdev](https://redirect.github.com/psjdev) in [#​4849](https://redirect.github.com/lucide-icons/lucide/pull/4849) #### New Contributors - [@​UbaidUllah9962](https://redirect.github.com/UbaidUllah9962) made their first contribution in [#​4017](https://redirect.github.com/lucide-icons/lucide/pull/4017) - [@​sarajdhakal](https://redirect.github.com/sarajdhakal) made their first contribution in [#​4608](https://redirect.github.com/lucide-icons/lucide/pull/4608) - [@​AlecRust](https://redirect.github.com/AlecRust) made their first contribution in [#​4029](https://redirect.github.com/lucide-icons/lucide/pull/4029) - [@​gkkconan](https://redirect.github.com/gkkconan) made their first contribution in [#​3979](https://redirect.github.com/lucide-icons/lucide/pull/3979) - [@​HPRILLER](https://redirect.github.com/HPRILLER) made their first contribution in [#​4848](https://redirect.github.com/lucide-icons/lucide/pull/4848) - [@​psjdev](https://redirect.github.com/psjdev) made their first contribution in [#​4849](https://redirect.github.com/lucide-icons/lucide/pull/4849) **Full Changelog**: <lucide-icons/lucide@1.46.0...1.47.0> ### [`v1.46.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.46.0): Version 1.46.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/1.45.0...1.46.0) #### What's Changed - feat(icons): Added new icons: `clef-g`, `clef-f` and `clef-c` by [@​rrod497](https://redirect.github.com/rrod497) in [#​4083](https://redirect.github.com/lucide-icons/lucide/pull/4083) - chore(deps-dev): bump astro from 7.3.0 to 7.3.2 in the astro-deps group by [@​dependabot](https://redirect.github.com/dependabot)\[bot] in [#​4860](https://redirect.github.com/lucide-icons/lucide/pull/4860) - feat(icons): added `monitor-pc` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4791](https://redirect.github.com/lucide-icons/lucide/pull/4791) **Full Changelog**: <lucide-icons/lucide@1.45.0...1.46.0> ### [`v1.45.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.45.0): Version 1.45.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/1.44.0...1.45.0) #### What's Changed - feat(icons): added `calendar-chevrons-right` icon by [@​AlexandrePhilibert](https://redirect.github.com/AlexandrePhilibert) in [#​3565](https://redirect.github.com/lucide-icons/lucide/pull/3565) - feat(icons): added `building-complex-plus` icon by [@​tylerkade](https://redirect.github.com/tylerkade) in [#​4758](https://redirect.github.com/lucide-icons/lucide/pull/4758) - feat(icons): add hourglass-cog icon by [@​lazerg](https://redirect.github.com/lazerg) in [#​4635](https://redirect.github.com/lucide-icons/lucide/pull/4635) - feat(icons): added `mouth` & `mouth-off` by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4787](https://redirect.github.com/lucide-icons/lucide/pull/4787) - feat(icons): added `iv-bag` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4821](https://redirect.github.com/lucide-icons/lucide/pull/4821) - fix(icons): changed `lectern` icon by [@​UsamaKhan](https://redirect.github.com/UsamaKhan) in [#​2925](https://redirect.github.com/lucide-icons/lucide/pull/2925) - feat(icons): add `layout-arrow-right` and `layout-arrow-down` by [@​samuelalake](https://redirect.github.com/samuelalake) in [#​4541](https://redirect.github.com/lucide-icons/lucide/pull/4541) - feat(icons): added `park` icon by [@​skajosborn](https://redirect.github.com/skajosborn) in [#​3177](https://redirect.github.com/lucide-icons/lucide/pull/3177) - fix(icons): changed `album`, `book-marked`, `folder-bookmark` icons by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​3043](https://redirect.github.com/lucide-icons/lucide/pull/3043) - fix(icons): correct misspelled tags by [@​decknamec](https://redirect.github.com/decknamec) in [#​4836](https://redirect.github.com/lucide-icons/lucide/pull/4836) - feat(icons): added `houses` icon by [@​danielbayley](https://redirect.github.com/danielbayley) in [#​3241](https://redirect.github.com/lucide-icons/lucide/pull/3241) - feat(icons): added `notebook-dot` icon by [@​elenakovelskikh](https://redirect.github.com/elenakovelskikh) in [#​3228](https://redirect.github.com/lucide-icons/lucide/pull/3228) - feat(icons): add `messages-circle` icon by [@​Mirazstudio-offical](https://redirect.github.com/Mirazstudio-offical) in [#​4754](https://redirect.github.com/lucide-icons/lucide/pull/4754) - feat(icons): added `plant-pot` icon by [@​vqh2602](https://redirect.github.com/vqh2602) in [#​3122](https://redirect.github.com/lucide-icons/lucide/pull/3122) - fix(icons): changed `cookie` icon by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4815](https://redirect.github.com/lucide-icons/lucide/pull/4815) - fix(icons): remove duplicate use-cases prop from cookie.json by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4838](https://redirect.github.com/lucide-icons/lucide/pull/4838) - fix(site): fix home card icons by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4839](https://redirect.github.com/lucide-icons/lucide/pull/4839) - feat(docs): added "How to use Lucide icons" section to resources by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4829](https://redirect.github.com/lucide-icons/lucide/pull/4829) - fix(packages/vue): fix generated icon declaration types for [@​lucide/vue](https://redirect.github.com/lucide/vue) by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4841](https://redirect.github.com/lucide-icons/lucide/pull/4841) - chore(deps-dev): bump vitest from 4.1.10 to 4.1.11 by [@​dependabot](https://redirect.github.com/dependabot)\[bot] in [#​4845](https://redirect.github.com/lucide-icons/lucide/pull/4845) - chore(deps-dev): bump vitest from 4.1.10 to 4.1.11 in /integrations/lucide-react/vite by [@​dependabot](https://redirect.github.com/dependabot)\[bot] in [#​4844](https://redirect.github.com/lucide-icons/lucide/pull/4844) - ci(ci.yml): Add dispatch post release by [@​ericfennis](https://redirect.github.com/ericfennis) in [#​4847](https://redirect.github.com/lucide-icons/lucide/pull/4847) - feat(icons): added `globe-code` icon by [@​AleksejDix](https://redirect.github.com/AleksejDix) in [#​3722](https://redirect.github.com/lucide-icons/lucide/pull/3722) #### New Contributors - [@​tylerkade](https://redirect.github.com/tylerkade) made their first contribution in [#​4758](https://redirect.github.com/lucide-icons/lucide/pull/4758) - [@​alx-xo](https://redirect.github.com/alx-xo) made their first contribution in [#​4115](https://redirect.github.com/lucide-icons/lucide/pull/4115) - [@​skajosborn](https://redirect.github.com/skajosborn) made their first contribution in [#​3177](https://redirect.github.com/lucide-icons/lucide/pull/3177) - [@​elenakovelskikh](https://redirect.github.com/elenakovelskikh) made their first contribution in [#​3228](https://redirect.github.com/lucide-icons/lucide/pull/3228) - [@​Mirazstudio-offical](https://redirect.github.com/Mirazstudio-offical) made their first contribution in [#​4754](https://redirect.github.com/lucide-icons/lucide/pull/4754) - [@​AleksejDix](https://redirect.github.com/AleksejDix) made their first contribution in [#​3722](https://redirect.github.com/lucide-icons/lucide/pull/3722) **Full Changelog**: <lucide-icons/lucide@1.44.0...1.45.0> </details> <details> <summary>mqttjs/MQTT.js (mqtt)</summary> ### [`v5.16.0`](https://redirect.github.com/mqttjs/MQTT.js/blob/HEAD/CHANGELOG.md#5160-2026-09-16) [Compare Source](https://redirect.github.com/mqttjs/MQTT.js/compare/v5.15.2...v5.16.0) ##### Bug Fixes - bounds-check the suback granted array against subscriptions sent (GHSA-rj8f-4655-cgg2) ([#​2061](https://redirect.github.com/mqttjs/MQTT.js/issues/2061)) ([46ee23f](https://redirect.github.com/mqttjs/MQTT.js/commit/46ee23f3f1d4a8ef21e5c2b26ea24818f1b2dce3)), closes [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) - enforce MQTT 5 Receive Maximum for inbound QoS 2 messages (GHSA-h8jm-hm87-fqw3) ([#​2065](https://redirect.github.com/mqttjs/MQTT.js/issues/2065)) ([ec5bf3e](https://redirect.github.com/mqttjs/MQTT.js/commit/ec5bf3ea16e960313d670f280b0eac91dac35fe2)), closes [#​2064](https://redirect.github.com/mqttjs/MQTT.js/issues/2064) [#​2063](https://redirect.github.com/mqttjs/MQTT.js/issues/2063) [#​2062](https://redirect.github.com/mqttjs/MQTT.js/issues/2062) [#​2061](https://redirect.github.com/mqttjs/MQTT.js/issues/2061) [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) - keep broker CONNACK properties out of the user options object (GHSA-gfxc-3w7m-8ch4) ([#​2062](https://redirect.github.com/mqttjs/MQTT.js/issues/2062)) ([3db16f5](https://redirect.github.com/mqttjs/MQTT.js/commit/3db16f54a12e46e496c6fca352d6bb8324f8634e)), closes [#​2061](https://redirect.github.com/mqttjs/MQTT.js/issues/2061) [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) - reject a duplicate CONNACK instead of re-running connection setup (GHSA-8phv-jwjm-93rr) ([#​2064](https://redirect.github.com/mqttjs/MQTT.js/issues/2064)) ([7108ea5](https://redirect.github.com/mqttjs/MQTT.js/commit/7108ea597458e6c538e88f3ac1cf9f7c2001dcc2)), closes [#​2063](https://redirect.github.com/mqttjs/MQTT.js/issues/2063) [#​2062](https://redirect.github.com/mqttjs/MQTT.js/issues/2062) [#​2061](https://redirect.github.com/mqttjs/MQTT.js/issues/2061) [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) - reject an unsolicited AUTH instead of crashing the process (GHSA-fwrw-4mhv-wxvm) ([#​2063](https://redirect.github.com/mqttjs/MQTT.js/issues/2063)) ([b511e7b](https://redirect.github.com/mqttjs/MQTT.js/commit/b511e7b7d9a927bc3af4c146b65e493c652a11ec)), closes [#​2062](https://redirect.github.com/mqttjs/MQTT.js/issues/2062) [#​2061](https://redirect.github.com/mqttjs/MQTT.js/issues/2061) [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) [#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060) - reject unsolicited MQTT 5 topic alias instead of crashing (GHSA-c8jq-r765-cq7g) ([#​2060](https://redirect.github.com/mqttjs/MQTT.js/issues/2060)) ([7d07757](https://redirect.github.com/mqttjs/MQTT.js/commit/7d07757a1498f52243c64cb8c2dad416cf15449d)) #### [5.15.2](https://redirect.github.com/mqttjs/MQTT.js/compare/v5.15.1...v5.15.2) (2026-07-06) ##### Bug Fixes - **connect/tls:** don't overwrite an explicit servername (SNI) ([#​2051](https://redirect.github.com/mqttjs/MQTT.js/issues/2051)) ([cd1d780](https://redirect.github.com/mqttjs/MQTT.js/commit/cd1d780f5742852e9bcf46dbd9732e0c5e20823a)) - support relative WebSocket URLs ([#​2049](https://redirect.github.com/mqttjs/MQTT.js/issues/2049)) ([c4be563](https://redirect.github.com/mqttjs/MQTT.js/commit/c4be5633594b5b839862440aa78125f22021f8e6)), closes [#​2035](https://redirect.github.com/mqttjs/MQTT.js/issues/2035) #### [5.15.1](https://redirect.github.com/mqttjs/MQTT.js/compare/v5.15.0...v5.15.1) (2026-03-24) </details> <details> <summary>vercel/next.js (next)</summary> ### [`v16.3.5`](https://redirect.github.com/vercel/next.js/compare/v16.3.4...v16.3.5) [Compare Source](https://redirect.github.com/vercel/next.js/compare/v16.3.4...v16.3.5) </details> <details> <summary>dcastil/tailwind-merge (tailwind-merge)</summary> ### [`v3.7.0`](https://redirect.github.com/dcastil/tailwind-merge/compare/v3.6.0...tailwind-merge@3.7.0) [Compare Source](https://redirect.github.com/dcastil/tailwind-merge/compare/v3.6.0...tailwind-merge@3.7.0) </details> <details> <summary>colinhacks/zod (zod)</summary> ### [`v4.6.5`](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.5) [Compare Source](https://redirect.github.com/colinhacks/zod/compare/v4.6.4...v4.6.5) #### Commits: - [`d2b135c`](https://redirect.github.com/colinhacks/zod/commit/d2b135cfb7a3582b9eb515756b9166bcb9521f4a) docs: add the 4.6.x patch highlights to the 4.6 post - [`f1448f7`](https://redirect.github.com/colinhacks/zod/commit/f1448f7cee00df9fe1e9ad84a000aa1828cc8bc1) docs: fold the 4.6.x patch highlights into the 4.6 post's own sections - [`de65a5c`](https://redirect.github.com/colinhacks/zod/commit/de65a5cb39ed22a507fac935788f718fa88d104f) docs: lead the properties section with the check and add a Zod Mini tab ([#​6598](https://redirect.github.com/colinhacks/zod/issues/6598)) - [`56222cd`](https://redirect.github.com/colinhacks/zod/commit/56222cd1532c07bcb91b67df529cab4c0a215330) feat(instanceof): key the .properties() shape off the instance type ([#​6600](https://redirect.github.com/colinhacks/zod/issues/6600)) - [`ca0229a`](https://redirect.github.com/colinhacks/zod/commit/ca0229a404818290e6cdcfefcd7eb2d04bcbb543) Revert "feat: add z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI ([#​6595](https://redirect.github.com/colinhacks/zod/issues/6595))" - [`cc4cd4e`](https://redirect.github.com/colinhacks/zod/commit/cc4cd4ee9c52fcaa10964e48cc144541e41a5ed9) Revert "Revert "feat: add z.currencyCode() over a vendored ISO 4217 list, refreshed weekly by CI ([#​6595](https://redirect.github.com/colinhacks/zod/issues/6595))"" - [`0f3f5ee`](https://redirect.github.com/colinhacks/zod/commit/0f3f5ee3ca56c7574bf849e54f79e9a6e02562ee) 4.6.5 - [`59bbc03`](https://redirect.github.com/colinhacks/zod/commit/59bbc03e10c636b9eb3c393dfeb552819774ec21) chore: re-pin the integration peers to the workspace zod after the 4.6.5 bump ### [`v4.6.4`](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.4) [Compare Source](https://redirect.github.com/colinhacks/zod/compare/v4.6.3...v4.6.4) A patch on top of [4.6.3](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.3). - [`d6bc1e30`](https://redirect.github.com/colinhacks/zod/commit/d6bc1e30) feat: add `z.currencyCode()` over a vendored ISO 4217 list, refreshed weekly by CI ([#​6595](https://redirect.github.com/colinhacks/zod/pull/6595)) - [`ad32d751`](https://redirect.github.com/colinhacks/zod/commit/ad32d751) perf: `z.url()` rejects an invalid URL with `URL.canParse()` instead of a throwing constructor, about 50x faster; fewer allocations on the validation path ([#​6588](https://redirect.github.com/colinhacks/zod/pull/6588)) - [`2bb08717`](https://redirect.github.com/colinhacks/zod/commit/2bb08717) chore: re-pin the integration peers to the workspace zod after the 4.6.4 bump - [`f6e1701a`](https://redirect.github.com/colinhacks/zod/commit/f6e1701a) chore(deps): bump next to 15.5.25 and vite to 7.3.6 ([#​6153](https://redirect.github.com/colinhacks/zod/pull/6153)) ### [`v4.6.3`](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.3) [Compare Source](https://redirect.github.com/colinhacks/zod/compare/v4.6.2...v4.6.3) A patch on top of [4.6.2](https://redirect.github.com/colinhacks/zod/releases/tag/v4.6.2). - [`413cce9a`](https://redirect.github.com/colinhacks/zod/commit/413cce9a) fix(v4): make z.properties() a check again ([#​6594](https://redirect.github.com/colinhacks/zod/pull/6594)) — removes the standalone `z.properties()` schema from 4.6.0; `z.instanceof().properties()` and `.check(...z.properties())` are unchanged - [`75d63ee1`](https://redirect.github.com/colinhacks/zod/commit/75d63ee1) docs: show only the `.properties()` method form in the 4.6 post - [`46da9572`](https://redirect.github.com/colinhacks/zod/commit/46da9572) docs: match the error-message examples to what the parsers emit </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "before 6am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/sleepypod/core). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMDMuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjEwMy4wIiwidGFyZ2V0QnJhbmNoIjoiZGV2IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyIsImZpeC1wYXRjaCIsIm5vbi1tYWpvciJdfQ==--> <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update renovate/all-minor-patch ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/renovate/all-minor-patch/scripts/install \ | sudo bash -s -- --branch renovate/all-minor-patch ``` 🎯 **Pin to this exact build** ([run 35564801167](https://github.com/sleepypod/core/actions/runs/35564801167) · `a56bb57`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/a56bb57c5cd2a52d3826cb4e11497b79f1e8f16f/scripts/install \ | sudo bash -s -- \ --branch renovate/all-minor-patch \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/35564801167/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/35564801167/artifacts/10623636240) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…r switches (#718) Normal HomeKit shutdown called hap-nodejs `Accessory.destroy()`, which deletes pairing keys and accessory identifiers despite their persistent storage location. Use `unpublish()` for ordinary shutdown. Explicit unpair/reset still clears HAP storage and rotates identity. If advertisement cleanup fails after HAP closes its server, report the bridge as stopped and allow a fresh listener to start while retired advertiser cleanup remains best-effort. Remove the separate left/right bed power switches. Each existing thermostat already supports Off/Auto and target temperature; its UUID and remaining accessory IDs stay unchanged. Automations that specifically referenced a removed power switch will need to use the corresponding thermostat. Validation: - CI: 3,859 tests passed, 1 skipped; lint, TypeScript, and both builds passed. Local pre-push checks also validated both Drizzle schemas. - Codecov confirms all modified, coverable lines are tested. Bridge and storage have 100% line coverage; bridge branch coverage is 98.52%. - Real hap-nodejs filesystem regression tests prove shutdown preserves keys and identifiers while explicit reset removes them. Reintroducing `destroy()` makes the regression fail. - Real HAP publish/stop/fresh-publish tests open loopback TCP listeners and preserve signing keys, controller pairings, and all AIDs/IIDs. Tests cover both server-destruction failure and advertiser rejection after the listener closes, including recovery while retired cleanup still rejects. Only mDNS and physical sensor builders are stubbed. - Deployed production commit `0b53800` to pod `192.168.1.88`; the final commit adds tests only. Upgrade and HomeKit disable/enable preserved all HAP files byte-for-byte, with two paired controllers and a reconnected Home controller. Initial validation also confirmed thermostats retain AIDs 2/6, all 128 retained identifier-cache entries are unchanged, and the running bridge exposes 10 accessories with no bed power switches. - Addressed CodeRabbit's partial-teardown finding and published-lifecycle test suggestion. CodeRabbit acknowledged the fix and marked its thread resolved; the full follow-up review was rate-limited. Deployment note: the old process still contains the destructive shutdown code. For the first upgrade, back up HomeKit storage before stopping the old process and restore it before starting the fixed build. This was done on .88; subsequent upgrades and restart/toggle verification required no restoration. The test deployment used the pod's existing ARM native/runtime dependencies to supplement the standalone bundle.
…erflow, smoother temp updates (#716)
) ## Problem `sleepypod-biometrics-archiver` logs `archived=0 removed=0 failed=0` on every run and `/persistent/biometrics-archive/` never fills, so raw frames from past nights cannot be replayed offline (e.g. against the piezo-processor beat detector). ADR-0018's cold archive has effectively never worked on current firmware. ## Cause (firmware-side, not this repo) Observed on a Pod 4 on current firmware, read-only: - The logger rotates `*.RAW` every **~15 min** (~10 MB per frame, ≈1 GB/day). - At rotation the finished frame is **already unlinked** — within a second of its successor appearing. The firmware's descriptors have both moved to the new frame and tmpfs usage drops straight back to the size of the fresh file, so the inode is fully released: not even a deleted-but-open grace period. - Journal order per rotation: `raw_logger.h startNewLogFile|[logger] writing to <new>.RAW` → `[Upload] Ending Batch` → `[seq] save - checkpoint: <previous>` → `[cloud] [Upload] File complete/File to upload` → `[Upload] Starting Batch`. - Nothing in this repo removes `*.RAW` on a schedule: the archiver only after a successful gzip, the pruner only inside `biometrics-archive/`, and `POST /raw/files/delete` only on request. The firmware's own `defibrillator.sh` sweeps `*.RAW` under `/persistent` when free space runs low — an emergency path, and it spares `*.RAW.gz`. So the archiver's `find -mmin "+$KEEP_RECENT_MIN"` selection can never match: the deletion window is ~0, and `KEEP_RECENT_MIN` also happened to equal the rotation period. ## Fix Pin each frame with a second hard link while the firmware still holds it. - **New `sleepypod-biometrics-linker` + timer (every minute)** hard-links every live `*.RAW` into `/persistent/biometrics/.pending/` on the same tmpfs. While both names exist they are one inode — no copy, no extra memory, and everything the firmware appends after the link is still in the pinned frame. When the firmware unlinks its name, ours keeps the frames alive. - **Archiver gains a second pass**: a pinned frame is gzipped to eMMC once the live name is gone, then the link is dropped. eMMC still sees one batched sequential compressed write per frame on the existing 15-min cadence, so ADR-0018's wear goal is unchanged — the archive simply starts filling, bounded as designed by the pruner's 80% disk cap. - **Live writes protected**: the linker caps the pending set at `PENDING_MAX_PCT` (default 50%) of the tmpfs and drops oldest-first, so a stuck archiver (eMMC full, gzip failing) can never starve the firmware. - **Invisible to every other reader**: `.pending/` is dot-prefixed and one level down, so the sidecars (`raw_follower`), `piezoStream`, the calibrator, `/raw/files`, the export route and `sp-status` all walk past it. - **NATS transition stays safe**: `remove_biometrics_archiver_for_nats` now refuses to unmount the tmpfs while an unarchived pin remains, exactly as it already did for live frames. Wired into `install`/`sp-update` (via the archiver helpers), `sp-uninstall`, `sp-status` (timer + pinned count) and `sp-bundle-logs`. ## Tests `scripts/tests/biometricsArchiver.test.ts` (+8 cases, 20 total): the pin shares the inode and survives an unlink; appends after the link are preserved; SEQNO.RAW and the tmpfs-prep symlinks are skipped; repeat runs are quiet no-ops; a non-tmpfs path is skipped; the cap drops oldest-first and never touches live frames; an unreadable `df` skips enforcement instead of dropping everything; the archiver archives a released pin (gunzip round-trip), leaves a still-live one alone, drops a pin already in the archive, and clears pins on the `KEEP_RECENT_MIN=-1` transition path; the NATS cleanup refuses to unmount with a pin outstanding. Quality gates: `pnpm tsc`, `pnpm lint`, `pnpm test` (3919 passed) all green. ## Docs ADR-0018 gains a "Rotation deletes the finished frame" section (measurements, the linker decision, and why a shorter cadence, a `.path` unit, an inotify daemon, journal tailing or copying were rejected), plus README diagram/prose and a `docs/DEBUGGING.md` symptom flow for "RAW archive is empty / `archived=0`". ## Deployment note This has not yet run on a pod — it is a new timer, so it takes effect after `sp-update`. First check afterwards: `sp-status` shows `sleepypod-biometrics-linker.timer` active with `.RAW pinned (.pending)` = 1, and the archiver starts logging `archived=1` about once per rotation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Biometrics frames released during firmware rotation are retained for the next archive run. * System status now shows the number of frames awaiting archiving. * Uninstallation now attempts to archive remaining frames before cleanup and preserves unarchived data and recovery tools if cleanup fails. * **Bug Fixes** * Frames are less likely to be lost before the next archive run. * **Documentation** * Updated architecture and troubleshooting guidance with frame-retention details, archive limits, and steps for investigating archiving issues. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Jonathan Ng <jon@jonathanng.com>
The Pod 3/4 presence check summed |val - mean| / std with std floored at 5 and threshold 6. A quiet calibration window puts std at the floor, so ~30 raw units of thermal drift in either direction read as occupied: sessions stayed open for many hours after the sleeper got up, and a baseline captured while someone was in bed made the empty bed (far below it) read occupied until the next recalibration. Presence is now the summed signed rise over the per-channel baseline in raw units (default 300), mirroring the existing capSense2 fix. On Pod 4 hardware empty-bed drift stays around +100 summed while an occupant adds at least ~+450. Legacy profiles store a z-score threshold (6.0) and no format key, so they use the raw-unit default instead of being read as raw units. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An open session lived only in memory, so a service restart or pod reboot before the sleeper's absence closed it dropped the whole night. The tracker state is now checkpointed to sleep-detector-state.json next to biometrics.db (tmp + fsync + rename) every 60s and immediately on session start, bed-exit and close. On startup a recent session resumes; if absence is committed before any presence is seen, the exit is dated at the last pre-restart presence. A save older than 30 min is closed at the last presence instead of resumed. Samples the RAW follower replays from the current file's start are skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cap calibration Scheduled capacitance calibration picks the "quietest 5-minute window", which cannot tell an empty bed from a motionless sleeper. The daily fallback runs at a fixed UTC hour, which is mid-night in many timezones; when it captured the sleeper as "empty", the bed read occupied until the next recalibration. The sleep-detector now keeps its own per-side empty-bed baseline: - presence enters above the raw-unit threshold and exits below half of it - while empty and within the exit threshold it follows drift (30 min tau) - any reading below baseline pulls it down fast (2 min tau), so a baseline captured with someone in bed recovers minutes after they get up - a session force-closed at the 16h cap makes the current level the new empty level (a load that isn't a sleeper) - seeded from saved state, else the calibration profile, else the first frame; a newer calibration profile (manual recalibration) is adopted - persisted in the state file and published to calibration_profiles every 15 min (source "adaptive") so Node's capSense2 occupancy and the UI agree The calibrator no longer calibrates capacitance on startup, retry, the daily fallback, or the pre-prime trigger (now tagged source "scheduled"); a manual request still does. Piezo and temperature are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A solo sleeper who rolls over or puts a leg on the empty side loads that side's sensors too: its capSense level rises while the sleeper's side is still occupied, and its piezo reports the sleeper's heart rate. That produced phantom sessions and vitals rows on the empty side. Exactly one side in away mode now means one sleeper on the other side (common/side_mode.py reads side_settings.away_mode read-only every 60s): - sleep-detector: the sleeper is in bed while either side reads occupied, so migrating across the bed stays one home-side session; movement from both sides is summed; the away side records nothing but keeps tracking its baseline; a capped session resets both baselines - piezo-processor: each cycle's candidates from both sides are paired and the higher-quality one is written under the home side; an unpaired away-side reading (fully rolled over) is written as the home side Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
restore() returned for a side with no open session before restoring its last-processed timestamp, so that side replayed the current RAW file from offset 0 after a restart. In single-sleeper mode the sessionless away side feeds the home session: its replayed (older) frames reached home.commit() with timestamps going backwards and could fake an exit, splitting one continuous night into two sessions. The watermark is now restored for every side regardless of session state, and process_single_sleeper drops a frame the home side has already processed instead of committing the away side's reading of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Presence enters above the threshold and exits below half of it, using a per-side latch (_level_present) that was not saved. After a restart a reading between the two thresholds (someone lying near the edge) came back as absent, closing a session that was still going and losing the rest of the night. The latch is now saved in the state file and restored for every side, with or without an open session; it applies to both the RAW and NATS sources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SingleSleeperVitals cleared a held candidate before writing it, so a failed SQLite write (flush, or the paired best-of-two write) dropped a row that the side had already been told was accepted. A held candidate is now cleared only once its write commits. A failed paired write keeps the better reading held for retry; a failed flush keeps it and retries after another VITALS_INTERVAL_S; and a same-side repeat is refused while the older row is unwritten, so its side keeps the new one and resubmits instead of the older row being overwritten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_sync_baseline ignored profiles marked source "adaptive" (the detector's own published baseline) so it would never re-adopt its own output over a live baseline. It also ignored them when there was no baseline at all, so with the state file missing or rejected the first frame became the empty level even when that frame was occupied and a valid published baseline existed. An adaptive profile now seeds the baseline when none exists; it still never replaces a live one. Docs note this and the restart behaviour of the replay watermark and hysteresis latch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…safe sessions, single-sleeper mode (#715) ## Summary Fixes a set of presence and session bugs on Pod 3/4 (`capSense`) pods that caused missing nights, sessions stuck open for many hours, and phantom sessions on an unused side. One commit per change: 1. **`fix(sleep-detector)`: raw-unit, rise-only presence for Pod 3/4 capSense.** The capSense check summed `|val - mean| / std` with std floored at 5 and threshold 6, so ~30 raw units of drift in *either* direction read as occupied. Presence is now the summed signed rise over baseline in raw units (default 300), mirroring the existing capSense2 fix. Legacy profiles (z-score `threshold: 6.0`, no `format`) use the raw-unit default instead of being read as raw units. 2. **`feat(sleep-detector)`: persist in-progress sessions across restarts.** An open session lived only in memory, so a reboot or service restart mid-session dropped the night. State is checkpointed to `sleep-detector-state.json` (tmp + fsync + rename) every 60 s and on start/exit/close. On startup a recent session resumes; a gap over 30 min closes it at the last presence; an exit during downtime is dated at the last pre-restart presence; frames the RAW follower replays from the current file's start are skipped. 3. **`feat(sleep-detector)`: self-adjusting presence baseline, no scheduled capacitance calibration.** The calibrator's "quietest 5-minute window" can't tell an empty bed from a motionless sleeper, and the daily fallback runs at a fixed UTC hour that is mid-night in many timezones. The detector now keeps its own baseline (`AdaptiveBaseline`): - enter/exit hysteresis (exit at half the threshold) - slow drift tracking while empty (30 min τ) - fast recovery when the reading drops below baseline (2 min τ — a body only adds capacitance) - reset after a 16 h capped session - seeded from saved state → calibration profile → first frame - published back to `calibration_profiles` every 15 min (`source: "adaptive"`) so Node's capSense2 occupancy and the UI agree The calibrator no longer runs capacitance on startup/retry/daily/pre-prime (the pre-prime trigger is tagged `source: "scheduled"`). A **manual** recalibration still runs and is adopted as a reseed. Piezo/temperature scheduling is unchanged. 4. **`feat(biometrics)`: single-sleeper mode when one side is in away mode.** A solo sleeper who rolls onto the empty side raises its capSense level and shows up on its piezo with the same heart rate, producing phantom sessions and vitals on that side. Exactly one side in away mode now means one sleeper on the other side: the sleep-detector treats either side occupied as in bed (one home-side session, movement summed), and the piezo-processor pairs each cycle's candidates and writes only the higher-quality one under the home side. ## Validation - Replayed recorded capSense data through the new detector, starting from a baseline captured with the sleeper in bed. The baseline recovered within minutes of getting up, sessions ended at the real wake time instead of at the next recalibration, and a bedding shift of a few tens of units per channel produced no session. With one side in away mode, a rollover night produced one home-side session instead of several short sessions on the away side. - Thresholds: on Pod 4 hardware empty-bed drift stays around +100 summed, while an occupant adds at least ~+450 (typically ~+1800). A still sleeper can hold within a few units for 30 min, so stillness is **not** used to infer an object on the bed. - Python suites: common 215, sleep-detector 64, piezo-processor 93, calibrator 17 (environment-monitor and cover-buttons unchanged). `pnpm tsc`, `pnpm lint` (only the pre-existing `stryker.config.mjs` warning), `pnpm test` and `pnpm build` all pass. ## Reviewer notes - `CAPSENSE_PRESENCE_THRESHOLD = 300` is tuned on Pod 4 data. capSense2 keeps the profile threshold (default 6.0) with the same adaptive tracking, but hasn't been replayed against Pod 5 data. - A heavy object left on the bed still yields one 16 h capped session before the baseline resets. - The piezo "quietest window" calibration has the same blind spot capacitance did (noted in the ADR-0014 amendment); it isn't changed here. - Away mode keeps its existing meaning (schedules off for that side); single-sleeper merging is an added effect. - Docs: ADR-0014 amendment, `docs/sleep-detector.md` (presence, baseline, restarts, single-sleeper), `docs/piezo-processor.md`, `docs/trpc-api-architecture.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - In single-sleeper mode, presence, movement, and vitals from either side are combined under the home side. For paired vitals, the higher-quality reading is kept; a lone reading is saved after a wait. - Sleep detection adapts to sensor drift, applies separate presence entry and exit thresholds, and preserves session state across restarts while filtering replayed readings. - **Changes** - Automatic calibration now covers piezo and temperature; capacitance calibration remains available on manual request. - Updated documentation describes presence detection, calibration, session recovery, and single-sleeper behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
The release promotion exposed missing coverage for single-sleeper UI
routing. Add page-level tests using the real VitalsPanel and
useBiometricsSide hook to verify both home sides, disabled away-side
queries, exclusion of cached away-side sessions, and normal two-side
comparisons.
Validation: three targeted tests, typecheck, lint, and pre-push checks
pass. No production behavior changes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Added coverage for single-sleeper routing and the default both-sides
view, including displayed sleep data, query behavior, and side
selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- sleepypod:install-start -->
<!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block.
Hello fellow agent / curious human! Anything between the
sleepypod:install-start and sleepypod:install-end markers
gets overwritten on the next CI run. Want to change what's
here? Edit .github/workflows/build.yml instead. 💤 -->
<details open>
<summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary>
Pick the snippet that matches your pod. **Already-installed pods can't
use the curl bootstraps below** — sleepypod's egress firewall DROPs
github.com, and the script that knows how to unblock WAN is the very
file curl is trying to fetch. Use `sp-update` instead; it's on disk and
opens WAN as its first step.
🔄 **Already on a sleepypod** (most common — review a PR on a running
pod):
```bash
sudo sp-update codex/promotion-ui-coverage
```
🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every
push:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/codex/promotion-ui-coverage/scripts/install \
| sudo bash -s -- --branch codex/promotion-ui-coverage
```
🎯 **Pin to this exact build** ([run
36278522554](https://github.com/sleepypod/core/actions/runs/36278522554)
· `430d43e`) — fresh-pod path, locked to one CI artifact for
reproducible review:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/430d43edbb4c2f48fa9e7206aca9e3483ae97344/scripts/install \
| sudo bash -s -- \
--branch codex/promotion-ui-coverage \
--artifact-url 'https://nightly.link/sleepypod/core/actions/runs/36278522554/sleepypod-core.zip'
```
🧊 Artifact:
[`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/36278522554/artifacts/10918331121)
· self-destructs in 30 days 💥
</details>
<!-- sleepypod:install-end -->
Extend the page routing regressions to both normal side selections and
the standalone VitalsPanel. Verify both toggle directions and that
single-sleeper mode hides the standalone side switch. This covers the
remaining navigation branches reported by the main promotion coverage
gate.
Validation: all eight page/navigation tests pass, plus typecheck and
pre-push quality gates. Tests only; no production behavior changes.
<!-- sleepypod:install-start -->
<!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block.
Hello fellow agent / curious human! Anything between the
sleepypod:install-start and sleepypod:install-end markers
gets overwritten on the next CI run. Want to change what's
here? Edit .github/workflows/build.yml instead. 💤 -->
<details open>
<summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary>
Pick the snippet that matches your pod. **Already-installed pods can't
use the curl bootstraps below** — sleepypod's egress firewall DROPs
github.com, and the script that knows how to unblock WAN is the very
file curl is trying to fetch. Use `sp-update` instead; it's on disk and
opens WAN as its first step.
🔄 **Already on a sleepypod** (most common — review a PR on a running
pod):
```bash
sudo sp-update test/promotion-side-toggle
```
🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every
push:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/test/promotion-side-toggle/scripts/install \
| sudo bash -s -- --branch test/promotion-side-toggle
```
🎯 **Pin to this exact build** ([run
36279027799](https://github.com/sleepypod/core/actions/runs/36279027799)
· `76d6071`) — fresh-pod path, locked to one CI artifact for
reproducible review:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/76d6071ca8ba964b5ffab719be73d1b61aa32e31/scripts/install \
| sudo bash -s -- \
--branch test/promotion-side-toggle \
--artifact-url 'https://nightly.link/sleepypod/core/actions/runs/36279027799/sleepypod-core.zip'
```
🧊 Artifact:
[`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/36279027799/artifacts/10918551605)
· self-destructs in 30 days 💥
</details>
<!-- sleepypod:install-end -->
…cks (#724) ## Summary Enabling a merge queue on `dev` requires the required status checks to run on the temporary merge-group branch, otherwise every queue entry times out. This adds the `merge_group` trigger to the CI workflow. For the Lint task the diff base now comes from `BASE_REF`, which is the PR base on pull requests and `refs/heads/<base>` on merge-group runs (stripped in the shell). Push runs are unchanged. ## Test plan - [ ] CI runs on this PR as before (Lint, Typecheck, Unit Tests, codecov/patch). - [ ] After merge, the `dev` ruleset gains a merge queue rule; the first queued PR shows the same checks reporting on the merge group. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Automated checks now run for changes evaluated through the merge queue. * Lint checks now compare changes against the appropriate target branch, including when a merge-queue target is used. This keeps validation aligned with the branch the changes are intended to join. <!-- end of auto-generated comment: release notes by coderabbit.ai --> <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update ci/merge-group-trigger ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/ci/merge-group-trigger/scripts/install \ | sudo bash -s -- --branch ci/merge-group-trigger ``` 🎯 **Pin to this exact build** ([run 36296076828](https://github.com/sleepypod/core/actions/runs/36296076828) · `936ea61`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/936ea613c965e640f237995ec4c8301254f23535/scripts/install \ | sudo bash -s -- \ --branch ci/merge-group-trigger \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/36296076828/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/36296076828/artifacts/10923204552) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
## Summary PR #723's Unit Tests job failed with `Error: read ECONNRESET` in the "Upload test results to Codecov" step even though every test passed. That step uses `codecov/test-results-action@v1`, which is deprecated and logs a warning pointing to `codecov-action@v5` with `report_type: test_results`. ## Changes - `.github/workflows/test.yml` and `.github/workflows/python-modules.yml`: replace `codecov/test-results-action@v1` with `codecov/codecov-action@v5` + `report_type: test_results`, keeping the same OIDC auth, slug, flags, and files. - Set `fail_ci_if_error: false` on every Codecov upload (coverage and test results) so a Codecov network or service blip cannot turn a green test run red. Codecov's own status checks (`codecov/patch`) still report coverage independently. ## Test plan - [ ] Both workflows run green on this PR (`Unit Tests` and the Python module matrix) - [ ] Codecov shows test results and coverage for this PR's commit under the `javascript` and `python,*` flags - [ ] No deprecation warning from the test-results step in the job log 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update ci/codecov-test-results-v5 ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/ci/codecov-test-results-v5/scripts/install \ | sudo bash -s -- --branch ci/codecov-test-results-v5 ``` 🎯 **Pin to this exact build** ([run 36296207849](https://github.com/sleepypod/core/actions/runs/36296207849) · `7eeb761`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/7eeb7614d88f777ca9c367034ae81e636e9e007b/scripts/install \ | sudo bash -s -- \ --branch ci/codecov-test-results-v5 \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/36296207849/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/36296207849/artifacts/10923932850) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
## Summary Agent guidance was split across two overlapping files: `CLAUDE.md` (build/test, architecture, conventions, debugging, plus a Yggdrasil block) and `AGENTS.md` (wiki usage, a second Yggdrasil block, release rules, session completion). This PR collapses both into a single, tool-agnostic `AGENTS.md` and deletes `CLAUDE.md`. ## Changes - **`AGENTS.md`** rewritten as the one project guide: - Knowledge base (wiki-first workflow, never edit generated wiki files) - Worktrees (reuse, naming under `.codex/worktrees/<task-slug>`, local `info/exclude` rule) - Architecture (`src/hardware`, `src/server`, `src/scheduler`, `src/automation`, `src/streaming`, `src/homekit`, `src/db`, `modules/`, UI) - Invariants and validation (`withSideLock`, `globalThis` singletons, sequential DAC protocol, Stryker/Vitest/pytest expectations, uv-locked Python deps, migration journal rules) - Releases (PR-to-`main` title gate) and pod debugging pointers - **`CLAUDE.md`** removed. Its content is either carried into `AGENTS.md` or was tooling-specific boilerplate (Yggdrasil hook docs, RTK) that the `ygg` integration re-injects at session start. Net: 69 insertions, 231 deletions across the two files. ## Notes for reviewers - The Yggdrasil `<!-- BEGIN YGG INTEGRATION -->` blocks were dropped from both files. If `ygg` relies on that marker for re-stamping, it will re-add a block on next init; nothing in `.github/` or scripts references the marker. - The Vercel/RTK guidance was never in these files and is unaffected. ## Test plan - [ ] `AGENTS.md` renders cleanly on GitHub and every referenced path exists (`docs/wiki/INDEX.md`, `docs/DEBUGGING.md`, `src/server/openapi.ts`, `.github/workflows/python-modules.yml`) - [ ] Start a fresh Claude Code / Codex session in the repo and confirm the agent picks up `AGENTS.md` guidance - [ ] Confirm no CI workflow or script greps for `CLAUDE.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update docs/consolidate-agent-guidance ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/docs/consolidate-agent-guidance/scripts/install \ | sudo bash -s -- --branch docs/consolidate-agent-guidance ``` 🎯 **Pin to this exact build** ([run 36295924382](https://github.com/sleepypod/core/actions/runs/36295924382) · `95e661c`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/95e661cbcbdff96b0b914cc0a5413b885191a600/scripts/install \ | sudo bash -s -- \ --branch docs/consolidate-agent-guidance \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/36295924382/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/36295924382/artifacts/10924395055) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end -->
…728) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@tanstack/react-query](https://tanstack.com/query) ([source](https://redirect.github.com/TanStack/query/tree/HEAD/packages/react-query)) | [`5.103.1` → `5.103.2`](https://renovatebot.com/diffs/npm/@tanstack%2freact-query/5.103.1/5.103.2) |  |  | | [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`25.9.7` → `25.9.8`](https://renovatebot.com/diffs/npm/@types%2fnode/25.9.7/25.9.8) |  |  | | [@xyflow/react](https://reactflow.dev) ([source](https://redirect.github.com/xyflow/xyflow/tree/HEAD/packages/react)) | [`12.11.6` → `12.12.0`](https://renovatebot.com/diffs/npm/@xyflow%2freact/12.11.6/12.12.0) |  |  | | [drizzle-kit](https://orm.drizzle.team) ([source](https://redirect.github.com/drizzle-team/drizzle-orm)) | [`0.31.10` → `0.31.11`](https://renovatebot.com/diffs/npm/drizzle-kit/0.31.10/0.31.11) |  |  | | [drizzle-orm](https://orm.drizzle.team) ([source](https://redirect.github.com/drizzle-team/drizzle-orm)) | [`0.45.2` → `0.45.3`](https://renovatebot.com/diffs/npm/drizzle-orm/0.45.2/0.45.3) |  |  | | [eslint-config-next](https://nextjs.org/docs/app/api-reference/config/eslint) ([source](https://redirect.github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next)) | [`16.3.5` → `16.3.6`](https://renovatebot.com/diffs/npm/eslint-config-next/16.3.5/16.3.6) |  |  | | [lucide-react](https://lucide.dev) ([source](https://redirect.github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react)) | [`1.47.0` → `1.48.0`](https://renovatebot.com/diffs/npm/lucide-react/1.47.0/1.48.0) |  |  | | [next](https://nextjs.org) ([source](https://redirect.github.com/vercel/next.js)) | [`16.3.5` → `16.3.6`](https://renovatebot.com/diffs/npm/next/16.3.5/16.3.6) |  |  | | [tsx](https://tsx.hirok.io) ([source](https://redirect.github.com/privatenumber/tsx)) | [`4.23.13` → `4.23.15`](https://renovatebot.com/diffs/npm/tsx/4.23.13/4.23.15) |  |  | | [typescript-eslint](https://typescript-eslint.io/packages/typescript-eslint) ([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint)) | [`8.70.0` → `8.70.1`](https://renovatebot.com/diffs/npm/typescript-eslint/8.70.0/8.70.1) |  |  | --- ### Release Notes <details> <summary>TanStack/query (@​tanstack/react-query)</summary> ### [`v5.103.2`](https://redirect.github.com/TanStack/query/blob/HEAD/packages/react-query/CHANGELOG.md#51032) [Compare Source](https://redirect.github.com/TanStack/query/compare/@tanstack/react-query@5.103.1...@tanstack/react-query@5.103.2) ##### Patch Changes - Updated dependencies \[[`8a28904`](https://redirect.github.com/TanStack/query/commit/8a28904aaccb6bc26398b751400a181c0f6d7f0e)]: - [@​tanstack/query-core](https://redirect.github.com/tanstack/query-core)@5.103.2 </details> <details> <summary>xyflow/xyflow (@​xyflow/react)</summary> ### [`v12.12.0`](https://redirect.github.com/xyflow/xyflow/blob/HEAD/packages/react/CHANGELOG.md#12120) [Compare Source](https://redirect.github.com/xyflow/xyflow/compare/@xyflow/react@12.11.6...@xyflow/react@12.12.0) ##### Minor Changes - [#​5998](https://redirect.github.com/xyflow/xyflow/pull/5998) [`91a4adf`](https://redirect.github.com/xyflow/xyflow/commit/91a4adf4f901d2c59ed77486bc0d3ec6d3843423) - Always fire onResizeEnd after onResizeStart ##### Patch Changes - [#​5998](https://redirect.github.com/xyflow/xyflow/pull/5998) [`91a4adf`](https://redirect.github.com/xyflow/xyflow/commit/91a4adf4f901d2c59ed77486bc0d3ec6d3843423) - Fix incorrect values during resize when shouldResize returns false - Updated dependencies \[[`91a4adf`](https://redirect.github.com/xyflow/xyflow/commit/91a4adf4f901d2c59ed77486bc0d3ec6d3843423), [`91a4adf`](https://redirect.github.com/xyflow/xyflow/commit/91a4adf4f901d2c59ed77486bc0d3ec6d3843423)]: - [@​xyflow/system](https://redirect.github.com/xyflow/system)@0.0.83 </details> <details> <summary>drizzle-team/drizzle-orm (drizzle-kit)</summary> ### [`v0.31.11`](https://redirect.github.com/drizzle-team/drizzle-orm/releases/tag/drizzle-kit%400.31.11) [Compare Source](https://redirect.github.com/drizzle-team/drizzle-orm/compare/drizzle-kit@0.31.10...drizzle-kit@0.31.11) - Warn about silent migration skips on drizzle-kit checks - thanks [@​shaneikennedy](https://redirect.github.com/shaneikennedy) </details> <details> <summary>vercel/next.js (eslint-config-next)</summary> ### [`v16.3.6`](https://redirect.github.com/vercel/next.js/compare/v16.3.5...v16.3.6) [Compare Source](https://redirect.github.com/vercel/next.js/compare/v16.3.5...v16.3.6) </details> <details> <summary>lucide-icons/lucide (lucide-react)</summary> ### [`v1.48.0`](https://redirect.github.com/lucide-icons/lucide/releases/tag/1.48.0): Version 1.48.0 [Compare Source](https://redirect.github.com/lucide-icons/lucide/compare/1.47.0...1.48.0) #### What's Changed - feat(icons): added `briefcase-plus` icon by [@​tylerkade](https://redirect.github.com/tylerkade) in [#​4757](https://redirect.github.com/lucide-icons/lucide/pull/4757) - feat(icons): added `square-sparkles` icon by [@​nananecy](https://redirect.github.com/nananecy) in [#​3610](https://redirect.github.com/lucide-icons/lucide/pull/3610) - feat(icons): added `line-dot-left-horizontal` icon by [@​nathan-de-pachtere](https://redirect.github.com/nathan-de-pachtere) in [#​3855](https://redirect.github.com/lucide-icons/lucide/pull/3855) - fix(packages/svelte,solid): fix shared type imports in Solid and Svelte by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4846](https://redirect.github.com/lucide-icons/lucide/pull/4846) - chore(deps-dev): bump react-native from 0.76.9 to 0.87.1 in the react-native-deps group across 1 directory by [@​dependabot](https://redirect.github.com/dependabot)\[bot] in [#​4673](https://redirect.github.com/lucide-icons/lucide/pull/4673) - feat(packages): export \_\_iconNode data across framework packages by [@​lx3133584](https://redirect.github.com/lx3133584) in [#​4761](https://redirect.github.com/lucide-icons/lucide/pull/4761) - fix(icons): Tweak `card-sim` chip by [@​danielbayley](https://redirect.github.com/danielbayley) in [#​3649](https://redirect.github.com/lucide-icons/lucide/pull/3649) - feat(icons): added `line-dot-top-vertical` icon by [@​nathan-de-pachtere](https://redirect.github.com/nathan-de-pachtere) in [#​3856](https://redirect.github.com/lucide-icons/lucide/pull/3856) - feat(icons): added `line-dot-bottom-vertical` icon by [@​nathan-de-pachtere](https://redirect.github.com/nathan-de-pachtere) in [#​3857](https://redirect.github.com/lucide-icons/lucide/pull/3857) - fix(packages/react-native): pass testID to the rendered Svg element by [@​OlegBezr](https://redirect.github.com/OlegBezr) in [#​4881](https://redirect.github.com/lucide-icons/lucide/pull/4881) - chore([@​lucide/vue](https://redirect.github.com/lucide/vue)): Fix types `@lucide/vue` package and added workflow for it. by [@​ericfennis](https://redirect.github.com/ericfennis) in [#​4883](https://redirect.github.com/lucide-icons/lucide/pull/4883) - test(packages/shared): cover buildLucideIconForReact by [@​vugarbbakhishov-hub](https://redirect.github.com/vugarbbakhishov-hub) in [#​4872](https://redirect.github.com/lucide-icons/lucide/pull/4872) - feat(site): Better icon detail page and add unreleased flag by [@​ericfennis](https://redirect.github.com/ericfennis) in [#​4877](https://redirect.github.com/lucide-icons/lucide/pull/4877) - fix(icons): changed `map-pinned` icon by [@​jguddas](https://redirect.github.com/jguddas) in [#​4880](https://redirect.github.com/lucide-icons/lucide/pull/4880) - fix(icons): changed `mail-pen` by [@​karsa-mistmere](https://redirect.github.com/karsa-mistmere) in [#​4899](https://redirect.github.com/lucide-icons/lucide/pull/4899) - chore(deps-dev): bump the angular-deps group across 1 directory with 14 updates by [@​dependabot](https://redirect.github.com/dependabot)\[bot] in [#​4895](https://redirect.github.com/lucide-icons/lucide/pull/4895) - chore(deps): bump the vue-deps group with 3 updates by [@​dependabot](https://redirect.github.com/dependabot)\[bot] in [#​4893](https://redirect.github.com/lucide-icons/lucide/pull/4893) - chore(typchecking): More typecheck jobs for all packages by [@​ericfennis](https://redirect.github.com/ericfennis) in [#​4885](https://redirect.github.com/lucide-icons/lucide/pull/4885) - feat(icons): add house-cog icon by [@​ajaxjiang96](https://redirect.github.com/ajaxjiang96) in [#​4904](https://redirect.github.com/lucide-icons/lucide/pull/4904) #### New Contributors - [@​nananecy](https://redirect.github.com/nananecy) made their first contribution in [#​3610](https://redirect.github.com/lucide-icons/lucide/pull/3610) - [@​OlegBezr](https://redirect.github.com/OlegBezr) made their first contribution in [#​4881](https://redirect.github.com/lucide-icons/lucide/pull/4881) - [@​vugarbbakhishov-hub](https://redirect.github.com/vugarbbakhishov-hub) made their first contribution in [#​4872](https://redirect.github.com/lucide-icons/lucide/pull/4872) - [@​ajaxjiang96](https://redirect.github.com/ajaxjiang96) made their first contribution in [#​4904](https://redirect.github.com/lucide-icons/lucide/pull/4904) **Full Changelog**: <lucide-icons/lucide@1.47.0...1.48.0> </details> <details> <summary>vercel/next.js (next)</summary> ### [`v16.3.6`](https://redirect.github.com/vercel/next.js/compare/v16.3.5...v16.3.6) [Compare Source](https://redirect.github.com/vercel/next.js/compare/v16.3.5...v16.3.6) </details> <details> <summary>privatenumber/tsx (tsx)</summary> ### [`v4.23.15`](https://redirect.github.com/privatenumber/tsx/releases/tag/v4.23.15) [Compare Source](https://redirect.github.com/privatenumber/tsx/compare/v4.23.14...v4.23.15) ##### Bug Fixes - exclude bare builtins from namespace inheritance ([38e1588](https://redirect.github.com/privatenumber/tsx/commit/38e158857e50bca311be7c232a5057e5a2e5347a)) - expose require.cache and require.extensions to tsImport CommonJS modules ([2da3407](https://redirect.github.com/privatenumber/tsx/commit/2da34075afaed43e2b7fd0aca5fbebaaf337ff3a)) - make namespaced register() overloads portable for declaration emit ([562c434](https://redirect.github.com/privatenumber/tsx/commit/562c434a5c8695e74327bbeb51cfeb9b86fc7e15)) *** This release is also available on: - [npm package (@​latest dist-tag)](https://www.npmjs.com/package/tsx/v/4.23.15) ### [`v4.23.14`](https://redirect.github.com/privatenumber/tsx/compare/v4.23.13...6e5236b065738d3687a06396d064774cfede390f) [Compare Source](https://redirect.github.com/privatenumber/tsx/compare/v4.23.13...v4.23.14) </details> <details> <summary>typescript-eslint/typescript-eslint (typescript-eslint)</summary> ### [`v8.70.1`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/typescript-eslint/CHANGELOG.md#8701-2026-09-21) [Compare Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.70.0...v8.70.1) This was a version bump only for typescript-eslint to align it with other projects, there were no code changes. See [GitHub Releases](https://redirect.github.com/typescript-eslint/typescript-eslint/releases/tag/v8.70.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "before 6am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/sleepypod/core). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMTIuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjExMi4wIiwidGFyZ2V0QnJhbmNoIjoiZGV2IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyIsImZpeC1wYXRjaCIsIm5vbi1tYWpvciJdfQ==--> <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update renovate/all-minor-patch ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/renovate/all-minor-patch/scripts/install \ | sudo bash -s -- --branch renovate/all-minor-patch ``` 🎯 **Pin to this exact build** ([run 36389425165](https://github.com/sleepypod/core/actions/runs/36389425165) · `0bedb93`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/0bedb9350dfb70b24ac34f462c8e59826ecbd5fe/scripts/install \ | sudo bash -s -- \ --branch renovate/all-minor-patch \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/36389425165/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/36389425165/artifacts/10955398677) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
) Turning a rotary dial currently competes with schedules and Autopilot, so the next automated write can undo the user's adjustment. This adds a shared per-side controller: manual hold → run-once session → Autopilot → recurring schedule, with safety and explicit shutdown taking precedence. Existing temperature API calls acquire a persisted 30-minute hold. Consumers can choose `holdMinutes`, inspect ownership in status, and explicitly Resume; the temperature screen exposes these controls. Schedule/session clocks keep advancing during holds, and Resume resolves the current point without replaying missed commands. Power-on without an explicit temperature honors the current owner without acquiring a hold. Autopilot publishes bounded requests instead of writing hardware directly. Relative actions, including window expressions, use schedule/session baselines. Policy reactivation does not replay sibling one-shot effects. Hardware cutoffs remain independent of hold expiry and survive SQLite reopen, keepalive, and safety recovery. Two generated migrations add hold storage and the explicit hardware deadline. Service shutdown drains admitted writes and cancels queued reconciliation; rule deactivation cancels obsolete queued shutdowns. Corrupt stored sessions are isolated from manual control/status. Validation: full Vitest suite **4,012 passed / 1 skipped across 174 files**; production build, TypeScript, ESLint and both Drizzle schema checks passed. Final GitHub CI, including coverage, lint, typecheck, builds and branch release, is green. ESLint retains one pre-existing warning in stryker.config.mjs. Tests cover competing producers, hold expiry/refresh, SQLite reopen, queued commands, DST, recovery bounds, rule deactivation, run-once rollback, corrupt-session recovery, and UI Resume/duration controls. Two full/diff Optimizer and Skeptic review rounds plus focused repair verification completed. CodeRabbit's four initial comments were verified and addressed; follow-up review requested after push but CodeRabbit rate-limited that request. Native reviewers used the available GPT-6 session model; the skill's prescribed older models were unavailable. No live Pod deployment or hardware test performed. Raw diagnostic commands remain a documented low-level exception. Consumer contract: [docs/temperature-control.md](https://github.com/sleepypod/core/blob/feat/temperature-controller/docs/temperature-control.md). Architecture decision updated in ADR 0023.
After deploying #727 to a Pod with hundreds of schedule rows, synchronous forward cron resolution saturated the Node event loop and caused API timeouts. A read-only on-Pod benchmark took 5.7 seconds for just 20 rows; the controller repeated the full calculation every minute. Cache each row's weekly occurrence interval per side and reuse it while `startsAt <= now < expiresAt`. Crossing the next occurrence, moving the clock backward, or changing the timezone/day/time recomputes that interval. Temperature edits apply immediately; removed entries are pruned. Forward cron iteration and its existing DST behavior remain unchanged. Initial cold-cache resolution is still expensive, but ordinary minute ticks no longer repeat it. Validation: 201 focused tests pass across controller, SQLite integration, Autopilot and scheduler suites. New regressions cover minute reuse, exact boundaries, clock rollback, timezone/time/temperature edits, pruning and DST gap/fold behavior. Independent focused adversarial review approved the repair; a 400-row probe made zero parser calls across 60 warm minute resolutions. Pre-push typecheck/lint/schema/related-test gates and live Pod verification accompany this follow-up. Live validation on .88 with the repaired build: after the initial cache fill, left/right control endpoints returned HTTP200 in70ms/55ms, version in119ms, and full device status included both ownership objects. Before this fix, repeated requests timed out at15–20seconds. This is a temporary repair deployment; the final requested deployment will use merged dev. Initial cache fill remains a startup latency limitation. <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update fix/temperature-schedule-cache ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/fix/temperature-schedule-cache/scripts/install \ | sudo bash -s -- --branch fix/temperature-schedule-cache ``` 🎯 **Pin to this exact build** ([run 36392765184](https://github.com/sleepypod/core/actions/runs/36392765184) · `1175f2d`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/1175f2dcdbf9f93386a4aeb000e74ea93d1f4a91/scripts/install \ | sudo bash -s -- \ --branch fix/temperature-schedule-cache \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/36392765184/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/36392765184/artifacts/10956862456) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end -->
…ntly (#849) Settings → Gestures shows each tap twice: what it does **normally**, and what it does **while an alarm is ringing**. Both columns edited the same record, though, and one action type covered both, so changing one column changed the other: - A temperature tap changed the temperature in both columns. There was no way to make it snooze while ringing. - Setting a tap's ringing action to snooze switched the whole record to an alarm action. Its normal action then showed "Nothing". ## What changes A tap now stores its two actions separately: | | Comes from | Choices | |---|---|---| | Normal action | `actionType` | temperature step, power toggle (alarm type with `alarmInactiveBehavior: power`), or nothing | | Ringing action | `alarmBehavior` (+ `alarmSnoozeDuration`) | snooze N min or stop, for **any** action type | - **`settings.setGesture`:** the temperature variant also accepts `alarmBehavior` and `alarmSnoozeDuration`. Existing alarm-type rows keep working unchanged, so no migration is needed. - **`gestureActionHandler`:** - While an alarm is vibrating, a tap does only its ringing action. A temperature tap no longer also nudges the temperature as the alarm stops. - With no ringing action configured, the tap just records the alarm as stopped; the firmware stops vibration on any tap. - Otherwise the normal action runs. - The resilient clear from dev is kept: a failed `clearAlarm` write is logged and doesn't drop the snooze. - **`TapGestureConfig`:** the editor has two sections, "Normally" and "While an alarm is ringing". Saving writes both, whichever column opened it. The summary row describes each column independently. ## Tests - **Handler:** while ringing, temperature taps snooze or stop and don't change the temperature. When idle, the same tap steps the temperature. A tap with no ringing action only records the alarm as stopped. - **Settings router:** a temperature gesture saves and returns its ringing action. - **`TapGestureConfig`:** editing the ringing column keeps the normal action, and vice versa. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…acts, and run them on the pod (#855) ## Why sleepypod-ios trains personal stage models on the phone (`docs/design/on-device-stage-training.md`, sleepypod/ios#65). The design calls for the same two models to be trained off the pod in the analysis container on the same features, and for a path to run the result on the pod as an updated algorithm. ## What - **Container training** (`docs/research/sleep-stage-calibration/personal-models.py`): logistic regression and random forest (200 trees, depth 4) on feature contract A (the challenger's z-scores) or B (pod native), 5-minute epochs labelled by the Watch stage at the midpoint, inputs restricted to the bed window, rare-class oversampling, leave-one-night-out κ pooled and per night, single-class folds skipped. `--fixture` writes a synthetic parity fixture (never a real night unless `--fixture-real`, which must not be committed). - **Portable export** (`--export`): `personal-<kind>.json`, the pod's form (forest trees as parallel arrays with four-class leaf votes; logistic as scaler plus weights), with the feature contract, hyperparameters and test vectors, cross-checked against scikit-learn on every training row before writing. `.mlmodel` too when coremltools is installed. - **Pod evaluator** (`src/lib/personal-stage-model.ts`): zod schema for the document, the two feature contracts ported exactly (the synthetic fixture is replayed in `src/lib/tests/personal-stage-model.test.ts`), forest and logistic evaluation, test-vector validation, and `classifyWithStageModel` with the rules engine's outlier filter, smoothing and transition constraints. - **Stored model per side** (`side_stage_models`, migration 0030) and router procedures `sleepStages.getModel`, `setModel` (PUT /sleep-stages/model; refuses an unknown contract or a model failing its own vectors) and `previewModel` (POST /sleep-stages/model/preview; stages recent nights with the stored or an inline model and scores it against the Watch beside the side's rules, per night and pooled). - README section 6 documents the run, the two parity checks and the pod upload. Not in this PR: making the model the side's live classifier. That is pinned to the hashed biometrics manifest through the profile system and needs its own migration; the stored model is scored on read beside the rules. ## Results on the ten left-side nights to 2026-10-19 | | logistic κ | forest κ | |---|---|---| | contract A, leave-one-night-out | 0.08 | 0.05 | | contract B, leave-one-night-out | 0.11 | 0.20 | The container's contract-B forest evaluated by the pod's TypeScript over the real nights: in-sample κ 0.33, 56%. Bundled challenger on the same nights κ 0.12; pod rules about 0.02. ## Test plan - [x] `vitest`: 7 evaluator tests (forest, multinomial and two-class logistic, feature parity with the Python fixture, epoch grid, smoothing) and 3 router tests (store/remove, refusal, preview scored beside rules); schema contract tests updated for the new table. - [x] `tsc --noEmit` clean; eslint clean on the changed files. - [x] Real data: exported forest verified against scikit-learn on 724 rows; pod evaluator run over 11 nights from a pod snapshot. - [ ] CI (the local pre-push hook's waveform integration test needs the piezo-processor venv, absent in this worktree). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update docs/stage-model-parity ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/docs/stage-model-parity/scripts/install \ | sudo bash -s -- --branch docs/stage-model-parity ``` 🎯 **Pin to this exact build** ([run 39495974979](https://github.com/sleepypod/core/actions/runs/39495974979) · `d2c4c06`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/d2c4c06494312db10696fa93b854d3dbc2f1ed8e/scripts/install \ | sudo bash -s -- \ --branch docs/stage-model-parity \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/39495974979/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/39495974979/artifacts/12297128021) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Added support for personal sleep-stage models, including logistic and forest-based models. * Models can be uploaded, stored per side, validated, previewed, and compared with the existing rules-based classification. * Added sleep-stage scoring against reference nights, including per-night and pooled agreement results. * Added a training workflow for creating models from pod biometric data, with optional automatic upload. * **Documentation** * Added guidance for model training, validation, export, deployment, and feature compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
) ## Why Each training run leaves a ~75 MB biometrics snapshot in the work directory; nothing else the pipeline produces is larger than half a megabyte. ## What `scripts/train-personal-models.py` deletes the snapshot it took once the dataset is built, unless `--keep-snapshot` is passed or the snapshot was supplied with `--snapshot`. README notes the footprint: ~0.5 MB per run on the Mac, one replaced model row per side on the pod. ## Test plan - [x] Ran against eight-pod.local with `--no-upload`: snapshot removed, work directory 1.2 MB after the run. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Added a `--keep-snapshot` option to retain the locally generated database snapshot after dataset generation. * **Documentation** * Clarified required imported Watch stages and retained training outputs. * Documented snapshot cleanup behavior and model replacement behavior during uploads. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
## Why A leave-one-night-out sweep on the developer's eleven nights (contract B): 200 trees at depth 3/4/6/8/unlimited gave κ 0.18/0.21/0.22/0.26/0.23; 100–1000 trees changed nothing; logistic regression was flat across C; histogram gradient boosting at depth 3 reached 0.27 but within noise and needs its own export. Depth 8 is a free +0.05 κ. ## What `personal-models.py` HYPER forest maxDepth 4 → 8 (trees stay 200); README records the sweep. The iOS trainer's pinned hyperparameters change in a matching sleepypod-ios PR. ## Test plan - [x] Retrained and uploaded to eight-pod.local with the new depth; the pod stored and validated it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update feat/forest-depth-8 ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/feat/forest-depth-8/scripts/install \ | sudo bash -s -- --branch feat/forest-depth-8 ``` 🎯 **Pin to this exact build** ([run 39510007068](https://github.com/sleepypod/core/actions/runs/39510007068) · `ce6a08a`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/ce6a08a6a85012c111cc3b95d3c69bc12cac1dcc/scripts/install \ | sudo bash -s -- \ --branch feat/forest-depth-8 \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/39510007068/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/39510007068/artifacts/12304803970) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…859) ## Why The pod stored and scored the Mac-trained personal model (#855) but still staged every night with the rules. Nobody customises rulesets, so rather than adding a model engine to the hashed biometrics manifest (which would invalidate the pod's runtime projection and the profile system's revisions), the model becomes a per-side switch beside the configured rules. ## What - `side_stage_models.active` (migration 0031): when set, `biometrics.getSleepStages` and the Watch comparison stage the side with the stored model over the record window, falling back to the rules for any night the model cannot stage. The profile's classifier is unchanged. - `sleepStages.setModelActive` (PUT /sleep-stages/model/active) and `getModel.active`; `getSleepStages` returns `stagedBy: { engine: 'rules' | 'model', version }`. - Web: a "Personal model" card on Settings → Sleep stages shows the stored model, its held-out κ and the "Stage nights with this model" switch; the Night view marks model-staged nights. - README documents the switch and why no manifest migration was needed. ## Test plan - [x] Router test: activation refused without a model; after `setModel` + `setModelActive`, `getSleepStages` returns 12 five-minute model epochs with `stagedBy.engine = 'model'`; removing the model returns to rules. 158 router tests pass, schema contract snapshot updated, 40 editor component tests pass including the new card. - [x] tsc clean, eslint clean on changed files. - [ ] After deploy: switch on for the left side, confirm the web Night view shows "Personal model" and the phone's Night page (which reads its own fetched copy) agrees. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update feat/model-stage-engine ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/feat/model-stage-engine/scripts/install \ | sudo bash -s -- --branch feat/model-stage-engine ``` 🎯 **Pin to this exact build** ([run 39517842759](https://github.com/sleepypod/core/actions/runs/39517842759) · `22f0d2b`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/22f0d2bfcbffa4946557a8beb80c2f5877853dbc/scripts/install \ | sudo bash -s -- \ --branch feat/model-stage-engine \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/39517842759/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/39517842759/artifacts/12310145377) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit - **New Features** - Activate a stored personal sleep-stage model separately for each side through Settings or the API. - Active models classify sleep stages and automatically fall back to profile rules when needed. - Sleep-stage results identify whether rules or a personal model produced them, including the model version. - Night view labels nights staged by a personal model. - Settings now displays model details, validation metrics, activation status, and errors. - **Documentation** - Updated guidance for activating and using personal sleep-stage models. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Beat-to-beat heartbeat detection in the piezo-processor, stored per minute in a new `heartbeats` table. Vitals are unchanged: heart rate and HRV in `vitals` still come from the versioned window estimator. ## Why The window estimator gives one heart rate per minute. Beat times are what HRV (RMSSD and friends) and heart-rate-based sleep staging need, and a beat-derived heart rate tracks the pulse more closely. On a Pod 4 overnight recording against a wrist-worn reference, the beat-derived rate was within 5 bpm 97% of the time (median absolute error 1.6 bpm), covering 84% of in-bed minutes. This PR only records the beats. Using them for `vitals.heart_rate` / `vitals.hrv` would change those columns' meaning, which the biometrics manifest versions (`hrv_index` is `window_ibi_variability_v1`), so that's left for a follow-up that registers beat-derived metrics properly. ## What **`beats.py`, per side, on a 20 s window advancing 5 s** (only the central 5 s of beats are committed, so every beat is decided with context on both sides; ~12.5 s latency): - **Band-pass and decimate:** each piezo channel to the cardiac band, 500 → 100 Hz. - **Motion blanking:** with breaks recorded, so no interval spans a turn-over. - **Other side as a noise reference:** ridge-regression FIR, kept only if it doesn't collapse cardiac periodicity. - **Two channels:** combined when the combination beats the better single channel. - **Period selection:** subharmonic summation (Hermes 1988), with rejection of half-period and multiple lags. - **Rhythm tracking:** follows the heart rhythm rather than the strongest one; the side's window heart rate (`rate_hint`) decides where tracking starts, which keeps a strong second wave in each beat from being taken for a beat. - **Template matching and anchor continuity:** beat times share one fiducial point all night. - **Artifact correction:** intervals > 20% from the recent median are rejected; missed beats and ectopy become breaks, never interpolated (after Lipponen & Tarvainen 2019). - **Pump-gated records:** pushed masked rather than restarting the window. - **Lost-sample markers:** repaired before detection. **Occupancy:** a side's tracker only runs while piezo presence holds and the bed's capacitance presence (sleep-detector state, `BedPresence`) doesn't say it's empty; an empty bed still carries regular vibration from the pod. **Single sleeper:** each minute keeps whichever side saw the sleeper's beats best, stored under the home side. **Storage:** `heartbeats` (migration `0033`): one row per side per minute, beat times as ms offsets with `null` for a break, plus a quality score. About 150 KB per side per night (compact JSON). Pruned with the other time series, and given an archive merge rule. **Shared processor:** gains a `rate_hint` callback fed the window heart rate. It doesn't change any vitals value; the manifest's pinned source hashes for `main.py` and `processing.py` are refreshed accordingly. ## Tests - `test_beats.py`: synthetic ballistocardiograms with known beat times: ≥ 97% sensitivity, ≥ 99% PPV, < 5 ms RMS timing error on clean signals at 45–100 bpm. Also covers rhythm tracking, rate hints, double waves, masked records, anchor alignment and artifact correction. - `test_main.py`: heartbeat storage (including replayed minutes), single-sleeper routing, the occupancy gate (only present sides tracked, leaving the bed is one break), and the rate hint carrying the window rate with vitals unchanged. - Retention, archive merge and manifest contract tests updated for the new table and sources. Docs: `docs/piezo-processor.md` §5a. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added heartbeat tracking that detects and records beats for each side, with heart-rate hints and support for single-sleeper mode. * Improved handling of motion, signal gaps, sensor transitions, and masked periods to support more reliable beat records. * Added heartbeat data to biometrics archiving and retention. * **Documentation** * Added an overview of heartbeat detection, data quality checks, limitations, and processing estimates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
## Changes
Users can keep multiple personal sleep-stage models per side and upload
`candidate.json` directly from Settings → Sleep stages, including when
no model exists. The person selector applies to both models and rules.
Choosing a saved candidate only previews its metadata; **Use this
model** explicitly activates it. The settings card presents a held-out
evaluation scorecard, separated rules/model scale labels, per-stage
recall when exported, saved-model rows, a Rules only option, and an
Upload model button. Missing baseline/per-stage/per-night evaluation
data is explicitly unavailable. Model versions and scores use monospace,
with plain-language explanations.
A new configuration-database library carries existing models forward
without changing activation. New plural model endpoints list summaries,
save validated candidates idempotently, and select a saved model for a
side. Legacy singular endpoints remain compatible and archive
replacements so older CLI/iOS clients do not discard earlier models.
The nightly Sleep view overlays imported Apple Watch stages on the pod
hypnogram and identifies model-generated nights with a brain-icon chip.
Below that card, Nights now reuses the exact Biometrics chart/explorer
with recorded per-minute readings, shared colors, axes, zoom, hover, and
movement. Both surfaces show Watch heart rate, SDNN, and breathing
references. Watch HRV/breathing use circular outlined markers in the
shared purple/blue metric colors; gaps remain gaps, and SDNN stays
explicitly distinct from the pod's IBI variability index.
## Validation
- 101 focused router, migration, schema, and UI tests passed; a broader
pre-push related suite passed 1,483 tests across 113 files; the final
incremental push passed all 75 related tests.
- TypeScript and changed-file ESLint checks passed.
- 124 focused sleep/biometrics tests and 138 settings tests passed,
covering Watch stage rendering and gaps, HRV timestamp positioning,
side/metric/time filtering, and error fallback.
- Browser verified first-model upload, a second candidate without
activation, explicit switching, and selection persistence after reload
against disposable local databases.
- Model contract and test vectors are validated before saving/selection;
cross-side selection is rejected.
## Deployment verification
Deployed the feature on the `bae14c22` engine baseline to preserve the
existing pod profile's pinned manifest. Verified original personal-model
hashes and activation states after migration, and confirmed the
numerical consumers acknowledged the unchanged pre-deploy configuration
hash. The newer heartbeat engine on `dev` changes the manifest and is
outside this feature's scope.
<!-- sleepypod:install-start -->
<!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block.
Hello fellow agent / curious human! Anything between the
sleepypod:install-start and sleepypod:install-end markers
gets overwritten on the next CI run. Want to change what's
here? Edit .github/workflows/build.yml instead. 💤 -->
<details open>
<summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary>
Pick the snippet that matches your pod. **Already-installed pods can't
use the curl bootstraps below** — sleepypod's egress firewall DROPs
github.com, and the script that knows how to unblock WAN is the very
file curl is trying to fetch. Use `sp-update` instead; it's on disk and
opens WAN as its first step.
🔄 **Already on a sleepypod** (most common — review a PR on a running
pod):
```bash
sudo sp-update feat/stage-model-library
```
🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every
push:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/feat/stage-model-library/scripts/install \
| sudo bash -s -- --branch feat/stage-model-library
```
🎯 **Pin to this exact build** ([run
39708837795](https://github.com/sleepypod/core/actions/runs/39708837795)
· `56a24a6`) — fresh-pod path, locked to one CI artifact for
reproducible review:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/56a24a667f70ec10014aaa9032d05c6b018677a0/scripts/install \
| sudo bash -s -- \
--branch feat/stage-model-library \
--artifact-url 'https://nightly.link/sleepypod/core/actions/runs/39708837795/sleepypod-core.zip'
```
🧊 Artifact:
[`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/39708837795/artifacts/12395489438)
· self-destructs in 30 days 💥
</details>
<!-- sleepypod:install-end -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
- **New Features**
- Added a per-side sleep-stage model library in Settings, with model
versions, evaluation scores, and active status.
- Save model candidates without activating them, explicitly select a
saved model, or switch to rules-only staging.
- Compare Apple Watch sleep stages with staged sleep in nightly charts.
- View nightly vitals alongside Apple Watch heart-rate variability and
breathing-rate readings.
- Include saved models in backups and preserve existing activation
settings during migration.
- **Bug Fixes**
- Validate model uploads and selections, prevent duplicate saved models,
and preserve previous models when replacements are uploaded.
- Show errors when reference sleep stages or vitals can’t be loaded.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary Adds five MCP tools in `src/mcp/tools/stageModels.ts`. Each wraps an existing `sleepStages` procedure, so contract and test-vector validation are unchanged: - `get_stage_models` (read-only): the side's model library, newest first, with `selected`/`active` flags. - `preview_stage_model` (read-only): stages the last N nights (default 7) with an inline candidate, or the selected model when none is given. It returns pooled and per-night agreement with the Watch next to the rules'. Per-epoch blocks are dropped, and the result notes that training nights are in-sample. - `add_stage_model`: stores a model in the library without selecting it. - `select_stage_model`: selects a library entry (`active` decides whether it stages nights) and returns the previously selected entry so it can be restored. - `set_stage_model_active`: turns the selected model on or off; off returns the side to the rules. Tool descriptions and the server instructions tell hosts to preview first, report in-sample scores as such, change one side at a time, and select or activate only after the user approves. `PUT /sleep-stages/model`, which replaces the model in place and keeps it active, is deliberately not exposed. **Size:** inline models are capped at 256 KB of JSON. Logistic models fit. The forests sleepypod/training exports are about 0.9–2.2 MB, too large for a host to write into a tool call, so the error and the docs point to `POST /api/sleep-stages/models` with a ready `jq`/`curl` command. After that upload, these tools preview, select and activate the model. The tool table and a "Personal stage models" section are added to `src/mcp/README.md`. ## Test plan - [x] 13 new tests in `src/mcp/tests/server.test.ts`: - listing, with and without an active model; - preview: the default 7 nights, an inline candidate for a single record, and no stored model; - add: success, a refused contract or test vectors, the size cap on add and on preview; - validation of a malformed model and an unknown side; - select, including the previous-entry restore info, no previous entry, and an unknown id; - turning the model off and activating without a model; - annotations, and `set_stage_model` not being exposed. - [x] `stageModels.ts` coverage: 100% statements, branches, functions and lines. - [x] `vitest run src/mcp app/api/mcp`: 96 passed. `eslint src/mcp` is clean, `tsc --noEmit` passes. - [ ] Manual check: `claude mcp add --transport http sleepypod http://<pod>:3000/api/mcp`, then `get_stage_models` and `preview_stage_model` on a pod running `dev`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016Wac1uHSpomCEBtwBdw1YA <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update feat/mcp-stage-models ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/feat/mcp-stage-models/scripts/install \ | sudo bash -s -- --branch feat/mcp-stage-models ``` 🎯 **Pin to this exact build** ([run 39892878560](https://github.com/sleepypod/core/actions/runs/39892878560) · `a40feb7`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/a40feb70ee286574645efb4ab553411680852717/scripts/install \ | sudo bash -s -- \ --branch feat/mcp-stage-models \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/39892878560/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/39892878560/artifacts/12475318447) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Added tools to list, preview, upload, select, and activate personal sleep-stage models. * Previews can compare a candidate or selected model with rules and Apple Watch references, using one sleep record or up to 30 recent nights. * Uploading a model does not automatically select it. Selecting a model reports the previous selection, and deactivating a model returns staging to rules. * Inline model uploads are limited to 256 KB; larger models can be uploaded through REST. * **Documentation** * Added guidance on model management, validation, and selection. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…les missing' while allowed (#856) Two firewall problems on a Pod 4: the internet access toggle fails after every reboot, and Health shows "Firewall rules missing" whenever internet access is allowed. ## 1. The toggle fails after a reboot Settings → Network → Internet access failed with: ``` Failed to update iptables: Command failed: /usr/sbin/iptables -F modprobe: ERROR: could not insert 'ip_tables': Operation not permitted iptables v1.8.7 (legacy): can't initialize iptables table `filter': Table does not exist (do you need to insmod?) ``` The service runs iptables as `sleepypod` with `CAP_NET_ADMIN`, which can change rules but not load kernel modules. A Pod 4 boots without `ip_tables`, so legacy iptables tries `modprobe` and fails. It only worked when something running as root had already run iptables (and loaded the module) since boot. **Fix:** a root `ExecStartPre` loads `ip_tables`, `iptable_filter` and `iptable_nat`, all of which ship with the Pod 4 kernel (`5.15.37-mtk`). - It's best-effort (`; true`), so kernels with these built in still start. - It sets its own `PATH` to sbin, since the unit's `Environment` PATH has no sbin and `modprobe` lives in `/sbin`. - Fresh installs get it in the unit (`scripts/install`). Existing pods get it as a drop-in from `sp-update` (`iptables-modules.conf`), following the same pattern as the other self-healing drop-ins. ## 2. "Firewall rules missing" while internet access is allowed The health check and the startup repair require allow rules for mDNS (UDP 5353), the LAN (192.168.0.0/16) and NTP (UDP 123). Allowing internet access flushes every rule, so the footer showed "Firewall rules missing" even though nothing was blocked. Those rules only matter in front of a drop. A chain with `policy ACCEPT` and no `DROP`/`REJECT` rule now satisfies them, in both `checkIptables` (startup repair) and `checkIptablesCached` (Health). A listing without a chain header keeps the old behaviour. **Related:** `blockWan` didn't add the mDNS rules the check requires. Bonjour multicast (`224.0.0.251`) is outside the RFC 1918 ranges it allows, so blocking internet access also blocked app discovery until the next restart's repair inserted them. `blockWan` now adds the same three mDNS rules. ## Tests - **`scripts/tests/iptablesModules.test.ts`:** - `scripts/install` and `sp-update` carry the same root `ExecStartPre`, with an sbin PATH and no `$` expansion. - The `sp-update` drop-in block writes once and calls `daemon-reload` only on change. - The command exits 0 when `modprobe` is missing. - **`iptablesCheck`:** an open chain (policy ACCEPT, no drop) satisfies the allow rules on both paths, and a chain with a `DROP` still requires them. - **`system` router:** the `blockWan` command sequence includes the mDNS rules. - **Gates:** vitest for these files, `system` and `health` (168 passed), `tsc --noEmit` and eslint are clean, and the full pre-push gate passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * The service now attempts to load required firewall modules before starting. * mDNS traffic to the local multicast address is allowed while existing LAN-only firewall restrictions remain in place. * **Bug Fixes** * Firewall health checks now recognize permissive chains as allowing traffic, while continuing to flag chains with blocking rules. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Jonathan Ng <jon@jonathanng.com>
Moves the snooze length from tap gestures to alarms, and adds **Snooze taper**: each later snooze of the same alarm is shorter than the one before. ## Why How long a snooze lasts is a property of the alarm being snoozed, not of the tap that snoozes it, and the HomeKit switch always snoozed for a fixed 9 minutes. A taper is a common ask for heavy snoozers: fixed snoozes make it easy to drift back into deep sleep, whereas shorter and shorter ones ease you out of bed. ## What **Alarm editor → Snooze** - **Snooze for:** 1–30 min, default 5. - **Snooze taper:** Off, 30, 40, 50 or 60%. *"Each snooze is 30% shorter than the last, so you ease out of bed instead of drifting back off: 9, 6.3, 4.4, 3.1 min…"* The preview updates as you change either setting. - The alarm card shows `9 min snooze −30%`, and alarms with different snooze settings are kept as separate cards. **Tap gestures:** the editor now only picks Snooze or Stop alarm. A note says the length is set on each alarm, and the list shows `Snooze`. **Runtime** - `alarm_schedules` gains `snooze_duration` (default 300) and `snooze_taper` (default 0), in migration `0033`, exposed through the schedules router (create, update, batch). - The scheduled alarm carries both settings in its `AlarmConfig`. `snoozeManager` brings a snoozed alarm back with `snoozeCount` one higher. - `nextSnoozeSeconds` (`src/hardware/snoozeLength.ts`, pure) works out the next snooze: `snoozeDuration × (1 − taper)^count`, never under 5 seconds (or the set length, if that's shorter). - Tap gestures and the HomeKit switch snooze for that. An alarm without these settings, such as one started from the API, keeps the old defaults: 5 min from a tap, 9 min from HomeKit. - `device.snoozeAlarm` still takes an explicit duration. **Migration `0034` (custom):** copies each side's existing gesture snooze length onto its alarms, so nobody's snooze changes length. If several taps snooze, the one with the fewest taps wins; a side with no snoozing tap keeps the 5 min default. The `tap_gestures.alarm_snooze_duration` column stays in place but is no longer read. Docs: `docs/hardware/alarms.md` § Snooze length and Snooze taper. ## Tests - **`snoozeLength.test.ts`:** - the alarm length or the fallback; - no taper; - 9 min at 20% giving 540/432/346/276 s; - tapering below a minute, the 5-second floor, and a set length under it; - out-of-range input. - **`snoozeManager`:** a snoozed alarm comes back counting the snooze, also when restored from disk. - **`gestureActionHandler`:** a tap snoozes for the ringing alarm's length rather than the tap's legacy value, and the third snooze of a 9 min, 20% alarm lasts 346 s. - **`AlarmEditor`:** loads and saves both settings, the preview text updates, and a new alarm defaults to 5 min with no taper. The wake-window tests are now scoped to their own control. - **`AlarmSection`:** groups by snooze settings, and the card summary includes them. - **`TapGestureConfig`:** no length control, the hint is shown, and saves no longer send a length. - **Migrations test:** gesture lengths are copied per side; the double tap wins over the triple tap, a stop tap is ignored, and a side without a snoozing tap gets the default. - Router, scheduler, schema, demo-worker and snapshot contracts are updated. - **Gates:** `tsc --noEmit` and eslint are clean. Vitest for hardware, homekit, Schedule, Settings, server, scheduler, db, demo, mcp and hooks: 3,700 passed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit - **New Features** - Configure snooze length for each alarm, from 1 to 30 minutes, with optional tapering at 30%, 40%, 50%, or 60%. - Preview how repeated snoozes shorten, with a minimum snooze of 5 seconds. - View snooze settings in alarm details. Tap and HomeKit snoozes now use the ringing alarm’s settings when available. - New alarms default to a 5-minute snooze with tapering off. - **Updates** - Snooze length is now configured per alarm rather than per tap gesture. HomeKit snoozes without an available alarm continue to default to 9 minutes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Jonathan Ng <jon@jonathanng.com>
#866) The training command stores candidates with POST /sleep-stages/models and prints an inline-model preview. It selects only with --select (inactive) or --activate (active), after a successful preview. The README now describes the singular PUT accurately: it replaces the selection, preserves activation, and archives both models. Explicit unlabeled rows now break Watch label segments even when the next labeled row has the same stage. Validation: 9 synthetic tests (plus 3 parameterized subtests) passed. train-personal-models.py line coverage 98.8%; the changed label function is fully covered (the standalone research helper overall is 19.1% in this focused suite). Tests cover request methods/bodies, default command behavior, selection flags, failed preview, HTTP errors, contradictory flags, snapshot cleanup, and label gaps. No model was sent to or activated on a pod. Training PR #11 copies the helper from reviewed revision `18da4f6313064113fc970a0ccec542ed7fa047a0`; both synthetic parity pipelines pass. All 66 repository-script tests pass (plus 3 parameterized subtests). The scripts CI job now uses the locked piezo numerical dependencies because the research helper imports NumPy. Full pre-push TypeScript, ESLint and both Drizzle checks passed. CodeRabbit mirror review: ng#34. Initial actionable findings are addressed; follow-up review requested.
#867) Watch-only nights now build sessions with null pod metrics, allowing HR, SDNN or breathing samples to render without invented pod readings. Biometrics shows the same SDNN comparability warning as NightVitals. Demo Watch samples use a fixed Unix five-minute grid and distinct SDNN/breathing values, so overlapping date queries agree. Validation: 174 related UI/demo tests passed with two workers after one default-concurrency demo-link test hit its five-second timeout under severe host load. TypeScript, ESLint and both Drizzle checks passed. Combined changed-file line coverage: 95.5% (NightVitals 100%, BiometricsPage 91.7%, demo handler 97.7%). Regression cases cover each Watch-only metric, conditional warning visibility, overlapping-window stability and distinct values. No physical Watch or pod validation was performed. CodeRabbit mirror review: ng#35. Initial actionable findings are addressed; follow-up review requested. Revalidated 2026-10-24: all 174 related UI/demo tests pass; combined changed-file line coverage 95.46% (NightVitals 100%, BiometricsPage 91.66%, demo handler 97.7%).
Heartbeat database outages now stop at the first failed queued transaction and wait five seconds after its completion before retrying. Chunks drain in order after recovery; the existing bounded queue policy is preserved, and reconnect accounting advances only for actual attempts. Idle reader polls retry without new piezo records, and shutdown gets one bounded final drain. The runtime manifest and browser-worker fingerprint are regenerated. Reference-sample tests use a fixed clock so a short live demo night cannot fail unrelated CI at bedtime. Validation: - 290 piezo tests passed using locked dependencies, including locked-database/I/O outages, backoff, reconnect cadence, ordering, idle retries, shutdown, and overflow. Runtime main.py coverage: 84% including branches. - 3 worker contract and 8 reference-sample tests passed. Fingerprint generated with isolated dependencies matches CI output. - Full pre-push TypeScript, ESLint, and both Drizzle checks passed. - No physical pod outage was induced. Mirror review: ng#33. Final CI and incremental review are running. <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update fix/heartbeat-retry-1022 ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/fix/heartbeat-retry-1022/scripts/install \ | sudo bash -s -- --branch fix/heartbeat-retry-1022 ``` 🎯 **Pin to this exact build** ([run 40142661083](https://github.com/sleepypod/core/actions/runs/40142661083) · `8e2714c`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/8e2714c0f486938dab4efb67fc2c154027e7cac5/scripts/install \ | sudo bash -s -- \ --branch fix/heartbeat-retry-1022 \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/40142661083/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/40142661083/artifacts/12578903535) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Bug Fixes** * Heartbeat data is retried after temporary write failures, including during idle polling, with a delay between attempts. * Queued heartbeat data is kept in order, with the oldest entries dropped if the retry queue exceeds its limit. * Pending heartbeat data receives a final write attempt during shutdown. * When leaving single-sleeper mode, held minutes are written before per-side data chunks. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
) Upgrades Next.js to **16.4.3**. The installed 16.4.0 is affected by three advisories, and `next build` now refuses to build it, so every production build and deploy of `dev` fails with `SecurityFatalError: Your version of Next.js is affected by a known security vulnerability`. | Advisory | Severity | Issue | |---|---|---| | [GHSA-2464-fhr4-w9xw](GHSA-2464-fhr4-w9xw) | critical | Unauthenticated RCE in the Image Optimization API when SVG files are used | | [GHSA-673q-mjm6-r5xr](GHSA-673q-mjm6-r5xr) | critical | Unauthenticated RCE in `use cache` functions returning typed arrays | | [GHSA-p3m5-59mp-jv5r](GHSA-p3m5-59mp-jv5r) | high | Denial of service in the App Router using Server Actions | All three affect `>=16.0.0 <=16.4.0` and are fixed from 16.4.1. The pod's API is unauthenticated on the LAN, so the RCE paths are worth closing regardless of the build gate. ## Changes - **`next` and `eslint-config-next`:** `^16.1.6` (locked at 16.4.0) → `^16.4.3`, so `@next/env`, `@next/swc-*` and `@next/eslint-plugin-next` move with them. - **Transitive updates:** pnpm also refreshed a few dependencies of Next.js (`@babel/parser`/`types`, `browserslist`, `caniuse-lite`, `baseline-browser-mapping`). - **Hashes:** the biometrics manifest pins `pnpm-lock.yaml` (`dependencyHashes`), so that hash is refreshed and the demo worker was rebuilt (`worker-build.json`). ## Checks - `pnpm build` completes with no security stop; 44/44 static pages generated. - `tsc --noEmit` and `eslint . --max-warnings=0` are clean. - Full vitest run, plus pytest `common` (424 passed). The only failures: - load timeouts that pass on rerun (`waveform-publication`, `runtime-integration`, `worker-child`); - `src/mcp/tests/server.test.ts > get_sleep_stage_rules counts an in-bed night…` (`epochs` 72 vs 144). This **fails on `dev` without this change too**, so it's unrelated and probably date-dependent. - The full pre-push gate passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Chores** * Updated the application framework and its associated development tooling. * Refreshed internal build and package verification records. * Updated automated checks for sleep-stage data across the rolling 14-day window. * No user-facing changes are identified in this update. These changes update project tooling and verification records; they do not introduce new app features or alter the experience described in these release notes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Jonathan Ng <jon@jonathanng.com>
…rd types (#872) ### Summary Following recent STM32 microcontroller firmware upgrades (cover sensor STM32G0 to `v5.5.22.0` and freezer thermal controller STM32 to `v1.6.114`), the firmware emits three telemetry record types over NATS JetStream: - `sensHealth`: Cover sensor health and link connectivity (`left.connected`, `right.connected`, `version: 1`). - `frzWater`: Water level, capacitive reading, and priming status (`isPriming`, `hasWater`, `capValid`, `capLevel`, `needsPriming`). - `lps`: Linear Pressure Sensor raw load-cell samples (200 Hz binary waveform buffers). In `modules/common/dialect.py`, `KNOWN_RECORD_TYPES` is an allowlist intended to surface genuinely unknown firmware types once on startup while quietly ignoring known telemetry belonging to other subsystems. Because these types were missing, `sleep-detector` (and other biometrics daemons) logged startup warnings for each type. Additionally, in the TypeScript WebSocket streaming server (`src/streaming/piezoStream.ts`), `ALL_SENSOR_TYPES` flagged `frzWater` and `sensHealth` with `[sensorStream] unknown sensor frame type ...`. This PR: 1. Adds `"sensHealth"`, `"frzWater"`, `"lps"`, `"tap-gesture"`, and `"alarm-info"` to `KNOWN_RECORD_TYPES` in `modules/common/dialect.py`. 2. Updates `modules/common/test_dialect.py` to assert these types are recognized. 3. Adds `'frzWater'` and `'sensHealth'` to `ALL_SENSOR_TYPES` in `src/streaming/piezoStream.ts`, `src/hooks/useSensorStream.ts`, and `src/demo/sensorFrames.ts`. 4. Defines `FrzWaterFrame` and `SensHealthFrame` TypeScript interfaces, added to the `SensorFrame` union. 5. Adds unit tests in `src/streaming/tests/piezoStream.test.ts` verifying subscriptions to `frzWater` and `sensHealth` function cleanly without unknown-type warnings. --- ### Verification #### Automated Tests - **Python Unit Tests:** 242 tests passing in `modules/common`: ```bash cd modules && uv run --with pytest --with cbor2 pytest common # ============================= 242 passed in 0.92s ============================== ``` - **TypeScript Vitest:** ```bash pnpm vitest run src/streaming/tests/piezoStream.test.ts # ✓ src/streaming/tests/piezoStream.test.ts (119 tests) pnpm vitest run src/hooks/tests/useSensorStream.test.ts # ✓ src/hooks/tests/useSensorStream.test.ts (84 tests) ``` #### Live Hardware Verification (Pod 3 Hub) Tested live on a physical Pod 3 Hub (MediaTek MT8365 Pumpkin, running frankenfirmware `3c552d6`, cover sensor `v5.5.22.0`, thermal controller `v1.6.114`). **Before (startup warnings in systemd journal):** `journalctl -u sleepypod-sleep-detector`: ```text Oct 28 00:00:33 eight-pod python[338]: 2026-10-28T00:00:33 [sleep-detector] WARNING Unknown record type 'lps' — passing through without normalization (sleep-detector) Oct 28 00:00:33 eight-pod python[338]: 2026-10-28T00:00:33 [sleep-detector] WARNING Unknown record type 'frzWater' — passing through without normalization (sleep-detector) Oct 28 00:01:30 eight-pod python[338]: 2026-10-28T00:01:30 [sleep-detector] WARNING Unknown record type 'sensHealth' — passing through without normalization (sleep-detector) ``` `journalctl -u sleepypod` (Next.js server): ```text Oct 28 00:00:43 eight-pod sh[333]: [sensorStream] unknown sensor frame type "frzWater" — broadcasting but not ingesting Oct 28 00:01:30 eight-pod sh[333]: [sensorStream] unknown sensor frame type "sensHealth" — broadcasting but not ingesting ``` **After (updated `dialect.py` deployed to `/opt/sleepypod/modules/common/dialect.py` and services restarted):** `journalctl -u sleepypod-sleep-detector -b --since "1 minute ago"`: ```text Oct 28 01:21:27 eight-pod systemd[1]: Stopped SleepyPod Sleep Detector (Session Boundaries and Movement). Oct 28 01:21:27 eight-pod systemd[1]: Started SleepyPod Sleep Detector (Session Boundaries and Movement). ``` `systemctl status sleepypod-sleep-detector`: ```text ● sleepypod-sleep-detector.service - SleepyPod Sleep Detector (Session Boundaries and Movement) Loaded: loaded (/etc/systemd/system/sleepypod-sleep-detector.service; enabled; vendor preset: disabled) Active: active (running) since Wed 2026-10-28 01:21:27 UTC; 12s ago Main PID: 1215 (python) ``` All three daemons (`sleepypod-sleep-detector`, `sleepypod-piezo-processor`, `sleepypod-environment-monitor`) ingest frames cleanly with zero unknown record warnings. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit - **New Features** - Added support for receiving and filtering `frzWater` and `sensHealth` sensor data. - Added recognition for additional record types, including `lps`, `tap-gesture`, and `alarm-info`. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Added Europe/Bucharest to the timezone options in device settings. When configuring a device’s timezone, you can now select Europe/Bucharest alongside the other available options, expanding the range of timezones selectable in the settings form. This update adds the new timezone option; all other device settings remain unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Jonathan Ng <jon@jonathanng.com>
Require Node.js >=24.22.1 <25 and pin development/CI to 24.22.1 for CVE-2026-111497. The installer previously downloaded 24.16.0 and accepted any Node major >=24, so rerunning it did not repair vulnerable Node 24 installations. It now checks the supported major and security patch floor before installation and after replacement, while retaining checksum verification. Validation: all 10 installer regression checks passed on Node 24.22.1, including missing Node, vulnerable patches, patched/newer Node 24, unsupported majors, and prereleases. The repository pre-push TypeScript, ESLint, and both Drizzle schema checks passed on Node 24.22.1. Deploying this source change does not itself upgrade existing pods; rerun the updated installer and verify the active service runtime. <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update fix/node-security-oct23 ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/fix/node-security-oct23/scripts/install \ | sudo bash -s -- --branch fix/node-security-oct23 ``` 🎯 **Pin to this exact build** ([run 40800594689](https://github.com/sleepypod/core/actions/runs/40800594689) · `81cd1b4`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/81cd1b45c7631729d150b279af18169d1ca76f8f/scripts/install \ | sudo bash -s -- \ --branch fix/node-security-oct23 \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/40800594689/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/40800594689/artifacts/12865836972) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Compatibility** * The project now requires Node.js 24.22.1 or later within the Node.js 24.x series. Earlier versions and other major versions are not supported. * **Installer** * The installer checks that the installed Node.js version meets the updated requirement and installs a supported version when needed. It also verifies the version after installation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
> [!NOTE]
> This PR body was truncated due to platform limits.
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update | Pending |
|---|---|---|---|---|---|---|
| [@babel/runtime](https://babel.dev/docs/en/next/babel-runtime)
([source](https://redirect.github.com/babel/babel/tree/HEAD/packages/babel-runtime))
| [`7.29.7` →
`7.29.10`](https://renovatebot.com/diffs/npm/@babel%2fruntime/7.29.7/7.29.10)
|

|

| pnpm.overrides | patch | |
| [@eslint/css](https://redirect.github.com/eslint/css) | [`2.0.0` →
`2.0.1`](https://renovatebot.com/diffs/npm/@eslint%2fcss/2.0.0/2.0.1) |

|

| devDependencies | patch | |
| [@lingui/babel-plugin-lingui-macro](https://lingui.dev)
([source](https://redirect.github.com/lingui/js-lingui/tree/HEAD/packages/babel-plugin-lingui-macro))
| [`6.9.1` →
`6.10.0`](https://renovatebot.com/diffs/npm/@lingui%2fbabel-plugin-lingui-macro/6.9.1/6.10.0)
|

|

| devDependencies | minor | |
| [@lingui/cli](https://lingui.dev)
([source](https://redirect.github.com/lingui/js-lingui/tree/HEAD/packages/cli))
| [`6.9.1` →
`6.10.0`](https://renovatebot.com/diffs/npm/@lingui%2fcli/6.9.1/6.10.0)
|

|

| devDependencies | minor | |
| [@lingui/core](https://lingui.dev)
([source](https://redirect.github.com/lingui/js-lingui/tree/HEAD/packages/core))
| [`6.9.1` →
`6.10.0`](https://renovatebot.com/diffs/npm/@lingui%2fcore/6.9.1/6.10.0)
|

|

| dependencies | minor | |
| [@lingui/loader](https://lingui.dev)
([source](https://redirect.github.com/lingui/js-lingui/tree/HEAD/packages/loader))
| [`6.9.1` →
`6.10.0`](https://renovatebot.com/diffs/npm/@lingui%2floader/6.9.1/6.10.0)
|

|

| devDependencies | minor | |
| [@lingui/react](https://lingui.dev)
([source](https://redirect.github.com/lingui/js-lingui/tree/HEAD/packages/react))
| [`6.9.1` →
`6.10.0`](https://renovatebot.com/diffs/npm/@lingui%2freact/6.9.1/6.10.0)
|

|

| dependencies | minor | |
| [@lingui/vite-plugin](https://lingui.dev)
([source](https://redirect.github.com/lingui/js-lingui/tree/HEAD/packages/vite-plugin))
| [`6.9.1` →
`6.10.0`](https://renovatebot.com/diffs/npm/@lingui%2fvite-plugin/6.9.1/6.10.0)
|

|

| devDependencies | minor | |
| [@playwright/test](https://playwright.dev)
([source](https://redirect.github.com/microsoft/playwright)) | [`1.58.2`
→
`1.64.1`](https://renovatebot.com/diffs/npm/@playwright%2ftest/1.58.2/1.64.1)
|

|

| devDependencies | minor | |
| [@tanstack/react-query](https://tanstack.com/query)
([source](https://redirect.github.com/TanStack/query/tree/HEAD/packages/react-query))
| [`5.104.1` →
`5.104.4`](https://renovatebot.com/diffs/npm/@tanstack%2freact-query/5.104.1/5.104.4)
|

|

| dependencies | patch | `5.104.5` |
| [@vitest/coverage-v8](https://vitest.dev/guide/coverage)
([source](https://redirect.github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8))
| [`4.1.11` →
`4.1.12`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-v8/4.1.11/4.1.12)
|

|

| devDependencies | patch | |
| [cbor-x](https://redirect.github.com/kriszyp/cbor-x) | [`1.6.6` →
`1.6.7`](https://renovatebot.com/diffs/npm/cbor-x/1.6.6/1.6.7) |

|

| dependencies | patch | `1.6.8` |
|
[eslint-config-next](https://nextjs.org/docs/app/api-reference/config/eslint)
([source](https://redirect.github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next))
| [`16.4.0` →
`16.4.2`](https://renovatebot.com/diffs/npm/eslint-config-next/16.4.0/16.4.2)
|

|

| devDependencies | patch | |
| [lucide-react](https://lucide.dev)
([source](https://redirect.github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react))
| [`1.54.0` →
`1.59.0`](https://renovatebot.com/diffs/npm/lucide-react/1.54.0/1.59.0)
|

|

| dependencies | minor | `1.61.0` (+1) |
| [mqtt](https://redirect.github.com/mqttjs/MQTT.js) | [`5.16.0` →
`5.16.1`](https://renovatebot.com/diffs/npm/mqtt/5.16.0/5.16.1) |

|

| dependencies | patch | |
| [next](https://nextjs.org)
([source](https://redirect.github.com/vercel/next.js)) | [`16.4.0` →
`16.4.2`](https://renovatebot.com/diffs/npm/next/16.4.0/16.4.2) |

|

| dependencies | patch | |
| [pnpm](https://pnpm.io)
([source](https://redirect.github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm))
| [`10.34.6` →
`10.34.7`](https://renovatebot.com/diffs/npm/pnpm/10.34.6/10.34.7) |

|

| packageManager | patch | |
| [python](https://redirect.github.com/actions/python-versions) | `3.10`
→ `3.15` |

|

| uses-with | minor | `3.15.1` |
| [react](https://react.dev/)
([source](https://redirect.github.com/react/react/tree/HEAD/packages/react))
| [`19.3.0` →
`19.3.1`](https://renovatebot.com/diffs/npm/react/19.3.0/19.3.1) |

|

| dependencies | patch | |
| [react-dom](https://react.dev/)
([source](https://redirect.github.com/react/react/tree/HEAD/packages/react-dom))
| [`19.3.0` →
`19.3.1`](https://renovatebot.com/diffs/npm/react-dom/19.3.0/19.3.1) |

|

| dependencies | patch | |
| [shadcn](https://ui.shadcn.com)
([source](https://redirect.github.com/shadcn-ui/ui/tree/HEAD/packages/shadcn))
| [`4.21.4` →
`4.22.1`](https://renovatebot.com/diffs/npm/shadcn/4.21.4/4.22.1) |

|

| dependencies | minor | |
| [trpc-to-openapi](https://redirect.github.com/mcampa/trpc-to-openapi)
| [`3.3.0` →
`3.3.3`](https://renovatebot.com/diffs/npm/trpc-to-openapi/3.3.0/3.3.3)
|

|

| dependencies | patch | |
|
[typescript-eslint](https://typescript-eslint.io/packages/typescript-eslint)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint))
| [`8.71.0` →
`8.71.2`](https://renovatebot.com/diffs/npm/typescript-eslint/8.71.0/8.71.2)
|

|

| devDependencies | patch | `8.72.0` |
| [vitest](https://vitest.dev)
([source](https://redirect.github.com/vitest-dev/vitest/tree/HEAD/packages/vitest))
| [`4.1.11` →
`4.1.12`](https://renovatebot.com/diffs/npm/vitest/4.1.11/4.1.12) |

|

| devDependencies | patch | |
| [zod](https://zod.dev)
([source](https://redirect.github.com/colinhacks/zod)) | [`4.6.5` →
`4.6.7`](https://renovatebot.com/diffs/npm/zod/4.6.5/4.6.7) |

|

| dependencies | patch | |
---
### Release Notes
<details>
<summary>babel/babel (@​babel/runtime)</summary>
###
[`v7.29.10`](https://redirect.github.com/babel/babel/releases/tag/v7.29.10)
[Compare
Source](https://redirect.github.com/babel/babel/compare/v7.29.7...v7.29.10)
#### v7.29.10 (2026-10-07)
##### :bug: Bug Fix
- `babel-helpers`, `babel-plugin-transform-class-properties`,
`babel-plugin-transform-modules-systemjs`,
`babel-plugin-transform-runtime`, `babel-preset-env`,
`babel-runtime-corejs3`
- [#​18287](https://redirect.github.com/babel/babel/pull/18287)
Guard `@@toPrimitive` usage in toPrimitive helper
([@​babel-bot](https://redirect.github.com/babel-bot))
- `babel-traverse`
- [#​18281](https://redirect.github.com/babel/babel/pull/18281)
fix(traverse): do not evaluate destructured bindings in `path.evaluate`
([@​babel-bot](https://redirect.github.com/babel-bot))
##### :house: Internal
- `babel-helpers`, `babel-plugin-transform-modules-commonjs`,
`babel-runtime-corejs3`
- [#​18279](https://redirect.github.com/babel/babel/pull/18279)
Simplify `interopRequireWildcard` helper
([@​babel-bot](https://redirect.github.com/babel-bot))
##### Committers: 2
- Babel Bot ([@​babel-bot](https://redirect.github.com/babel-bot))
- Nicolò Ribaudo
([@​nicolo-ribaudo](https://redirect.github.com/nicolo-ribaudo))
</details>
<details>
<summary>eslint/css (@​eslint/css)</summary>
###
[`v2.0.1`](https://redirect.github.com/eslint/css/blob/HEAD/CHANGELOG.md#201-2026-10-11)
[Compare
Source](https://redirect.github.com/eslint/css/compare/cb523536527359e5eb0a7aeaf2d3abc5d44032c2...c29255e6f9551360319c09470b406b03d2da31ca)
##### Bug Fixes
- allow additional properties in `CSSLanguageOptions`
([#​538](https://redirect.github.com/eslint/css/issues/538))
([471e568](https://redirect.github.com/eslint/css/commit/471e56813150cbfb5a1f27aa7602f0131dc56578))
- crash in `no-important` on non-standard important values
([#​541](https://redirect.github.com/eslint/css/issues/541))
([2985ad5](https://redirect.github.com/eslint/css/commit/2985ad5a79b33d355bafd8156e81bbe16921d7dd))
- handle `var()` and `env()` in `no-invalid-at-rules`
([#​570](https://redirect.github.com/eslint/css/issues/570))
([75050c3](https://redirect.github.com/eslint/css/commit/75050c315c3f3bed6bd399f7508d262cf2f59f43))
- preserve keyframe selector casing in error messages
([#​571](https://redirect.github.com/eslint/css/issues/571))
([6589273](https://redirect.github.com/eslint/css/commit/6589273acf49edd67f46bbb28e2c9d63da3a1395))
- preserve property casing in `use-baseline` messages
([#​573](https://redirect.github.com/eslint/css/issues/573))
([0f65765](https://redirect.github.com/eslint/css/commit/0f65765423b927fdce89d4499730c6d6fa9e20f4))
- prevent crashes on `[@supports](https://redirect.github.com/supports)`
queries in `use-baseline`
([#​576](https://redirect.github.com/eslint/css/issues/576))
([6ecf264](https://redirect.github.com/eslint/css/commit/6ecf264a402d47d17ff33cf1e5676729ab04f513))
- recognize `at-rule()` feature queries in `use-baseline`
([#​533](https://redirect.github.com/eslint/css/issues/533))
([ba65596](https://redirect.github.com/eslint/css/commit/ba655964e8d4bb424638ab1c56dcf50a24231130))
- recognize CSS whitespace in named grid areas
([#​556](https://redirect.github.com/eslint/css/issues/556))
([ea5407d](https://redirect.github.com/eslint/css/commit/ea5407ddd542955b820271db75b4fef73b9bf79e))
- report each disallowed attribute location in `selector-complexity`
([#​580](https://redirect.github.com/eslint/css/issues/580))
([2bf2350](https://redirect.github.com/eslint/css/commit/2bf2350be041912803ad4680f03f582bb6fb6587))
- support at-rules nested in `[@page](https://redirect.github.com/page)`
and
`[@font-feature-values](https://redirect.github.com/font-feature-values)`
([#​543](https://redirect.github.com/eslint/css/issues/543))
([12ec029](https://redirect.github.com/eslint/css/commit/12ec029258de03056d8533a6960de154d8fb7e76))
- update baseline data
([bcd705e](https://redirect.github.com/eslint/css/commit/bcd705e7536d47dd8aff8fb0b5dc9d6f3d72e276))
- update baseline data
([#​561](https://redirect.github.com/eslint/css/issues/561))
([94d79a4](https://redirect.github.com/eslint/css/commit/94d79a46c8249fc6d12e6c02132952e106a53b68))
- update baseline data
([#​566](https://redirect.github.com/eslint/css/issues/566))
([9856c3b](https://redirect.github.com/eslint/css/commit/9856c3b6f25f4ebaf2930b130159264b9d381628))
- update baseline data
([#​579](https://redirect.github.com/eslint/css/issues/579))
([c60193b](https://redirect.github.com/eslint/css/commit/c60193bbc365efe94fa4c908f73f630bb3ce90c4))
- update baseline data
([#​585](https://redirect.github.com/eslint/css/issues/585))
([7a2dc06](https://redirect.github.com/eslint/css/commit/7a2dc06e0b08b89af01767efd8ae3290c4060f94))
- update baseline data
([#​589](https://redirect.github.com/eslint/css/issues/589))
([38ac3ad](https://redirect.github.com/eslint/css/commit/38ac3ade993e0bd168a230c0ac8c583fc6978e0f))
- update dependency
[@​eslint/css-tree](https://redirect.github.com/eslint/css-tree)
to ^4.1.1
([#​568](https://redirect.github.com/eslint/css/issues/568))
([ae69b1a](https://redirect.github.com/eslint/css/commit/ae69b1ad922f0c96e11bd4421033a92209e6afed))
- update eslint
([#​554](https://redirect.github.com/eslint/css/issues/554))
([33e7314](https://redirect.github.com/eslint/css/commit/33e7314cf4127543f1863ebeab13a8b7ecc8f161))
</details>
<details>
<summary>lingui/js-lingui
(@​lingui/babel-plugin-lingui-macro)</summary>
###
[`v6.10.0`](https://redirect.github.com/lingui/js-lingui/blob/HEAD/packages/babel-plugin-lingui-macro/CHANGELOG.md#6100-2026-10-16)
[Compare
Source](https://redirect.github.com/lingui/js-lingui/compare/v6.9.1...v6.10.0)
**Note:** Version bump only for package
[@​lingui/babel-plugin-lingui-macro](https://redirect.github.com/lingui/babel-plugin-lingui-macro)
####
[6.9.1](https://redirect.github.com/lingui/js-lingui/compare/v6.9.0...v6.9.1)
(2026-10-09)
**Note:** Version bump only for package
[@​lingui/babel-plugin-lingui-macro](https://redirect.github.com/lingui/babel-plugin-lingui-macro)
</details>
<details>
<summary>lingui/js-lingui (@​lingui/cli)</summary>
###
[`v6.10.0`](https://redirect.github.com/lingui/js-lingui/blob/HEAD/packages/cli/CHANGELOG.md#6100-2026-10-16)
[Compare
Source](https://redirect.github.com/lingui/js-lingui/compare/v6.9.1...v6.10.0)
##### Bug Fixes
- **cli:** don't compile twice per change in watch mode without
--debounce
([#​2722](https://redirect.github.com/lingui/js-lingui/issues/2722))
([92c8622](https://redirect.github.com/lingui/js-lingui/commit/92c8622e68e6b94bd3ea4f241b7314d610c46e83))
####
[6.9.1](https://redirect.github.com/lingui/js-lingui/compare/v6.9.0...v6.9.1)
(2026-10-09)
##### Performance Improvements
- **cli:** write experimental extractor catalogs in worker pool
([#​2715](https://redirect.github.com/lingui/js-lingui/issues/2715))
([f7731f2](https://redirect.github.com/lingui/js-lingui/commit/f7731f2a3e07145c45e2a62482ab3496302b5536))
</details>
<details>
<summary>lingui/js-lingui (@​lingui/core)</summary>
###
[`v6.10.0`](https://redirect.github.com/lingui/js-lingui/blob/HEAD/packages/core/CHANGELOG.md#6100-2026-10-16)
[Compare
Source](https://redirect.github.com/lingui/js-lingui/compare/v6.9.1...v6.10.0)
##### Features
- **core:** add default global variables support to i18n instance
([#​2661](https://redirect.github.com/lingui/js-lingui/issues/2661))
([7fa9acd](https://redirect.github.com/lingui/js-lingui/commit/7fa9acd777290d5f78e17935df21d976585433a2))
- runtime core macro
([#​2618](https://redirect.github.com/lingui/js-lingui/issues/2618))
([bd79e85](https://redirect.github.com/lingui/js-lingui/commit/bd79e85d57d95669b4e3c05f624a9974897c42b9))
####
[6.9.1](https://redirect.github.com/lingui/js-lingui/compare/v6.9.0...v6.9.1)
(2026-10-09)
**Note:** Version bump only for package
[@​lingui/core](https://redirect.github.com/lingui/core)
</details>
<details>
<summary>lingui/js-lingui (@​lingui/loader)</summary>
###
[`v6.10.0`](https://redirect.github.com/lingui/js-lingui/blob/HEAD/packages/loader/CHANGELOG.md#6100-2026-10-16)
[Compare
Source](https://redirect.github.com/lingui/js-lingui/compare/v6.9.1...v6.10.0)
**Note:** Version bump only for package
[@​lingui/loader](https://redirect.github.com/lingui/loader)
####
[6.9.1](https://redirect.github.com/lingui/js-lingui/compare/v6.9.0...v6.9.1)
(2026-10-09)
**Note:** Version bump only for package
[@​lingui/loader](https://redirect.github.com/lingui/loader)
</details>
<details>
<summary>lingui/js-lingui (@​lingui/react)</summary>
###
[`v6.10.0`](https://redirect.github.com/lingui/js-lingui/blob/HEAD/packages/react/CHANGELOG.md#6100-2026-10-16)
[Compare
Source](https://redirect.github.com/lingui/js-lingui/compare/v6.9.1...v6.10.0)
##### Features
- **core:** add default global variables support to i18n instance
([#​2661](https://redirect.github.com/lingui/js-lingui/issues/2661))
([7fa9acd](https://redirect.github.com/lingui/js-lingui/commit/7fa9acd777290d5f78e17935df21d976585433a2))
####
[6.9.1](https://redirect.github.com/lingui/js-lingui/compare/v6.9.0...v6.9.1)
(2026-10-09)
**Note:** Version bump only for package
[@​lingui/react](https://redirect.github.com/lingui/react)
</details>
<details>
<summary>lingui/js-lingui (@​lingui/vite-plugin)</summary>
###
[`v6.10.0`](https://redirect.github.com/lingui/js-lingui/blob/HEAD/packages/vite-plugin/CHANGELOG.md#6100-2026-10-16)
[Compare
Source](https://redirect.github.com/lingui/js-lingui/compare/v6.9.1...v6.10.0)
**Note:** Version bump only for package
[@​lingui/vite-plugin](https://redirect.github.com/lingui/vite-plugin)
####
[6.9.1](https://redirect.github.com/lingui/js-lingui/compare/v6.9.0...v6.9.1)
(2026-10-09)
##### Bug Fixes
- **vite-plugin:** fail early when native transform is used with Solid
([#​2708](https://redirect.github.com/lingui/js-lingui/issues/2708))
([15549f3](https://redirect.github.com/lingui/js-lingui/commit/15549f3cec626ab59eac1de00cccbbb56730c84f))
- **vite-plugin:** strip query from module id before native macro
transform
([#​2703](https://redirect.github.com/lingui/js-lingui/issues/2703))
([10c1b79](https://redirect.github.com/lingui/js-lingui/commit/10c1b79b6b287983593946fb965b8b0b558b96c1))
</details>
<details>
<summary>microsoft/playwright (@​playwright/test)</summary>
###
[`v1.64.1`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.64.1)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.64.0...v1.64.1)
##### Bug Fixes
-
[#​43247](https://redirect.github.com/microsoft/playwright/issues/43247)
\[Bug]: \_electron.launch intermittently loses a packaged app's first
window since 1.64.0
-
[#​43242](https://redirect.github.com/microsoft/playwright/issues/43242)
\[Regression]: Chromium 155+ aborts a preflighted cross-origin redirect
whenever any route is registered (net::ERR_ABORTED)
###
[`v1.64.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.64.0)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.63.0...v1.64.0)
#### 🧰 WebMCP
New
[page.webmcp](https://playwright.dev/docs/api/class-page#page-webmcp)
and
[frame.webmcp](https://playwright.dev/docs/api/class-frame#frame-webmcp)
give access to the tools that a page registers through the experimental
[WebMCP](https://playwright.dev/docs/api/class-webmcp) browser API, so
you can test them like any other part of your app:
```js
const browser = await chromium.launch({ args: ['--enable-features=WebMCP'] });
const page = await browser.newPage();
await page.goto('https://example.com');
for (const tool of await page.webmcp.tools())
console.log(tool.name, tool.description);
const result = await page.webmcp.callTool('add', { a: 2, b: 40 });
console.log(result.content[0].text); // "42"
```
[Playwright MCP](https://playwright.dev/docs/getting-started-mcp) also
supports WebMCP by default, with page-defined tools offered to the agent
as `webmcp_<tool>`. Pass `--no-webmcp` to opt out.
[`playwright-cli`](https://playwright.dev/docs/getting-started-cli)
exposes them as well:
```bash
playwright-cli webmcp-list
playwright-cli webmcp-call search_catalog --params '{"query":"cats"}'
```
#### 🎬 Better videos
Videos can now be recorded at a custom frame rate, and the decorations
for actions are styled with plain CSS:
```js
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
video: {
mode: 'on',
size: { width: 1920, height: 1080 },
fps: 60,
show: {
actions: {
style: {
point: 'width: 20px; height: 20px; border-radius: 50%; background: red',
highlight: 'outline: 2px solid #​333; background: rgba(0, 128, 255, .15)',
title: 'font-size: 16px',
},
},
},
},
},
});
```
- New `fps` option in
[testOptions.video](https://playwright.dev/docs/api/class-testoptions#test-options-video),
[`recordVideo`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-video)
and
[screencast.start()](https://playwright.dev/docs/api/class-screencast#screencast-start).
Firefox and WebKit currently capture up to 25 frames per second.
- New
[`style`](https://playwright.dev/docs/api/class-screencast#screencast-show-actions-option-style)
option takes CSS declarations for the `point` marker, the target
`highlight` and the action `title`. It replaces the `fontSize` option,
which is now deprecated.
- The cursor stays visible at the last action point, survives
navigations and travels along a natural, eased path.
- Videos are encoded with VP9 instead of VP8, which takes less CPU and
produces smaller files of the same or better
quality.
- All video options are also available in [Playwright
MCP](https://playwright.dev/docs/getting-started-mcp) and
[`playwright-cli`](https://playwright.dev/docs/getting-started-cli).
#### 🎯 Test runner
- New
[testProject.default](https://playwright.dev/docs/api/class-testproject#test-project-default)
option keeps a project in the config without running it by default. You
can configure every browser you care about, and let a plain `npx
playwright test` run just your favourite one:
```js
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'chromium', use: devices['Desktop Chrome'] },
{ name: 'firefox', use: devices['Desktop Firefox'], default: false },
{ name: 'webkit', use: devices['Desktop Safari'], default: false },
],
});
```
```bash
npx playwright test # runs chromium only
npx playwright test --project=firefox # runs firefox
npx playwright test --project="*" # runs all three, for example on CI
```
- New `--shuffle` command line option schedules tests in a random order,
which helps to find tests that accidentally depend on each other.
```bash
npx playwright test --shuffle
# Running 42 tests using 4 workers, shuffle seed 271828182
# ...
# Pass the seed to reproduce the same order.
npx playwright test --shuffle 271828182
```
- New `lock` option in
[test.describe.configure()](https://playwright.dev/docs/api/class-test#test-describe-configure)
adds [test locks](https://playwright.dev/docs/test-parallel#test-locks)
to all tests in a file or a group:
```js
test.describe.configure({ lock: 'user-settings' });
```
- New `type` option of `toHaveScreenshot` in
[testConfig.expect](https://playwright.dev/docs/api/class-testconfig#test-config-expect)
stores all unnamed screenshots as WebP:
```js
// playwright.config.ts
export default defineConfig({
expect: {
toHaveScreenshot: { type: 'webp' },
},
});
```
#### 🪆 Locator.within()
New
[locator.within()](https://playwright.dev/docs/api/class-locator#locator-within)
combines two locators that you already have, reading in the natural
order —
"this button, within that dialog":
```js
const saveButton = page.getByRole('button', { name: 'Save' });
const dialog = page.getByTestId('settings-dialog');
await saveButton.within(dialog).click();
```
Relative locators such as
[locator.nth()](https://playwright.dev/docs/api/class-locator#locator-nth)
or
[locator.first()](https://playwright.dev/docs/api/class-locator#locator-first)
are resolved inside each parent
separately, which makes column-like queries straightforward:
```js
// The third cell of every row, not the third cell in the table.
const thirdColumn = page.getByRole('cell').nth(2).within(page.getByRole('row'));
await expect(thirdColumn).toHaveText(['Apple', 'Banana', 'Cherry']);
```
#### New APIs
- New
[page.getByRef()](https://playwright.dev/docs/api/class-page#page-get-by-ref)
locates an element by its aria ref, such as `e2`, reported by
[page.ariaSnapshot()](https://playwright.dev/docs/api/class-page#page-aria-snapshot)
in the `'ai'` mode.
- New option
[`includeShadow`](https://playwright.dev/docs/api/class-page#page-content-option-include-shadow)
in
[page.content()](https://playwright.dev/docs/api/class-page#page-content)
and
[frame.content()](https://playwright.dev/docs/api/class-frame#frame-content)
serializes open shadow roots as [declarative shadow
DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#declaratively_with_html).
- New option
[`signCount`](https://playwright.dev/docs/api/class-credentials#credentials-create-option-sign-count)
sets the initial signature counter of a virtual credential.
[credentials.create()](https://playwright.dev/docs/api/class-credentials#credentials-create)
and
[credentials.get()](https://playwright.dev/docs/api/class-credentials#credentials-get)
return the current `signCount`, and it is saved and restored together
with the credentials in the storage state.
- New
[fullConfig.filteredProjects](https://playwright.dev/docs/api/class-fullconfig#full-config-filtered-projects)
lists the projects that were selected to run after applying the
`--project` filter. It is available in global setup and reporters.
- New
[apiRequestContext.addCookies()](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-add-cookies),
[apiRequestContext.cookies()](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-cookies)
and
[apiRequestContext.clearCookies()](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-clear-cookies)
manage cookies of a request context, mirroring the
[BrowserContext](https://playwright.dev/docs/api/class-browsercontext)
methods:
```js
await request.addCookies([{ name: 'session-id', value: '42', url:
'https://example.com' }]);
console.log(await request.cookies('https://example.com'));
await request.clearCookies({ name: 'session-id' });
```
#### Breaking changes ⚠️
- `screen` property is now forwarded from the [device
descriptors](https://playwright.dev/docs/emulation#devices). If you use
`...devices['Desktop Chrome']` and alike, `window.screen` and media
queries now see the emulated screen size. You can opt-out by explicitly
setting `screen` to `undefined`:
```js
// playwright.config.ts
export default defineConfig({
use: {
...devices['Desktop Chrome'],
screen: undefined,
},
});
```
- JSX in your test files now follows your `tsconfig.json`. Playwright
compiles JSX in test files according to the `jsx`, `jsxFactory`,
`jsxFragmentFactory` and `jsxImportSource` [tsconfig
options](https://playwright.dev/docs/test-typescript#tsconfig-jsx-options),
and defaults to the automatic runtime from `react/jsx-runtime`.
- `--update-snapshots=missing` now passes the test run. Tests that only
create missing snapshots now pass in the `'missing'` mode of
[testConfig.updateSnapshots](https://playwright.dev/docs/api/class-testconfig#test-config-update-snapshots),
so that CI can generate new snapshots and verify the existing ones in a
single run. The mode used when the option is not specified is now called
`'default'` and behaves as before: missing snapshots are written and the
test fails.
- Elements inside hidden iframes are considered hidden. Elements inside
an iframe that is not visible, for example with `visibility: hidden`,
are now considered hidden by actions,
[locator.isVisible()](https://playwright.dev/docs/api/class-locator#locator-is-visible)
and
[expect(locator).toBeVisible()](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-visible).
#### Browser Versions
- Chromium 156.0.8078.4
- Mozilla Firefox 157.0
- WebKit 27.2
This version was also tested against the following stable channels:
- Google Chrome 155
- Microsoft Edge 155
###
[`v1.63.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.63.0)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.62.1...v1.63.0)
#### 🔒 Test locks
Tests that access a shared resource — an external service, a global
account setting — can now declare a named `lock`.
Tests that share a lock name never run concurrently, across files,
workers and [projects](https://playwright.dev/docs/test-projects), while
everything else keeps running in parallel:
```js
test('update user settings', { lock: 'user-settings' }, async ({ page }) => {
// never runs at the same time as other tests holding 'user-settings'
});
```
A test can hold multiple locks, and
[test.describe()](https://playwright.dev/docs/api/class-test#test-describe)
accepts a `lock` for the whole group.
Learn more about [test
locks](https://playwright.dev/docs/test-parallel#test-locks).
#### 🪟 Locate across frames
[page.frameLocator()](https://playwright.dev/docs/api/class-page#page-frame-locator)
and
[frame.frameLocator()](https://playwright.dev/docs/api/class-frame#frame-frame-locator)
called without a selector search in any frame of the
subtree, so you no longer need to locate the iframe first:
```js
// Finds the button in any frame on the page.
await page.frameLocator().getByRole('button').click();
```
The rest of the locator resolves inside a single frame, just like a
regular locator, and an error is thrown when it
matches elements in several frames.
#### 👁️ Visible-only locators
New
[locator.visible()](https://playwright.dev/docs/api/class-locator#locator-visible)
returns a locator that matches only visible elements. It is the
recommended
replacement for the `:visible` CSS pseudo-class:
```js
await page.locator('button').visible().click();
```
#### 🧾 Step params and subtitles
Steps now carry structured data for reporters. Playwright API steps
report the target locator and call arguments,
and [test.step()](https://playwright.dev/docs/api/class-test#test-step)
accepts `subtitle` and `params` options for your own steps:
```js
await test.step('Login', async () => {
// ...
}, { subtitle: 'as admin', params: { user: 'admin' } });
```
Reporters receive them via
[testStep.subtitle](https://playwright.dev/docs/api/class-teststep#test-step-subtitle)
and
[testStep.params](https://playwright.dev/docs/api/class-teststep#test-step-params).
For Playwright API
steps, the subtitle is the locator or the navigation url — for example,
`Click` with subtitle `getByRole('button')`.
Both are rendered next to the step title in the trace viewer and the
HTML report.
#### 🖼️ Aria and screen snapshots in traces
The `snapshots` option of
[tracing.start()](https://playwright.dev/docs/api/class-tracing#tracing-start)
and the
[testOptions.trace](https://playwright.dev/docs/api/class-testoptions#test-options-trace)
fixture option now accept an
object selecting what to capture on every action:
```js
// playwright.config.ts
export default defineConfig({
use: {
trace: {
mode: 'on',
snapshots: { dom: true, aria: true, screen: true }
},
},
});
```
With aria and screen snapshots recorded, the new **Display Aria** mode
in the trace viewer shows the action screenshot
side by side with the aria snapshot, and hovering an aria node
highlights it on the screenshot.
#### New APIs
##### Browser and Context
-
[`httpCredentials`](https://playwright.dev/docs/api/class-browser#browser-new-context-option-http-credentials)
now also accepts an array of credentials. The first entry matching the
request origin is used, and entries without an origin match any request.
- New option
[`opfs`](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state-option-opfs)
includes the [origin private file
system](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system)
in the storage state, so it can be persisted and restored into later
contexts.
- New events
[page.on('dialogclosed')](https://playwright.dev/docs/api/class-page#page-event-dialog-closed)
and
[browserContext.on('dialogclosed')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-dialog-closed)
are emitted when a JavaScript dialog is accepted, dismissed or closed by
the user.
##### Locators
- New
[locator.ariaSnapshotJSON()](https://playwright.dev/docs/api/class-locator#locator-aria-snapshot-json)
and
[page.ariaSnapshotJSON()](https://playwright.dev/docs/api/class-page#page-aria-snapshot-json)
return the aria snapshot as a JSON value instead of YAML markup, with
`mode`, `depth` and `boxes` options.
-
[apiRequestContext.get()](https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-get)
and other request methods accept a type argument that types the response
`json()`:
```js
const response = await request.get<User>('/api/users/42');
const user = await response.json(); // typed as User
```
##### Test runner
- New standalone
[testOptions.reducedMotion](https://playwright.dev/docs/api/class-testoptions#test-options-reduced-motion),
[testOptions.forcedColors](https://playwright.dev/docs/api/class-testoptions#test-options-forced-colors)
and
[testOptions.contrast](https://playwright.dev/docs/api/class-testoptions#test-options-contrast)
options.
- New `--add-reporter` command line option appends a reporter on top of
the ones configured in `playwright.config`, instead of replacing them
like `--reporter` does.
- New `omitTags` option for the `list`, `line`, `dot`, `github` and
`junit` reporters suppresses the tags that are automatically appended to
test titles.
##### Command line
- `npx playwright install --no-remove` keeps the browsers of other
Playwright installations instead of removing them.
- `npx playwright codegen --http-credentials` records against pages
behind HTTP authentication.
##### Miscellaneous
- New built-in
[`perfetto`](https://playwright.dev/docs/test-reporters#perfetto-reporter)
reporter writes a Trace Event Format file for the [Perfetto
UI](https://ui.perfetto.dev) or `chrome://tracing`, rendering the test
run as a timeline with a lane per worker.
- The HTML report renders a duration waterfall next to test steps.
#### Announcements
- ⚠️ The experimental `@playwright/experimental-ct-react`,
`@playwright/experimental-ct-react17` and
`@playwright/experimental-ct-vue` packages will no longer be updated.
Follow the [migration
guide](https://playwright.dev/docs/test-components#migration-from-the-experimental-packages)
to move to the stories model introduced in 1.62. Story ids passed to
[fixtures.mount()](https://playwright.dev/docs/api/class-fixtures#fixtures-mount)
can now be typed through the generated `Stories` registry.
- ⚠️ Ubuntu 20.04 is not supported anymore.
- 🐧 On Linux arm64, Playwright now downloads the [Chrome for
Testing](https://developer.chrome.com/blog/chrome-for-testing) build of
Chromium, the same build used on all other platforms.
#### Browser Versions
- Chromium 153.0.8010.12
- Mozilla Firefox 155.0
- WebKit 26.6
This version was also tested against the following stable channels:
- Google Chrome 153
- Microsoft Edge 153
###
[`v1.62.1`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.62.1)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.62.0...v1.62.1)
##### Bug Fixes
-
[#​41989](https://redirect.github.com/microsoft/playwright/issues/41989)
\[Regression]: tsconfig "extends" bare specifier isn't resolved via
node_modules walk-up like tsc (fatal since 1.62)
-
[#​41998](https://redirect.github.com/microsoft/playwright/issues/41998)
\[Regression]: directory-form tsconfig project references ("path":
"../pkg") fail to resolve (fatal since 1.62)
-
[#​41985](https://redirect.github.com/microsoft/playwright/issues/41985)
Accessibility snapshot drops button name when text is nested inside
spans with aria-hidden SVG
-
[#​42000](https://redirect.github.com/microsoft/playwright/issues/42000)
\[Regression]: page.evaluate() arg of a branded primitive type (string &
{ brand }) no longer type-checks since 1.62
-
[#​42013](https://redirect.github.com/microsoft/playwright/issues/42013)
\[BUG]Image-type actionable elements are not presented in the snapshot.
###
[`v1.62.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.62.0)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.61.1...v1.62.0)
#### 🧱 New component testing model
[Component testing](https://playwright.dev/docs/test-components) moves
to a **stories and galleries** model.
A **story** wraps your component in one specific scenario — hard-coded
props, mock data, providers — and a **gallery** page that you serve
renders stories on demand.
The new
[fixtures.mount()](https://playwright.dev/docs/api/class-fixtures#fixtures-mount)
fixture navigates to the gallery, mounts a story by id, and returns a
[Locator](https://playwright.dev/docs/api/class-locator) scoped to the
story's root element:
```js
test('click should expand', async ({ mount }) => {
const component = await mount('components/Expandable/Stateful');
await component.getByRole('button').click();
await expect(component.getByTestId('expanded')).toHaveValue('true');
});
```
Pass a story type as a template argument to type-check its props, and
use `update(props)` / `unmount()` on the returned locator to re-render
or tear down within a test.
#### 🛑 Cancel operations with AbortSignal
Most operations and web-first assertions now accept a `signal` option
that takes an
[`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal),
letting you cancel long-running actions, navigations, waits, and
assertions:
```js
const controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
await page.getByRole('button', { name: 'Submit' }).click({ signal: controller.signal });
await expect(page.getByText('Done')).toBeVisible({ signal: controller.signal });
```
Providing a signal does not disable the default timeout; pass `timeout:
0` to disable it.
#### 🖼️ WebP screenshots
[expect(page).toHaveScreenshot()](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1)
and
[expect(locator).toHaveScreenshot()](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-screenshot-1)
can now store snapshots in the WebP format — just give the snapshot a
`.webp` name:
```js
// Visual comparisons store the golden snapshot as lossless WebP.
await expect(page).toHaveScreenshot('homepage.webp');
// Standalone screenshots can trade quality for size with lossy WebP.
await page.screenshot({ path: 'homepage.webp', quality: 50 });
```
[page.screenshot()](https://playwright.dev/docs/api/class-page#page-screenshot)
and \[locator.screenshot()
(<https://playwright.dev/docs/api/class-locator#locator-screenshot>)
also accept `webp` as a `type`, where quality `100` (the default) is
lossless and lower values use lossy compression.
#### 🧩 Custom test filtering with Reporter.preprocess()
New
[reporter.preprocess()](https://playwright.dev/docs/api/class-reporter#reporter-preprocess)
hook runs after the configuration is resolved and before
[reporter.onBegin()](https://playwright.dev/docs/api/class-reporter#reporter-on-begin),
letting a reporter mark individual tests as skipped, excluded, fixed, or
failing through a
[TestRun](https://playwright.dev/docs/api/class-testrun) object:
```js
class MyReporter {
async preprocess({ config, suite, testRun }) {
for (const test of suite.allTests()) {
if (shouldSkip(test))
testRun.skip(test);
}
}
}
```
#### 🔁 Isolated retries
New
[testConfig.retryStrategy](https://playwright.dev/docs/api/class-testconfig#test-config-retry-strategy)
controls when failed tests are retried.
The default `'immediate'` retries as soon as a worker is free;
`'isolated'` runs all retries at the end, one by one in a single worker,
to minimize interference with the rest of the suite:
```js
// playwright.config.ts
export default defineConfig({
retries: 2,
retryStrategy: 'isolated',
});
```
#### New APIs
##### Browser and Context
- New option
[`credentials`](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state-option-credentials)
includes the context's virtual WebAuthn
[Credentials](https://playwright.dev/docs/api/class-credentials)
(passkeys) in the storage state, so they can be persisted and re-seeded
into later contexts.
##### Actions
- New `scroll` option (`"auto"` | `"none"`) on actions to opt out of
Playwright's automatic scroll-into-view.
##### Network
- New
[apiResponse.timing()](https://playwright.dev/docs/api/class-apiresponse#api-response-timing)
returns resource timing information for an API response.
##### Evaluation
- New
[locator.waitForFunction()](https://playwright.dev/docs/api/class-locator#locator-wait-for-function)
waits until a function — called with the matching element — returns a
truthy value.
-
[page.evaluate()](https://playwright.dev/docs/api/class-page#page-evaluate)
and related methods now accept functions as evaluate arguments.
-
[page.addInitScript()](https://playwright.dev/docs/api/class-page#page-add-init-script)
/
[browserContext.addInitScript()](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script)
now accept functions as init-script arguments.
##### Command line & MCP
- Playwright now bundles the [Playwright
MCP](https://playwright.dev/docs/getting-started-mcp) server and
[`playwright-cli`](https://playwright.dev/docs/getting-started-cli),
runnable via `npx playwright mcp` and `npx playwright cli`.
##### Reporters
- The HTML report's **Merge files** grouping — previously only a UI
toggle — can now be enabled from the config with the new `mergeFiles`
reporter option:
```js
// playwright.config.ts
export default defineConfig({
reporter: [['html', { mergeFiles: true }]],
});
```
#### Announcements
- ⚠️ Debian 11 is not supported anymore.
#### Browser Versions
- Chromium 151.0.7922.34
- Mozilla Firefox 153.0
- WebKit 26.5
This version was also tested against the following stable channels:
- Google Chrome 151
- Microsoft Edge 151
###
[`v1.61.1`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.61.1)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.61.0...v1.61.1)
##### Bug Fixes
-
[#​41365](https://redirect.github.com/microsoft/playwright/issues/41365)
\[Bug]: Expect.Extend matcher with same name as default matcher in same
expect instance overrides default matchers implementation to custom
matcher
-
[#​41351](https://redirect.github.com/microsoft/playwright/issues/41351)
\[Bug]: Playwright UI mode: apiRequestContext.\_wrapApiCall reports
unexpected number of bytes (same test passes in headed mode)
-
[#​41360](https://redirect.github.com/microsoft/playwright/issues/41360)
\[Bug]: Trace viewer: message times in websockets are downscaled by 1000
-
[#​41311](https://redirect.github.com/microsoft/playwright/issues/41311)
\[Bug]: \[Regression]: Sync loader throws "context.conditions?.includes
is not a function" on Node 22.15
-
[#​41371](https://redirect.github.com/microsoft/playwright/issues/41371)
\[Regression]: Sync ESM loader (registerHooks) fails to resolve
extensionless .ts subpath imports across pnpm workspace symlinks
###
[`v1.61.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.61.0)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.60.0...v1.61.0)
#### 🔑 WebAuthn passkeys
New [Credentials](https://playwright.dev/docs/api/class-credentials)
virtual authenticator, available via
[browserContext.credentials](https://playwright.dev/docs/api/class-browsercontext#browser-context-credentials),
lets tests register passkeys and answer `navigator.credentials.create()`
/ `navigator.credentials.get()` ceremonies in the page — no real
hardware key required, works in all browsers:
```js
const context = await browser.newContext();
// Seed a passkey your backend provisioned for a test user.
await context.credentials.create('example.com', {
id: credentialId,
userHandle,
privateKey,
publicKey,
});
await context.credentials.install();
const page = await context.newPage();
await page.goto('https://example.com/login');
// The page's navigator.credentials.get() is answered with the seeded passkey.
```
You can also let the app register a passkey once in a setup test, read
it back with
[credentials.get()](https://playwright.dev/docs/api/class-credentials#credentials-get),
and seed it into later tests — see
[Credentials](https://playwright.dev/docs/api/class-credentials) for
details.
#### 🗃️ Web Storage
New [WebStorage](https://playwright.dev/docs/api/class-webstorage) API,
available via
[page.localStorage](https://playwright.dev/docs/api/class-page#page-local-storage)
and
[page.sessionStorage](https://playwright.dev/docs/api/class-page#page-session-storage),
reads and writes the page's storage for the current origin:
```js
await page.localStorage.setItem('token', 'abc');
const token = await page.localStorage.getItem('token');
const items = await page.sessionStorage.items();
```
#### New APIs
##### Network
-
[apiResponse.securityDetails()](https://playwright.dev/docs/api/class-apiresponse#api-response-security-details)
and
[apiResponse.serverAddr()](https://playwright.dev/docs/api/class-apiresponse#api-response-server-addr)
mirror the browser-side
[response.securityDetails()](https://playwright.dev/docs/api/class-response#response-security-details)
and
[response.serverAddr()](https://playwright.dev/docs/api/class-response#response-server-addr).
##### Browser and Screencast
- New option `artifactsDir` in
[browserType.connectOverCDP()](https://playwright.dev/docs/api/class-browsertype#browser-type-connect-over-cdp)
controls where artifacts such as traces and downloads are stored when
attached to an existing browser.
- New option `cursor` in
[screencast.showActions()](https://playwright.dev/docs/api/class-screencast#screencast-show-actions)
controls the cursor decoration rendered for pointer actions.
- The `onFrame` callback in
[screencast.start()](https://playwright.dev/docs/api/class-screencast#screencast-start)
now receives a `timestamp` of when the frame was presented by the
browser.
##### Test runner
- The
[testOptions.video](https://playwright.dev/docs/api/class-testoptions#test-options-video)
option now supports the same set of modes as `trace`: new
`'on-all-retries'`, `'retain-on-first-failure'` and
`'retain-on-failure-and-retries'` values. See the [video modes
table](https://playwright.dev/docs/test-use-options#video-modes) for
which runs are recorded and kept in each mode.
- Supported `expect.soft.poll(...)`.
- New
[fullConfig.argv](https://playwright.dev/docs/api/class-fullconfig#full-config-argv)
— a snapshot of `process.argv` from the runner process, handy for
reading custom arguments passed after the `--` separator.
- New
[fullConfig.failOnFlakyTests](https://playwright.dev/docs/api/class-fullconfig#full-config-fail-on-flaky-tests)
mirrors the config option, so reporters can explain why a flaky run
failed.
-
[testInfo.errors](https://playwright.dev/docs/api/class-testinfo#test-info-errors)
now lists each sub-error of an `AggregateError` as a separate entry.
- New `-G` command line shorthand for `--grep-invert`.
#### 🛠️ Other improvements
- Playwright now supports Ubuntu 26.04.
- HAR and trace recordings now include WebSocket requests.
#### Browser Versions
- Chromium 149.0.7827.55
- Mozilla Firefox 151.0
- WebKit 26.5
This version was also tested against the following stable channels:
- Google Chrome 149
- Microsoft Edge 149
###
[`v1.60.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.60.0)
[Compare
Source](https://redirect.github.com/microsoft/playwright/compare/v1.59.1...v1.60.0)
#### 🌐 HAR recording on Tracing
[tracing.startHar()](https://playwright.dev/docs/api/class-tracing#tracing-start-har)
/
[tracing.stopHar()](https://playwright.dev/docs/api/class-tracing#tracing-stop-har)
expose HAR recording as a first-class tracing API, with the same
`content`, `mode` and `urlFilter` options as `recordHar`. The returned
[Disposable](https://playwright.dev/docs/api/class-disposable) makes it
easy to scope a recording with `await using`:
```js
await using har = await context.tracing.startHar('trace.har');
const page = await context.newPage();
await page.goto('https://playwright.dev');
// HAR is finalized when `har` goes out of scope.
```
#### 🪝 Drop API
New
[locator.drop()](https://playwright.dev/docs/api/class-locator#locator-drop)
simulates an external drag-and-drop of files or clipboard-like data onto
an element. Playwright dispatches `dragenter`, `dragover`, and `drop`
with a synthetic \[DataTransfer] in the page context — works
cross-browser and is great for testing upload zones:
```js
await page.locator('#dropzone').drop({
files: { name: 'note.txt', mimeType: 'text/plain', buffer: Buffer.from('hello') },
});
await page.locator('#dropzone').drop({
data: {
'text/plain': 'hello world',
'text/uri-list': 'https://example.com',
},
});
```
#### 🎯 Aria snapshots
-
[expect(page).toMatchAriaSnapshot()](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-match-aria-snapshot)
now works on a [Page](https://playwright.dev/docs/api/class-page), in
addition to a [Locator](https://playwright.dev/docs/api/class-locator) —
equivalent to asserting against `page.locator('body')`.
- New `boxes` option on
[locator.ariaSnapshot()](https://playwright.dev/docs/api/class-locator#locator-aria-snapshot)
/
[page.ariaSnapshot()](https://playwright.dev/docs/api/class-page#page-aria-snapshot)
appends each element's bounding box as `[box=x,y,width,height]`, useful
for AI consumption.
#### 🛑 test.abort()
New
[test.abort()](https://playwright.dev/docs/api/class-test#test-abort)
aborts the currently running test from a fixture, hook, or route handler
with an optional message. Use it when you have detected an unrecoverable
misuse and want to fail the test right away:
```js
test('does not publish to the shared page', async ({ page }) => {
await page.route('**/publish', route => {
test.abort('Tests must not publish to the shared page. Use the `clone` option.');
return route.abort();
});
// ...
});
```
#### New APIs
##### Browser, Context and Page
- Event
[browser.on('context')](https://playwright.dev/docs/api/class-browser#browser-event-context)
— fired when a new context is created on the browser.
- [BrowserContext](https://playwright.dev/docs/api/class-browsercontext)
now mirrors lifecycle events from its pages:
[browserContext.on('download')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-download),
[browserContext.on('frameattached')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-frame-attached),
[browserContext.on('framedetached')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-frame-detached),
[browserContext.on('framenavigated')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-frame-navigated),
[browserContext.on('pageclose')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-page-close),
[browserContext.on('pageload')](https://playwright.dev/docs/api/class-browsercontext#browser-context-event-page-load).
##### Locators and Assertions
- New option `description` in
[page.getByRole()](https://playwright.dev/docs/api/class-page#page-get-by-role)
/
[locator.getByRole()](https://playwright.dev/docs/api/class-locator#locator-get-by-role)
/
[frame.getByRole()](https://playwright.dev/docs/api/class-frame#frame-get-by-role)
/
[frameLocator.getByRole()](https://playwright.dev/docs/api/class-framelocator#frame-locator-get-by-role)
for matching the [accessible
description](https://www.w3.org/TR/wai-aria-1.2/#dfn-accessible-description).
- New option `pseudo` in
[expect(locator).toHaveCSS()](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-css)
reads computed styles from `::before` or `::after`.
- New option `style` in
[locator.highlight()](https://playwright.dev/docs/api/class-locator#locator-highlight)
applies extra inline CSS to the highlight overlay, plus new
[page.hideHighlight()](https://playwright.dev/docs/api/class-page#page-hide-highlight)
to clear all highlights.
##### Network
-
[webSocketRoute.protocols()](https://playwright.dev/docs/api/class-websocketroute#web-socket-route-protocols)
returns the WebSocket subprotocols requested by the page.
- New option `noDefaults` in
[browserType.connectOverCDP()](https://playwright.dev/docs/api/class-browsertype#browser-type-connect-over-cdp)
disables Playwright's default overrides on the default context (download
behavior, focus emulation, media emulation), so atta
> [!IMPORTANT]
> ✂ PR body was truncated to here.
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- "before 6am on monday"
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/sleepypod/core).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNjQuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjE3MS4yIiwidGFyZ2V0QnJhbmNoIjoiZGV2IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyIsImZpeC1wYXRjaCIsIm5vbi1tYWpvciJdfQ==-->
<!-- sleepypod:install-start -->
<!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block.
Hello fellow agent / curious human! Anything between the
sleepypod:install-start and sleepypod:install-end markers
gets overwritten on the next CI run. Want to change what's
here? Edit .github/workflows/build.yml instead. 💤 -->
<details open>
<summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary>
Pick the snippet that matches your pod. **Already-installed pods can't
use the curl bootstraps below** — sleepypod's egress firewall DROPs
github.com, and the script that knows how to unblock WAN is the very
file curl is trying to fetch. Use `sp-update` instead; it's on disk and
opens WAN as its first step.
🔄 **Already on a sleepypod** (most common — review a PR on a running
pod):
```bash
sudo sp-update renovate/all-minor-patch
```
🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every
push:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/renovate/all-minor-patch/scripts/install \
| sudo bash -s -- --branch renovate/all-minor-patch
```
🎯 **Pin to this exact build** ([run
40800873498](https://github.com/sleepypod/core/actions/runs/40800873498)
· `f3481d2`) — fresh-pod path, locked to one CI artifact for
reproducible review:
```bash
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/f3481d2eb30c48240e6943de5b7d0e4b033492da/scripts/install \
| sudo bash -s -- \
--branch renovate/all-minor-patch \
--artifact-url 'https://nightly.link/sleepypod/core/actions/runs/40800873498/sleepypod-core.zip'
```
🧊 Artifact:
[`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/40800873498/artifacts/12866395786)
· self-destructs in 30 days 💥
</details>
<!-- sleepypod:install-end -->
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Ng <jon@jonathanng.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #862 ### Description Removes the invalid update type `"fix"` from `packageRules[7].matchUpdateTypes` in `renovate.json`. In Renovate, allowed update types are `major`, `minor`, `patch`, `pin`, `pinDigest`, `digest`, `lockFileMaintenance`, `rollback`, `bump`, and `replacement`. Patch was already present in the array, so removing `"fix"` fixes the schema validation error while preserving the intended rule targeting. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Chores** * Updated automated dependency update rules so the patch-specific rule applies only to patch updates. Existing commit messages, actions, and labels remain unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…851) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@stryker-mutator/core](https://stryker-mutator.io/) ([source](https://redirect.github.com/stryker-mutator/stryker-js/tree/HEAD/packages/core)) | [`^9.6.1` → `^10.0.0`](https://renovatebot.com/diffs/npm/@stryker-mutator%2fcore/9.6.1/10.1.1) |  |  | | [@stryker-mutator/typescript-checker](https://stryker-mutator.io/docs/stryker-js/typescript-checker) ([source](https://redirect.github.com/stryker-mutator/stryker-js/tree/HEAD/packages/typescript-checker)) | [`^9.6.1` → `^10.0.0`](https://renovatebot.com/diffs/npm/@stryker-mutator%2ftypescript-checker/9.6.1/10.1.1) |  |  | | [@stryker-mutator/vitest-runner](https://stryker-mutator.io/docs/stryker-js/vitest-runner) ([source](https://redirect.github.com/stryker-mutator/stryker-js/tree/HEAD/packages/vitest-runner)) | [`^9.6.1` → `^10.0.0`](https://renovatebot.com/diffs/npm/@stryker-mutator%2fvitest-runner/9.6.1/10.1.1) |  |  | --- ### Release Notes <details> <summary>stryker-mutator/stryker-js (@​stryker-mutator/core)</summary> ### [`v10.1.1`](https://redirect.github.com/stryker-mutator/stryker-js/blob/HEAD/packages/core/CHANGELOG.md#1011-2026-10-23) [Compare Source](https://redirect.github.com/stryker-mutator/stryker-js/compare/v10.0.0...v10.1.1) **Note:** Version bump only for package [@​stryker-mutator/core](https://redirect.github.com/stryker-mutator/core) ### [`v10.0.0`](https://redirect.github.com/stryker-mutator/stryker-js/blob/HEAD/packages/core/CHANGELOG.md#1000-2026-08-14) [Compare Source](https://redirect.github.com/stryker-mutator/stryker-js/compare/v9.6.1...v10.0.0) ##### Bug Fixes - **deps:** update dependency ajv to v8.20.0 ([#​5992](https://redirect.github.com/stryker-mutator/stryker-js/issues/5992)) ([0ead8ce](https://redirect.github.com/stryker-mutator/stryker-js/commit/0ead8cef6cdb1b89ab617096c3af4c02fa995054)) - **deps:** update mutation-testing-elements monorepo to v3.8.0 ([#​6095](https://redirect.github.com/stryker-mutator/stryker-js/issues/6095)) ([d62fd75](https://redirect.github.com/stryker-mutator/stryker-js/commit/d62fd7518c7d5697801afc093578dd30d8dad835)) - **deps:** update mutation-testing-elements monorepo to v3.8.4 ([#​6118](https://redirect.github.com/stryker-mutator/stryker-js/issues/6118)) ([29ede69](https://redirect.github.com/stryker-mutator/stryker-js/commit/29ede696a930a99869c2d3366f94ba7cd323fe60)) ##### Features - **babel:** update babel to major version 8 ([#​6104](https://redirect.github.com/stryker-mutator/stryker-js/issues/6104)) ([291b7ea](https://redirect.github.com/stryker-mutator/stryker-js/commit/291b7ead950e8c0b46da0db77a7a6ece32547114)) - **core:** save partial incremental report on unexpected exit ([#​5986](https://redirect.github.com/stryker-mutator/stryker-js/issues/5986)) ([d091b1a](https://redirect.github.com/stryker-mutator/stryker-js/commit/d091b1a869e86c1581b09b2e32fb8a15aee68a82)) - empty-expression-mutator ([#​6012](https://redirect.github.com/stryker-mutator/stryker-js/issues/6012)) ([cc08738](https://redirect.github.com/stryker-mutator/stryker-js/commit/cc087380173e411ed4ba1960c2571db77eb0cce3)), closes [#​5765](https://redirect.github.com/stryker-mutator/stryker-js/issues/5765) - **node:** drop support for Node.js 20, require Node.js 22 or higher ([#​6002](https://redirect.github.com/stryker-mutator/stryker-js/issues/6002)) ([425cb9f](https://redirect.github.com/stryker-mutator/stryker-js/commit/425cb9f7306d4c86b30a90f955c82429a465487b)) ##### BREAKING CHANGES - **node:** Node.js 20 is no longer supported, please use Node.js 22 or higher. #### [9.6.1](https://redirect.github.com/stryker-mutator/stryker-js/compare/v9.6.0...v9.6.1) (2026-04-10) ##### Bug Fixes - **deps:** update dependency typed-rest-client to \~2.3.0 ([#​5933](https://redirect.github.com/stryker-mutator/stryker-js/issues/5933)) ([5a24298](https://redirect.github.com/stryker-mutator/stryker-js/commit/5a242981292e9cd4cb1ec4e5d0a5215db35c8f4a)) - **deps:** update mutation-testing-elements monorepo to v3.7.3 ([#​5912](https://redirect.github.com/stryker-mutator/stryker-js/issues/5912)) ([1f68437](https://redirect.github.com/stryker-mutator/stryker-js/commit/1f68437fceee0a1bfcbcc838d3251cdc42410a4c)) </details> <details> <summary>stryker-mutator/stryker-js (@​stryker-mutator/typescript-checker)</summary> ### [`v10.1.1`](https://redirect.github.com/stryker-mutator/stryker-js/blob/HEAD/packages/typescript-checker/CHANGELOG.md#1011-2026-10-23) [Compare Source](https://redirect.github.com/stryker-mutator/stryker-js/compare/v10.0.0...v10.1.1) **Note:** Version bump only for package [@​stryker-mutator/typescript-checker](https://redirect.github.com/stryker-mutator/typescript-checker) ### [`v10.0.0`](https://redirect.github.com/stryker-mutator/stryker-js/blob/HEAD/packages/typescript-checker/CHANGELOG.md#1000-2026-08-14) [Compare Source](https://redirect.github.com/stryker-mutator/stryker-js/compare/v9.6.1...v10.0.0) ##### Bug Fixes - **deps:** update dependency semver to v7.8.5 ([#​6075](https://redirect.github.com/stryker-mutator/stryker-js/issues/6075)) ([f66c2fc](https://redirect.github.com/stryker-mutator/stryker-js/commit/f66c2fcde4dc7b0546880d261afac7edbc0527f4)) ##### Features - **node:** drop support for Node.js 20, require Node.js 22 or higher ([#​6002](https://redirect.github.com/stryker-mutator/stryker-js/issues/6002)) ([425cb9f](https://redirect.github.com/stryker-mutator/stryker-js/commit/425cb9f7306d4c86b30a90f955c82429a465487b)) - **typescript-checker:** add experimental support for TypeScript\@​7 ([#​6099](https://redirect.github.com/stryker-mutator/stryker-js/issues/6099)) ([3d35679](https://redirect.github.com/stryker-mutator/stryker-js/commit/3d356791210930932b7d5c5c69546197025ceb5c)) ##### BREAKING CHANGES - **node:** Node.js 20 is no longer supported, please use Node.js 22 or higher. #### [9.6.1](https://redirect.github.com/stryker-mutator/stryker-js/compare/v9.6.0...v9.6.1) (2026-04-10) **Note:** Version bump only for package [@​stryker-mutator/typescript-checker](https://redirect.github.com/stryker-mutator/typescript-checker) </details> <details> <summary>stryker-mutator/stryker-js (@​stryker-mutator/vitest-runner)</summary> ### [`v10.1.1`](https://redirect.github.com/stryker-mutator/stryker-js/blob/HEAD/packages/vitest-runner/CHANGELOG.md#1011-2026-10-23) [Compare Source](https://redirect.github.com/stryker-mutator/stryker-js/compare/v10.0.0...v10.1.1) **Note:** Version bump only for package [@​stryker-mutator/vitest-runner](https://redirect.github.com/stryker-mutator/vitest-runner) ### [`v10.0.0`](https://redirect.github.com/stryker-mutator/stryker-js/blob/HEAD/packages/vitest-runner/CHANGELOG.md#1000-2026-08-14) [Compare Source](https://redirect.github.com/stryker-mutator/stryker-js/compare/v9.6.1...v10.0.0) ##### Bug Fixes - **vitest-runner:** fix noisy vitest options & setup file warnings ([#​6098](https://redirect.github.com/stryker-mutator/stryker-js/issues/6098)) ([58c4b85](https://redirect.github.com/stryker-mutator/stryker-js/commit/58c4b850fff61a7de232a42103f5745d01a46493)) ##### Features - empty-expression-mutator ([#​6012](https://redirect.github.com/stryker-mutator/stryker-js/issues/6012)) ([cc08738](https://redirect.github.com/stryker-mutator/stryker-js/commit/cc087380173e411ed4ba1960c2571db77eb0cce3)), closes [#​5765](https://redirect.github.com/stryker-mutator/stryker-js/issues/5765) - **node:** drop support for Node.js 20, require Node.js 22 or higher ([#​6002](https://redirect.github.com/stryker-mutator/stryker-js/issues/6002)) ([425cb9f](https://redirect.github.com/stryker-mutator/stryker-js/commit/425cb9f7306d4c86b30a90f955c82429a465487b)) ##### BREAKING CHANGES - **node:** Node.js 20 is no longer supported, please use Node.js 22 or higher. #### [9.6.1](https://redirect.github.com/stryker-mutator/stryker-js/compare/v9.6.0...v9.6.1) (2026-04-10) ##### Bug Fixes - **vitest-runner:** fix vitest runner mutant hitcount and coverage for v4.1 ([#​5928](https://redirect.github.com/stryker-mutator/stryker-js/issues/5928)) ([56c1ae2](https://redirect.github.com/stryker-mutator/stryker-js/commit/56c1ae23c823d618c8ca591d2959ab2b6d7df44d)) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "before 6am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/sleepypod/core). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNjQuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjE4OC4yIiwidGFyZ2V0QnJhbmNoIjoiZGV2IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyIsIm1ham9yIl19--> <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update renovate/major-stryker-js-monorepo ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/renovate/major-stryker-js-monorepo/scripts/install \ | sudo bash -s -- --branch renovate/major-stryker-js-monorepo ``` 🎯 **Pin to this exact build** ([run 40803712899](https://github.com/sleepypod/core/actions/runs/40803712899) · `98ed434`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/98ed434c8dfbfae3496d7da6a2d3461bbd781237/scripts/install \ | sudo bash -s -- \ --branch renovate/major-stryker-js-monorepo \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/40803712899/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/40803712899/artifacts/12866629543) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jonathan Ng <jon@jonathanng.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#877) Closes #873 ## Summary Follow-up to #872. The new STM32 firmware (cover sensor `v5.5.22.0`, thermal controller `v1.6.114`) emits `frzWater` and `sensHealth` frames. This PR shows both in the UI. Every addition is gated on its frame having arrived, so a pod on older firmware renders exactly what it does today. **1. Health → Live stream inspector** (`src/components/Sensors/streamGroups.ts`) - `frzWater` is counted in the **Freezer** row and `sensHealth` in the **Presence** row, so the inspector shows their packet rate and last-seen time. - A new test checks that every type in `ALL_SENSOR_TYPES` belongs to exactly one group. **2. Water tank gauge and priming state** (`src/components/status/waterTank.ts`, `WaterTankGauge.tsx`) - A five-bar tank that fills from the bottom. It uses `capLevel` only when `capValid` is true; otherwise every bar stays unlit and the label falls back to `hasWater` (OK / Low). The gauge turns amber at level ≤ 1 or whenever `hasWater` is false. - While `isPriming`, the bars pulse and the label reads "Priming…". When `needsPriming` is set, it reads "Needs prime" in amber. - **System → Dashboard:** the status line gains a **Water** fact, but only once a `frzWater` frame has arrived. - **System → Sensors → Freezer health:** the gauge replaces the binary `water ok/low` text in the secondary line, and also appears in the phone summary row. Without a frame, the card's markup is unchanged. - The binary DAC `DEVICE_STATUS` water level still drives the "Water level is low" attention item and the footer. This PR doesn't change it. **3. Cover sensor disconnect warning** (`dashboardLogic.ts`: `disconnectedCovers()` and a new `cover` attention item) - The warning reads "Left cover sensor disconnected", "Right cover sensor disconnected", or "Left and right cover sensors disconnected". It appears in the System → Dashboard attention card and, because the sidebar footer lists the same attention items, in the footer as well. - Only an explicit `connected: false` in a `sensHealth` frame triggers it. With no frame (older firmware) or a malformed one, nothing is shown. - The app shell (`usePodHealth`) subscribes to `sensHealth` through the new `useSensorSubscription()`, so the footer stays current on every route. The subscription merges with other consumers' sensors and doesn't re-render on frames. A reconnect re-requests a snapshot. As a side effect, the shell now keeps the sensor WebSocket open on every page. On System, **Stop** and pull-to-refresh only drop System's own all-sensor subscription and no longer close the socket. **4. Follow-ups from Pod 3 testing (@onemec)** - `FrzWaterFrame` now types its flags as `boolean | 0 | 1` and adds an optional `version` field, because the firmware sends integers (`"isPriming":1,"capValid":0`). Every reader checks truthiness. A test feeds the live Pod 3 payload through `waterTank()`. - `WaterLevelCard` shows the tank gauge in its header when a `frzWater` frame exists, and disables Start prime while `frzWater.isPriming` or the DAC reports priming. Without a frame, the card is unchanged. - `PresenceCard` shows an amber **Disconnected** dot on a side when `sensHealth` reports `connected: false` for it. Without the frame, nothing is added. **5. CodeRabbit follow-ups** - `WaterLevelCard` subscribes to `frzWater` itself. - In Freezer health, a low `frzWater` tank now turns the status dot to "Water low" even while the DAC reads ok. **Demo mode** now emits `frzWater` (level 4/5, priming follows the demo prime) and `sensHealth` (both sides connected), so the gauge appears in `NEXT_PUBLIC_DEMO=1` builds. ## Verification - `pnpm tsc` and `pnpm lint` are clean. The full pre-push hook passed on push: tsc, eslint, both `drizzle-kit check` runs, and `vitest related` (32 files, 392 tests). - New and updated tests cover three cases for each frame: present, absent (older firmware), and invalid or disconnected. - `streamGroups.test.ts` - `WaterTankGauge.test.tsx` - `dashboardLogic.test.ts` - `DiagnosticsConsole.test.tsx`: the dashboard attention card and the Water fact - `sensorCards.test.tsx`: Freezer health - `navigation.test.tsx`: the sidebar footer - `demo/tests/socket.test.ts` - Rendered locally in demo mode with the right cover temporarily patched to `connected: false`. The dashboard showed a `Water ≣ 4/5` fact and "Right cover sensor disconnected" in both the attention card and the sidebar footer. Freezer health showed `tank ≣ 4/5 · fan … rpm`, and the Health live stream reported the Freezer row's last frame as `frzWater`. The browser console logged no errors. ## Test plan **Pod 3 on new firmware** (cover `v5.5.22.0`, thermal `v1.6.114`) - [ ] `sudo sp-update feat/frzwater-senshealth-ui` (see the install block below) - [ ] System → Dashboard: the status line shows a **Water** gauge whose bar count matches `capLevel`. Compare against the raw `frzWater` frame in System → Sensors → raw frames. - [ ] Drain or top up the tank: the gauge level follows, and it turns amber at 1/5 or when `hasWater` goes false. - [ ] Start a prime: the gauge pulses and reads "Priming…" until `isPriming` clears. If the firmware sets `needsPriming`, "Needs prime" appears. - [ ] System → Sensors → Freezer health: the secondary line reads `tank ≣ n/5 · fan …` instead of `water ok`. - [ ] Unplug the left cover sensor: "Left cover sensor disconnected" appears in the Dashboard attention card and the sidebar footer, and clears once it's plugged back in. Repeat for the right side, then for both sides. - [ ] System → Health → Live stream: the Freezer and Presence rows count `frzWater` and `sensHealth` frames, and the per-minute rate and last-seen time update. **Pod on older firmware** (no `frzWater` / `sensHealth`) - [ ] The Dashboard status line has no Water fact, and Freezer health still reads `water ok` / `water low` from the DAC. - [ ] A low DAC water level still raises "Water level is low" in the attention card and footer. - [ ] No cover-disconnect warning appears anywhere. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update feat/frzwater-senshealth-ui ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/feat/frzwater-senshealth-ui/scripts/install \ | sudo bash -s -- --branch feat/frzwater-senshealth-ui ``` 🎯 **Pin to this exact build** ([run 40825838320](https://github.com/sleepypod/core/actions/runs/40825838320) · `1bba8a5`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/1bba8a5f09c1df93bdfd37b0def5e1b6b6401353/scripts/install \ | sudo bash -s -- \ --branch feat/frzwater-senshealth-ui \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/40825838320/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/40825838320/artifacts/12876811602) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Water-level displays include a tank gauge when readings are available, with level, warning, and priming indicators. * Diagnostics show tank status and flag disconnected cover sensors, identifying the affected side. * Presence displays identify cover sensors reported as disconnected. * Existing water-level details remain visible when tank readings are unavailable. * **Improvements** * Water-level status and priming controls reflect reported priming activity. * Diagnostics can show a low-water warning based on device status when no water reading is available. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
|
🎉 This PR is included in version 3.5.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Promotes
devtomainwith personal sleep-stage models, a heart-rate estimator that stops guessing on weak signal, beat-to-beat heartbeat detection, Watch HRV and breathing references, a schedule library, and the Next.js 16.4.3 security upgrade.Changes
candidate.jsonfrom Settings, and stage a side's nights with its model when switched on. Includes MCP tools (Personal stage models: train off the pod on the phone's feature contracts, and run them on the pod #855, chore: delete the pod snapshot after a personal-model training run #857–feat(sleep): manage personal models and compare Watch sleep data #860, feat(mcp): personal stage model tools #864, fix(training): preview candidates safely and preserve Watch label gaps #866).heart_rate.minimumPeakScore(default 0.4) now report no heart rate instead of a guess. In local replay this cut Watch HR error from 5.2 to 1.75 bpm on tuning nights and from 4.3 to 3.1 bpm on held-back nights, at about 75% coverage (feat(biometrics): let the HR estimator report nothing instead of a weak-signal guess #840).heartbeatstable. Vitals are unchanged. Heartbeat retries now use bounded, ordered backoff (feat(piezo-processor): beat-to-beat heartbeat detection #848, fix(piezo): bound heartbeat retries with ordered backoff #865).sensHealth,frzWaterandlpsas known record types (fix(biometrics): register sensHealth, frzWater, and lps as known record types #872, feat(ui): surface frzWater tank level and sensHealth cover disconnects #877).Migrations
Deploy note
#840 bumps the biometrics manifest to 1.1.0. Profile revisions pinned to the old manifest will report
PROFILE_INCOMPATIBLEand must be re-proposed against the new one.Test plan
devhead (1fbb953f): CI, Build & Package, Dev Release, Publish OpenAPI Spec, Ref Collision Check.mainmerge commit, thensudo sp-update mainonce on a pod and confirm the service starts and migrations apply.🤖 Generated with Claude Code
🛏️ Beam this PR onto a sleepypod ✨
Pick the snippet that matches your pod. Already-installed pods can't use the curl bootstraps below — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use
sp-updateinstead; it's on disk and opens WAN as its first step.🔄 Already on a sleepypod (most common — review a PR on a running pod):
🚀 Fresh pod / first install — bootstraps from GitHub, rebuilds every push:
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/dev/scripts/install \ | sudo bash -s -- --branch dev🎯 Pin to this exact build (run 41008176390 ·
1fbb953) — fresh-pod path, locked to one CI artifact for reproducible review:🧊 Artifact:
sleepypod-core· self-destructs in 30 days 💥