π docs(readme): move the language switcher above the logo - #591
Conversation
It read as buried under the logo, title, and tagline β and the tagline above it is English, so a non-English reader hit a sentence they may not read before finding their own language. Now it is the first thing rendered. Kept inside <div align="center"> rather than placed above it, so it stays centered instead of rendering left-aligned over a centered logo. Applied to all seven READMEs so the translated copies do not drift.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. π βΉοΈ Recent review infoβοΈ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: π Files selected for processing (7)
π WalkthroughWalkthroughThe localized README headers now show their language selectors near the top of the centered header block. Previous selector occurrences were removed, and related header spacing was adjusted in the Spanish and English READMEs. Possibly related PRs
π₯ Pre-merge checks | β 2β Passed checks (2 passed)
β¨ Finishing Touchesπ§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
β Action performedReview finished.
|
|
@coderabbitai review |
β Action performedReview finished.
|
* β
test(api): version-cache reset + adversarial-pass test hygiene (#557)
Closes the round-7 review finding on #550 plus two adjacent test-hygiene
issues found by an adversarial pass over the same surface, so the whole
batch lands in one review round.
## Changes
**Version cache reset (CodeRabbit finding on #550,
`app/api/portwing-ws.test.ts:324`)**
- `injectDrydockVersionForTesting` now accepts `undefined` to clear the
module-level `_drydockVersion` cache (the `!== undefined` guard in
`drydockVersion()` makes it naturally re-prime from `getVersion()`).
- The welcome-version test resets the cache before
`vi.mocked(getVersion).mockClear()`, so it stays order-independent: an
earlier inject or welcome frame can no longer leave the cache primed and
let the test pass without exercising the canonical `getVersion()` path.
**Same bug class one describe block down**
- The injector suite ended by "restoring" the cache to a literal
`'1.5.0'` β leaving every later test in the file primed with a stale
magic value, the exact order-dependency this PR fixes. It now resets to
`undefined` like the new idiom.
**Duplicate test scenario in the orchestration suite**
- `keeps an errored broken reference on the fast path and preserves its
digest` (repair-branch digest stickiness) and `repairs a broken
reference for an errored container on the fast path` (fast-path
eligibility) ran byte-identical arrange/act; the newer test asserts a
strict superset. Folded into one test whose name now carries both
intents. Coverage verified 100% across all metrics at directory scope
after the merge.
**Comment accuracy in `container-processing.ts`**
- The catch-block comment implied release-notes enrichment can reject
after a fresh result; `enrichContainerWithReleaseNotes` swallows its own
errors and never rejects, so the guard is defense-in-depth. The comment
now says so instead of overstating the live path.
## Verification
- `npx vitest run api/portwing-ws.test.ts
watchers/providers/docker/container-processing.test.ts
watchers/providers/docker/docker-image-details-orchestration.test.ts` β
158/158 pass.
- Directory-scope coverage: `docker-image-details-orchestration.ts` and
`container-processing.ts` both 100% branches/lines/functions/statements.
- Full pre-push gate (biome, qlty, coverage, build) green on push.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
- β¨ Added support for clearing the cached Drydock version with
`undefined`.
- π§ Changed API tests to reset the version cache, preventing
order-dependent behavior.
- π§ Clarified the defensive result-restore comment in Docker container
processing.
- ποΈ Removed a duplicate errored fast-path repair test and consolidated
digest assertions.
## Concerns
- Verify the targeted test suite and pre-push checks remain green.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π fix(security): report the configured scanner timeout in deadline errors (#559)
## Summary
- CI flake: `docker.test.ts` asserts the exact message `Scanner worker
timed out after 10ms`, but `waitForTimeout(timeoutMs)` bakes whatever
value it's armed with into the `DockerScannerTimeoutError` message. The
`run()` call site at the container-start phase arms the timer with
`remainingTimeoutMs` (configured `timeoutMs` minus elapsed setup time),
so if even ~1ms elapses before that timer arms, the message reads e.g.
`"9ms"` instead of the configured `"10ms"`. Failed twice on `main` run
29657499692.
- Root-cause fix: `waitForTimeout` now takes an optional
`reportedTimeoutMs` (defaults to `timeoutMs`) used only in the error
message, while `setTimeout` still arms with the real remaining budget.
The call site that passes `remainingTimeoutMs` now also passes
`runOptions.timeoutMs` as the reported value, so the displayed timeout
always matches what was configured β decoupled from how much budget was
actually left when the timer armed.
- Added a deterministic regression test (`reports the configured timeout
even when the deadline is armed mid-flight`) that mocks `Date.now` to
force a non-zero elapsed-setup remainder before the container-start
phase arms its timer, then asserts the rejection message still reads
`Scanner worker timed out after 10ms`.
This is test-hygiene for the rc.3 line β no runtime behavior change
beyond the error message text (the timer still fires after the same
remaining budget as before).
## Test plan
- [x] `npx vitest run security/backends/docker.test.ts` β 63/63 passed
- [x] `npx vitest run --coverage security/backends/` β `docker.ts` at
100% lines/branches/functions/statements in
`coverage/coverage-summary.json`
- [x] `npx biome check security/backends/docker.ts
security/backends/docker.test.ts` β clean
- [x] Full local pre-push gate (biome, qlty, qlty-smells,
workflow-tests, typecheck, coverage, build) β all green
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
- π Fixed scanner timeout errors to report the configured timeout
instead of the reduced remaining budget.
- π§ Updated `waitForTimeout` to separate timer duration from reported
timeout.
- β¨ Added a deterministic regression test covering elapsed setup time.
- β
Docker tests, coverage, formatting, and pre-push checks pass.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π security: harden SSRF, WS origins, auth fail-closed, store perms, session cookie, CSP, CI secret scans (#567)
## What
Nine security-hardening findings from the 2026-07-20 internal security
review, as 12 atomic commits plus CHANGELOG entries:
- **SSRF** (`app/triggers/providers/http`, Hass webhook URL handling,
registry/icon fetches): guarded DNS lookup blocks
cloud-metadata/link-local resolution on every request (override:
`allowmetadata=true`), and redirects can't hop into blocked address
space (DNS-rebinding + cross-host redirect protection).
- **WebSocket origin validation** (`app/api/ws-upgrade-utils.ts`): full
scheme/host/port origin comparison;
`X-Forwarded-Proto`/`X-Forwarded-Host` honored only with trust proxy
enabled; socket transport used otherwise.
- **Anonymous auth fails closed on upgrades**
(`app/authentications/providers/anonymous`): the warn-and-serve
grandfather path is gone β no auth configured (or anonymous unconfirmed)
now refuses to start, same as fresh installs.
`DD_ANONYMOUS_AUTH_CONFIRM=true` is the explicit opt-in. **Behavior
change for upgraders running open dashboards β called out in the
CHANGELOG.**
- **Store permissions**: `/store` created `0700` (Dockerfile), `dd.json`
`0600`, `umask 077` in the entrypoint and store process so Loki autosave
temp/replacement files never widen.
- **Application-scoped session cookie**: `drydock.sid` replaces the
Express default `connect.sid`. **One-time sign-out on upgrade.**
- **Icon CDN pinning** (`app/api/icons/providers.ts`):
dashboard-icons/selfh.st/simple-icons pinned to exact revisions/versions
instead of floating refs.
- **Website CSP** (`apps/web`): static `'unsafe-inline'` header CSP
replaced by a request-scoped nonce CSP built in `src/proxy.ts` per
Next.js guidance; `experimental.sri` stays off (the existing guard test
still enforces that) and the vercel.json CSP header is removed in favor
of the nonce policy. New `content-security-policy.test.mjs` covers the
builder.
- **CI secret scanning**: checksum-pinned Gitleaks in `ci-verify.yml`
with an exact-baseline `.gitleaksignore` (427 historical entries) and
`scripts/scan-secrets.sh` for local parity.
- **Dependency-test repairs**: the demo/scripts lockfile security tests
now accept patched upgrade ranges instead of pinning to vulnerable-era
versions.
## Why
Internal security review follow-up (report kept local by policy).
Targets the v1.6.1/rc.3 train via `dev/v1.6` β nothing here touches the
rc.2 soak or the GA promotion candidate.
## Tests
Full pre-push gate green on this exact branch (cut from
`origin/dev/v1.6`, cherry-picked 1:1 from the review line): app and ui
coverage gates at 100% on all four metrics, builds, biome, qlty,
workflow tests. New coverage: forwarded-protocol WS branches,
fail-closed upgrade auth, SSRF lookup guards, store-permission
enforcement, CSP builder.
## Notes for review
- The anonymous-auth fail-closed change and the session-cookie rename
are the two user-visible behavior changes; both have explicit CHANGELOG
entries with migration guidance.
- `vercel.json` loses its static CSP intentionally β the nonce policy
from `proxy.ts` supersedes it (a static header can't carry a per-request
nonce).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
- π Security: Hardened SSRF protection with DNS re-resolution,
metadata/link-local address blocking, pinned DNS results, and disabled
redirects (`maxRedirects: 0`).
- π Security: Strengthened WebSocket `Origin` validation by checking
both effective protocol + host with TLS/trust-proxy awareness.
- β οΈ Breaking: Anonymous authentication now requires explicit
`DD_ANONYMOUS_AUTH_CONFIRM=true` and fails closed during upgrade without
confirmation.
- β οΈ Breaking: Renamed the session cookie to `drydock.sid`, updating
CSRF and OpenAPI security scheme configuration and related tests.
- π Security: Enforced restrictive filesystem permissions for the store
(directory/file modes), Docker `/store` permissions (`0700`), and Docker
entrypoint-created files via `umask 077`; store permission repair now
avoids the TOCTOU window by handling `ENOENT` only.
- π Security: Replaced moving icon CDN references with pinned
versions/immutable upstream revisions for all affected providers.
- π Security: Implemented request-scoped nonce CSP handling for the
website (proxy-injected CSP + per-request nonce), removed global static
CSP header behavior, and ensured nonce-safe JSON-LD rendering via a
shared `JsonLd` component.
- β¨ Added: CI βsecretsβ security job using checksum-pinned Gitleaks with
full-history + tracked-tree scanning, repository-local script-driven
invocation, and a historical baseline ignore configuration.
- π§ Changed: Updated dependency-security tests to accept patched semver
upgrade ranges; expanded SSRF/WebSocket test fixtures for
axios/proxy/trust-proxy correctness; tightened lockfile vulnerability
tests to scan nested package paths.
- π Fixed: Updated and repaired security/format/workflow/integration
tests to match hardened behavior (store perms, cookie rename, pinned
icons, CSP/nonce propagation, SSRF DNS guards, and CI secrets scanning
assertions).
## Concerns
- Large, line-specific `.gitleaksignore` baseline may be brittle as repo
files evolveβensure it stays aligned with the pinned scanning commands
and modes.
- Confirm all runtime clients/integrations tolerate the `drydock.sid`
cookie rename (including any external reverse proxies or browser
automation).
- Validate CSP nonce propagation in all paths (dev vs prod,
caching/prefetch, and proxy matcher exclusions) and ensure no leftover
static CSP header conflicts.
- Review DNS guard correctness across IPv4/IPv6, `all: true` lookups,
and connection-time resolution failures; ensure metadata/link-local
detection is complete for every resolved address.
- Ensure store permission enforcement + repairs behave correctly under
all failure modes (only `ENOENT` swallowed; all other errors rethrown)
and that `umask` is correctly captured/restored.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π fix(store): only restart the maturity clock when the update candidate identity changes (#568)
## What
Fixes the #565 report: a manual per-container "check for updates"
restarted the maturity soak countdown, which reads to the user as their
maturity settings being wiped (the config itself was never touched β
only the clock).
Root cause: `getUpdateLifecycleTimestamp`'s restart branch gated on
`Container#resultChanged`, which compares `tag`, `suggestedTag`,
`digest`, AND `created`. The last two are display metadata that can
drift between scans (a manual recheck bypasses the registry poll cache),
so a cosmetic wobble re-stamped `updateDetectedAt` to now and the
countdown snapped back to the full window. Registries with trusted
publish dates (Docker Hub/GHCR) mask this via the publishedAt floor;
everyone else is fully exposed.
Fix: new `hasCandidateIdentityChanged()` comparing only `tag` +
`digest`; the lifecycle restart branch uses it directly.
`resultChanged`/`hasResultChanged` are untouched β the
trigger-notification `changed` flag in `container-processing.ts` must
stay sensitive to `suggestedTag`/`created` changes for notification
content. Intentional behavior is preserved: a genuinely new candidate
(tag or digest change) still restarts the soak.
## Why
Diagnosed against the live module (drove `updateContainer` through the
exact recheck sequence): identical results leave the clock alone; a
`suggestedTag`-only or `created`-only difference reset it every time.
Pre-existing since the v1.5.x maturity gate (restart branch from
v1.5.1-rc.1, comparison set from v1.5.0) β not an rc.2 regression.
Targets the v1.6.1/rc.3 train.
## Tests
Unit coverage for `hasCandidateIdentityChanged` (undefined handling both
directions, each field diff, metadata-only diff β false) plus three
store-level regression tests on `updateContainer`: metadata-only recheck
preserves `updateDetectedAt`; tag change resets; digest change resets.
Full pre-push gate green; `app/model/container.ts` and
`app/store/container.ts` at 100% on all four metrics.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
- π Fixed maturity soak countdowns restarting when only display metadata
changes.
- β¨ Added `hasCandidateIdentityChanged()` to compare update candidate
identity using only `tag` + `digest`.
- π§ Updated lifecycle timestamp logic to restart only on candidate
identity (`tag`/`digest`) changes (leaving existing `resultChanged`
behavior for trigger notifications intact).
- β¨ Added unit + store regression tests covering metadata-only changes,
tag/digest changes, and undefined handling.
- π Security: Updated `brace-expansion` minimum safe version (5.0.7) and
related e2e override validation.
- π§ Dependency: Bumped pinned `js-yaml` override (4.2.0 β 4.3.0) and
`brace-expansion` override (5.0.6 β 5.0.7).
- Ensure maturity restart logic uses the correct βraw updateβ `result`
shape consistently across persistence/rechecks.
- Verify `hasCandidateIdentityChanged()` undefined/missing-field
semantics align with actual runtime inputs.
- Confirm trigger notification paths still rely on existing
`resultChanged` behavior as intended.
- Run/verify unit + store tests and e2e security suite with the updated
dependency overrides.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π docs(freshness): correct stale auth/SSRF/WS/UI copy for the v1.6.1 delta (#569)
## What
Docs/copy freshness pass for the v1.6.1 delta (the #567 security batch,
the #566 display batch, and the #568 maturity-clock fix). 30 findings
from an adversarially-verified audit workflow (8 surface auditors, every
finding independently re-verified against the shipped code before it
counted; 1 rejected in verification), applied as one coherent edit per
doc region:
- **Anonymous-auth fail-closed (A1)**: the authentications page
(callout, behavior matrix, and the "Upgrading from v1.3.x" section), the
security guide, the README banner + quickstart line, and DEPRECATIONS.md
all still promised upgraders a warn-and-serve grace period. Rewritten
everywhere: upgrades now fail closed exactly like fresh installs;
`DD_ANONYMOUS_AUTH_CONFIRM=true` is the opt-in.
- **Session cookie rename (A4)**: `connect.sid` β `drydock.sid` one-time
sign-out documented in DEPRECATIONS.md, the upgrading section, and the
README banner.
- **SSRF hardening (A2)**: the HTTP trigger page now documents the
guarded DNS lookup, redirect refusal, and the `ALLOWMETADATA` escape
hatch.
- **WebSocket origin checks (A3)**: server config, FAQ, quickstart
reverse-proxy warning, `api/log.mdx`, and `api/container.mdx` now state
that origin validation covers scheme/host/port and that forwarded
headers are honored only with `DD_SERVER_TRUSTPROXY`.
- **UI vocabulary (B3/B4/B5)**: watchers page and FAQ updated for the
pin-glyph/"Current" pinned treatment; eligibility + API docs document
the new additive `clockSource`/`clockStartAt` blocker fields; the UI
page covers the column-picker auto-hidden annotation. Wording grounded
in the actual #566 locale strings, not paraphrased.
- **Website quickstarts actually start**: the shared `DockerRunSnippet`
(all 7 comparison pages) and the FAQ quickstart now include the auth
opt-in env var β without it the one-liner produces a container that
refuses to start on the v1.6 line.
Also rides along: `protobufjs` pinned to 7.6.5 (CVE-2026-59877 /
GHSA-j3f2-48v5-ccww, third fresh advisory today) in app (new override)
and e2e (existing override bumped) β it began failing the qlty
osv-scanner gate mid-push.
Deliberately skipped: `content/docs/current/updates/index.mdx` (strictly
per-release curated notes; rc.3 highlights belong to the actual cut) and
`UPGRADE-NOTES.md` (version-gated to 1.4.6β1.5.x by
`scripts/append-upgrade-notes.mjs`; v1.6 content there would leak into
1.5.x release notes).
## Why
The security batch merged with zero docs changes, leaving the auth docs
actively contradicting shipped behavior. Docs on `dev/v1.6` publish at
the GA devβmain merge, so these corrections go live exactly when the
behavior does.
## Tests
Full pre-push gate green. apps/web script tests 43/43; MDX parse
validated via sync-docs + fumadocs-mdx; biome clean.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
- π Documented fail-closed anonymous authentication for both fresh
installs and upgrades; require `DD_ANONYMOUS_AUTH_CONFIRM=true` (and
clarified `/health` vs API response behavior).
- β οΈ Security documented βno deprecation windowβ enforcement: removed
upgrade grandfather path when anonymous auth isnβt configured/confirmed;
upgrades stop serving/fail closed.
- β οΈ π Documented `connect.sid` β `drydock.sid` session cookie rename
and one-time sign-out on upgrade.
- π Documented SSRF protections for guarded DNS lookup, redirect refusal
(3xx never followed), and `ALLOWMETADATA` escape hatch.
- π Documented WebSocket `Origin` validation (scheme/host/port) and
clarified reverse-proxy requirements via `DD_SERVER_TRUSTPROXY` +
forwarded headers (`X-Forwarded-Proto`/`X-Forwarded-Host`), including
log-stream connection failures when misconfigured.
- β¨ Documented maturity-clock details for `maturity-not-reached` (clock
source + ISO-8601 start timestamp payload) and UI countdown rendering
behavior.
- β¨ Updated UI copy/annotations for column visibility and βhidden to
fitβ behavior.
- β¨ Updated watcher UI description for fully-pinned tags to reflect
`updateInsight` chip styling + persistent pin glyph/tooltips.
- π§ Added `DD_ANONYMOUS_AUTH_CONFIRM=true` to website quickstarts and
updated Docker run snippet wording.
- π§ Pinned `protobufjs` to `7.6.5` in both app and e2e dependencies.
- π§ Updated doc identity/release tests to assert current fail-closed
semantics for `content/docs/current` while preserving legacy v1.5
behavior.
## Concerns
- Confirm `DD_SERVER_TRUSTPROXY` docs explicitly require forwarding
`X-Forwarded-Proto` and `X-Forwarded-Host` for scheme/host matching.
- Validate that the documented fail-closed behavior matches the actual
endpoints: API 401 behavior vs `/health` 503 reporting.
- Ensure the βredirects never followedβ documentation aligns with the
actual redirect-following disablement and how `ALLOWMETADATA` is
evaluated.
- Verify cookie rename handling is documented consistently across all
relevant upgrade/install docs (including one-time sign-out semantics).
- Check that WebSocket origin validation requirements cover both
dashboard log viewer and container log streaming endpoints (upgrade vs
steady-state behavior).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* β¨ feat(ui): display-honesty batch β update-status vocabulary, pin glyph, one-clock maturity panel (#566)
## What
The display-honesty batch: renames and removes the update-status
vocabulary that #498 and #556 reported as unreadable, based on a
peer-tool UX research pass (WUD, Diun, Watchtower, Cup, Dockge, Arcane,
Portainer, Komodo, Home Assistant, Renovate/Dependabot) and an
owner-approved design.
- **"Digest" β "Digest update"** in the Update column (Cup's web-UI
string, Arcane's popover title). A digest-only change now reads as what
it is: an update.
- **NEW/MATURE badges removed.** The freshness fact moves into the
update-kind badge's tooltip ("Detected 3 days ago"). Idle state gets no
chrome; `UpdateMaturityBadge.vue` deleted.
- **"Pinned" leaves the Update column.** It was an update status
pretending to be a tag property (only rendered when a newer family
version existed β the exact inconsistency reported in #498). Pinned-ness
is now a persistent pin glyph on the Tag cell (existing `pin` icon-map
entry, not the padlock β that's a live action affordance), with a
tooltip carrying the `dd.tag.family=loose` remedy. Up-to-date pinned
rows read "Current" like everything else.
- **Maturity panel keeps exactly one clock.** The policy sentence names
the clock the gate actually measures against ("Candidate published
{date} β {n} more days until the {minDays}-day minimum", detection-date
fallback), and the duplicated countdown collapses to "{countdown} Β·
unlocks {date}".
- **Backend rider:** `resolveMaturityClock()` in
`app/model/maturity-policy.ts` (behavior-preserving refactor of
`getMaturityStartMs`) now exposes `clockSource`/`clockStartAt` as
additive optional fields on the maturity blocker's `details`, and both
UI call sites (`container-mapper.ts`, `useContainerPolicy.ts`) consume
the backend verdict instead of re-deriving maturity-blocked from
`updateDetectedAt` alone β three drifting formulas become one.
- **Column picker honesty:** breakpoint-auto-hidden columns stay checked
but get a muted "{column} β hidden to fit" annotation, and the "+1"
toolbar badge tooltip names the hidden columns instead of a bare count.
- **Unknown-kind badge:** the silent unknown-update-kind fallback now
renders a neutral "Unknown" badge.
- **Bouncer "Blocked" β "Security hold"** (display string only; the
maturity/hard-block wording is untouched).
i18n: net 12 keys added / 7 removed / 12 values changed, English
catalogs only per convention, plus stale values for the *repurposed*
keys dropped from all 16 non-English locales so they fall back to
English until Crowdin retranslates (otherwise German would render the
old badge text "NEU" as a tooltip).
## Why
Direct follow-up to the rc.2 field feedback in #498 (Cesc1986) and the
#556 display cluster. Design researched and approved before
implementation; targets v1.6.1/rc.3 β nothing here touches the rc.2
soak.
## Tests
TDD gates green in full: app 100/100/100/100 (12,244 tests), ui
100/100/100/100 (4,236 tests). New coverage for the clock resolution
(source precedence + tie-breaks), blocker serialization, pin glyph,
unknown-kind fallback, column-picker annotation branches, collapsed
countdown, and both `useContainerPolicy` paths (backend-verdict
preferred, legacy fallback).
## Notes for review
- `dashboard-container-metrics.ts` kept its `freshUpdates` KPI via a
local `isFreshUpdate()` helper (same 7-day threshold) after the badge
helper's deletion β dashboard behavior unchanged.
- The owner does a rendered visual pass in the QA environment before
this merges.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
- β¨ Added backend-resolved maturity clocks with source/start metadata
and defensive eligibility parsing.
- β¨ Added shared update-kind labeling with localized `Unknown` fallback.
- β¨ Added pin-gate policy detection and `tagPinGated` propagation from
backend to UI.
- β¨ Added auto-hidden column annotations and accurate picker
badge/tooltips.
- π§ Changed update terminology: βDigest updateβ and βSecurity holdβ.
- π§ Consolidated maturity countdowns into update-kind/status tooltips
and insight notes.
- π§ Moved pinned status to persistent pin glyphs in version/tag cells.
- π§ Preserved dashboard fresh-update KPI behavior with an independent
seven-day threshold.
- π§ Improved tooltip fallback synchronization and registry search by
dotted instance ID.
- π§ Updated pluralized legacy-configuration banner translations.
- ποΈ Removed `UpdateMaturityBadge`, `getUpdateMaturity`, separate
NEW/MATURE badges, and the dedicated pinned update-status state.
- ποΈ Removed stale localized `blockedByBouncer`, `updateAge`,
maturity-badge, and pinned-summary values.
- β οΈ Updated the `Container` contract: removed `updateMaturity`; added
`updateMaturityTooltip`, `updatePolicyState`, and `tagPinGated`.
## Concerns
- Verify backend `updateEligibility` payloads expose sufficiently
reliable maturity clock metadata.
- Confirm Crowdin retranslation coverage for removed and repurposed
locale keys.
- Audit downstream consumers for the removed `getUpdateMaturity`,
`updateMaturity`, and `pinned` APIs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π docs(release): bump release identity to v1.6.0-rc.3 (#571)
Release plumbing for the v1.6.0-rc.3 cut β the full identity bump the
guard tests enforce.
- Dated `[1.6.0-rc.3] β 2026-07-21` CHANGELOG heading, fresh empty
`[Unreleased]` section, matching link-reference definitions
- rc.3 across README badge/highlights summary, website site-config +
timeline, updates page (new rc.3 highlights section), API doc example
payloads, quickstart tag matrix, and the demo runtime fixtures
- Guard tests updated in lockstep: release-identity,
release-docs-identity (compare base now `v1.6.0-rc.2...v1.6.0-rc.3`),
changelog-links
rc.3 delta over rc.2: #566 display-honesty batch, #567 security
hardening, #568 maturity-clock restart fix (#565), #569 docs freshness.
Verified: 105/105 script tests, 43/43 web-script tests, full pre-push
gate green.
Last dev-side change before the release head freeze.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
π§ **Changed**
- Updated release identity from `v1.6.0-rc.2` to `v1.6.0-rc.3` across
README, website metadata, roadmap, docs, API examples, quickstart tags,
and demo fixtures.
- Added the dated `v1.6.0-rc.3` updates section and refreshed changelog
link references.
- Updated release identity and documentation guard tests, including rc
comparison ranges and candidate fixtures.
β¨ **Added**
- Documented the rc.3 changes covering issues `#566`, `#567`,
`#568/`#565, and `#569`.
π **Security**
- Documented the rc.3 security hardening batch.
## Verification
- Script tests: 105/105 passed
- Web-script tests: 43/43 passed
- Full pre-push gate: passed
## Concerns
- Verify the release date `July 21, 2026` is correct.
- Confirm all remaining `1.6.0-rc.2` references are intentional.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π fix(v1.6): remediate comprehensive review findings (#574)
Closes every repository-actionable finding from the comprehensive v1.6 review, incorporates the superseded E2E correction from #573, and includes all follow-up review fixes and evidence hardening.
* π§ͺ test(security): avoid secret-like cache fixtures (#576)
## Summary
- replace two secret-like bearer-token fixtures with low-entropy test
values
- preserve the strict cache-expiry boundary assertions
- restore the tracked-working-tree Gitleaks gate after earlier tests
shifted the allowlisted source lines
## Verification
- `node ./node_modules/vitest/vitest.mjs run
registries/BaseRegistry.test.ts --maxWorkers=1 --fileParallelism=false`
(221 passed)
- `./scripts/scan-secrets.sh` (history and tracked tree clean)
- full pre-push gate (100% app/UI coverage, app/UI builds, scripts,
workflows, typecheck, Zizmor)
* π§ͺ test(ci): gate Playwright on QA fixture readiness (#589)
Rescued from the `fix/playwright-qa-readiness` worktree during the
mainβdev reconciliation audit β this commit had reached neither branch
and would have been lost when that worktree was cleaned up.
It's the root-cause fix for the Playwright flake that burned an rc.5 cut
cycle (`apiRequestContext.post: Timeout 45000ms` on the
notification-bell spec, plus the intermittent "Start drydock" Cucumber
failures).
## What it does
- **`e2e/playwright/auth.setup.ts`** β auth setup now polls
`/api/v1/containers` until the *complete* 29-container QA fixture
snapshot is present (specific named containers with their `dd.group`
labels and a populated `result`) before writing `storageState`.
Previously specs could start against a half-populated store, so
assertions raced the startup watcher scan. 180s poll budget at 2s
intervals, setup timeout raised to 240s.
- **`test/qa-compose.yml`** β parks the local and remote watcher crons
on `0 0 29 2 *` with `JITTER=0` and `WATCHEVENTS=false`, so no scheduled
or event-driven rescan can mutate fixtures mid-suite. The startup scan
still runs; only the later races are removed.
- **`.github/tests/e2e-playwright-workflow.test.ts`** β coverage
asserting the readiness gating stays wired up.
## Verification
Full pre-push gate green: clean-tree, ts-nocheck, biome, qlty,
qlty-smells, scripts-test, workflow-tests (9 files / 46 tests),
typecheck-ui, coverage (app + ui both at 100%), build.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
β¨ Added
- Playwright fixture-readiness gating for 29 expected QA containers
before saving `storageState`.
- Workflow test coverage for setup sequencing and readiness
configuration.
- Documentation of the startup race and cold-stack validation.
π§ Changed
- Increased Playwright setup timeout to 240 seconds with a 180-second
readiness polling budget.
- Disabled scheduled and event-driven watcher rescans during browser
tests while preserving the startup scan.
- Added required container, label, result, and update-availability
checks.
- Readiness validation is tightly coupled to a hardcoded set of 29
containers and labels.
- The `0 0 29 2 *` watcher schedule should remain intentional and
documented as a disabled schedule.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π§ chore(ci): block release cuts when main has drifted from the dev branch (#590)
v1.6's rc.3βrc.5 fixes landed straight on `main` while `dev/v1.6` sat
112 files behind holding superseded UI, and nothing caught it for weeks.
Two gaps made that possible; both are closed here.
## 1. `release-cut.yml` refuses to tag a drifted main
New *Assert main is in sync with the active dev branch* step derives
`dev/vX.Y` from the release tag and fails the cut when main's tree
differs, so a direct-to-main fix can't be tagged without being
forward-ported first.
- Derives the branch from the tag rather than hardcoding a version
(`v1.6.0-rc.6` β `dev/v1.6`, `v1.10.3-rc.1` β `dev/v1.10`).
- Skips cleanly when the dev branch is absent β retired after GA, or not
yet created.
- Runs after `Checkout` and before any tagging or publishing work.
**It compares trees, not commit ancestry.** This repo allows squash
merges only (`allow_merge_commit: false`, `allow_rebase_merge: false`),
so every `dev β main` sync mints a commit `dev` lacks. A `merge-base
--is-ancestor` check would fail on every cut after the first and would
have to be disabled, defeating the guard. Identical trees is the
invariant that actually holds.
## 2. `ci-verify.yml` now gates `dev/**` pull requests
The trigger was `pull_request: branches: [main]`, so **PRs into
`dev/v1.6` ran no backend or frontend CI at all** β dev was only ever as
good as whatever the author ran locally. The integration branch is where
features are supposed to land, so it now gets the same gate main gets.
## Verification
Full pre-push gate green (284s): clean-tree, ts-nocheck, biome, qlty,
qlty-smells, scripts-test, workflow-tests, typecheck-ui, coverage (app +
ui at 100%), build, zizmor.
New tests cover the failure path, the tree-not-ancestry choice, the
post-GA skip, step ordering, and the widened trigger. Guard logic was
also exercised against the live repo: tagβbranch derivation for 5 tag
shapes, `ls-remote` presence/absence, and a real `main` vs `dev/v1.6`
comparison (currently in sync, so a cut today passes).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog
β¨ Added
- Added a release guard that blocks tagging and publishing when `main`βs
tree differs from the active `dev/vX.Y` branch.
- Added coverage for drift detection, retired development branches, tree
comparison, ordering, and CI triggers.
π§ Changed
- CI verification now runs for pull requests targeting `main` and
`dev/**`.
- Confirm the release guard derives the expected development branch
correctly from `release_tag`.
- Confirm missing development branches intentionally bypass the drift
check.
- Confirm the guard executes before all tagging and publishing steps.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π docs(readme): move the language switcher above the logo (#591)
The switcher read as buried under the logo, title, and tagline. Since
the tagline directly above it is English, a non-English reader hit a
sentence they may not read before finding their own language. It is now
the first thing rendered.
Kept inside `<div align="center">` rather than above it, so it stays
centered instead of rendering left-aligned over a centered logo.
Applied to all seven READMEs (`README.md` + 6 translations) so the
copies do not drift β the Spanish one needed a different matcher since
Spanish is bold rather than a link there.
Pure move: 2 lines changed per file, no content edits. Full pre-push
gate green (275s), `release-docs-identity` 11/11.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
### Changelog
- π§ Changed language switcher placement in all seven README files,
moving it above the logo while keeping it within the centered header.
- ποΈ Removed the previous switcher locations without changing content.
- β
Full pre-push gate passed, including `release-docs-identity`
(11/11).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π§ chore(ci): point bot PRs and the browser gate at the integration branch (#592)
#590 makes release-cut refuse to tag a drifted `main`. This is the other
half: the automation that was creating the drift.
Renovate and Crowdin both opened their PRs against `main`. Every one of
those merges put work on `main` that `dev/v1.6` never received β 11 such
PRs are open right now β so with #590 in place the next cut would fail
on drift the bots themselves caused.
## Renovate
`baseBranches` now matches `dev/vX.Y` by pattern instead of falling
through to the default branch. A pattern rather than a pinned `dev/v1.6`
so it follows the branch into `dev/v1.7` without an edit that gets
forgotten at the minor bump. Verified the regex accepts `dev/v1.6` and
`dev/v1.10` and rejects `main`, `dev/v1.6.1`, and `release/v1.6`.
## Crowdin
The action's base branch is a static input, so a hardcoded value would
go stale the same way. A new step resolves it at run time: highest
`dev/vX.Y` on origin, sorted with `-V` so `dev/v1.10` beats `dev/v1.9`
rather than losing a lexical compare. Falls back to the default branch
when no dev branch exists β the window between a GA and the next branch
being cut β so translations are never stranded with nowhere to land.
Needs `fetch-depth: 0`; the default shallow clone has no refs to query.
Source upload now also fires on `dev/**`, since that's where the English
strings change first. Resolution logic was run against the live repo: it
returns `dev/v1.6`.
## Playwright
`pull_request` was `[main]` only, so **no PR into `dev/v1.6` has ever
run the browser suite.** The first signal was release-cut polling "Wait
for successful E2E Playwright on release source SHA", which is a cut
cycle burned on something a PR run would have caught. Same widening #590
applied to `ci-verify`.
## Verification
Full pre-push gate green (275s): clean-tree, ts-nocheck, biome, qlty,
qlty-smells, scripts-test, workflow-tests, typecheck-ui, coverage (app +
ui at 100%), build, zizmor.
New `integration-branch-targets.test.ts` covers the Renovate pattern
against five branch shapes, the Crowdin resolution step and its
fallback, the `fetch-depth` requirement, and both widened trigger lists.
The 11 open bot PRs get retargeted once this lands.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
β¨ Added
- Tests covering integration-branch matching, Crowdin fallback behavior,
checkout depth, and workflow triggers.
π§ Changed
- Renovate now targets only `dev/vX.Y` branches.
- Crowdin dynamically targets the highest matching `dev/vX.Y` branch,
falling back to the default branch.
- Crowdin uploads now trigger from `main` and `dev/**`.
- Playwright and `ci-verify` cover pull requests targeting `main` and
`dev/**`.
- Crowdin uses full-depth checkout for branch resolution.
- Contributor guidance now directs changes and PRs to the active release
branch.
π Fixed
- Prevented bot- and contributor-created drift on `main` from causing
release-cut failures.
- Retarget the 11 existing bot PRs to the appropriate integration branch
after merge.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* π¦ deps(ui): update npm minor/patch (ui) (#485)
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@fontsource/comic-mono](https://fontsource.org/fonts/comic-mono)
([source](https://redirect.github.com/fontsource/font-files/tree/HEAD/fonts/other/comic-mono))
| [`5.2.5` β
`5.3.0`](https://renovatebot.com/diffs/npm/@fontsource%2fcomic-mono/5.2.5/5.3.0)
|

|

|
| [@fontsource/commit-mono](https://fontsource.org/fonts/commit-mono)
([source](https://redirect.github.com/fontsource/font-files/tree/HEAD/fonts/other/commit-mono))
| [`5.2.5` β
`5.3.0`](https://renovatebot.com/diffs/npm/@fontsource%2fcommit-mono/5.2.5/5.3.0)
|

|

|
|
[@fontsource/ibm-plex-mono](https://fontsource.org/fonts/ibm-plex-mono)
([source](https://redirect.github.com/fontsource/font-files/tree/HEAD/fonts/google/ibm-plex-mono))
| [`5.2.7` β
`5.3.0`](https://renovatebot.com/diffs/npm/@fontsource%2fibm-plex-mono/5.2.7/5.3.0)
|

|

|
| [@fontsource/inconsolata](https://fontsource.org/fonts/inconsolata)
([source](https://redirect.github.com/fontsource/font-files/tree/HEAD/fonts/google/inconsolata))
| [`5.2.8` β
`5.3.0`](https://renovatebot.com/diffs/npm/@fontsource%2finconsolata/5.2.8/5.3.0)
|

|

|
|
[@fontsource/jetbrains-mono](https://fontsource.org/fonts/jetbrains-mono)
([source](https://redirect.github.com/fontsource/font-files/tree/HEAD/fonts/google/jetbrains-mono))
| [`5.2.8` β
`5.3.0`](https://renovatebot.com/diffs/npm/@fontsource%2fjetbrains-mono/5.2.8/5.3.0)
|

|

|
|
[@fontsource/source-code-pro](https://fontsource.org/fonts/source-code-pro)
([source](https://redirect.github.com/fontsource/font-files/tree/HEAD/fonts/google/source-code-pro))
| [`5.2.7` β
`5.3.0`](https://renovatebot.com/diffs/npm/@fontsource%2fsource-code-pro/5.2.7/5.3.0)
|

|

|
| [@iconify-json/lucide](https://icon-sets.iconify.design/lucide/) |
[`1.2.116` β
`1.2.118`](https://renovatebot.com/diffs/npm/@iconify-json%2flucide/1.2.116/1.2.118)
|

|

|
| [@iconify-json/tabler](https://icon-sets.iconify.design/tabler/) |
[`1.2.35` β
`1.2.37`](https://renovatebot.com/diffs/npm/@iconify-json%2ftabler/1.2.35/1.2.37)
|

|

|
| [@tailwindcss/vite](https://tailwindcss.com)
([source](https://redirect.github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite))
| [`4.3.2` β
`4.3.3`](https://renovatebot.com/diffs/npm/@tailwindcss%2fvite/4.3.2/4.3.3)
|

|

|
|
[@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.4` β
`25.9.5`](https://renovatebot.com/diffs/npm/@types%2fnode/25.9.4/25.9.5)
|

|

|
|
[@vitejs/plugin-vue](https://redirect.github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue#readme)
([source](https://redirect.github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue))
| [`6.0.7` β
`6.0.8`](https://renovatebot.com/diffs/npm/@vitejs%2fplugin-vue/6.0.7/6.0.8)
|

|

|
| [@vitest/coverage-v8](https://vitest.dev/guide/coverage)
([source](https://redirect.github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8))
| [`4.1.9` β
`4.1.10`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-v8/4.1.9/4.1.10)
|

|

|
| [knip](https://knip.dev)
([source](https://redirect.github.com/webpro-nl/knip/tree/HEAD/packages/knip))
| [`6.24.0` β
`6.27.0`](https://renovatebot.com/diffs/npm/knip/6.24.0/6.27.0) |

|

|
| [postcss](https://postcss.org/)
([source](https://redirect.github.com/postcss/postcss)) | [`8.5.16` β
`8.5.21`](https://renovatebot.com/diffs/npm/postcss/8.5.16/8.5.21) |

|

|
| [tailwindcss](https://tailwindcss.com)
([source](https://redirect.github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss))
| [`4.3.2` β
`4.3.3`](https://renovatebot.com/diffs/npm/tailwindcss/4.3.2/4.3.3) |

|

|
| [vite](https://vite.dev)
([source](https://redirect.github.com/vitejs/vite/tree/HEAD/packages/vite))
| [`8.1.3` β
`8.1.5`](https://renovatebot.com/diffs/npm/vite/8.1.3/8.1.5) |

|

|
| [vitest](https://vitest.dev)
([source](https://redirect.github.com/vitest-dev/vitest/tree/HEAD/packages/vitest))
| [`4.1.9` β
`4.1.10`](https://renovatebot.com/diffs/npm/vitest/4.1.9/4.1.10) |

|

|
| [vue](https://vuejs.org/)
([source](https://redirect.github.com/vuejs/core)) | [`3.5.39` β
`3.5.40`](https://renovatebot.com/diffs/npm/vue/3.5.39/3.5.40) |

|

|
|
[vue-i18n](https://redirect.github.com/intlify/vue-i18n/tree/master/packages/vue-i18n#readme)
([source](https://redirect.github.com/intlify/vue-i18n/tree/HEAD/packages/vue-i18n))
| [`11.4.6` β
`11.4.7`](https://renovatebot.com/diffs/npm/vue-i18n/11.4.6/11.4.7) |

|

|
| [vue-router](https://router.vuejs.org)
([source](https://redirect.github.com/vuejs/router)) | [`5.1.0` β
`5.2.0`](https://renovatebot.com/diffs/npm/vue-router/5.1.0/5.2.0) |

|

|
---
### Release Notes
<details>
<summary>fontsource/font-files (@​fontsource/comic-mono)</summary>
###
[`v5.3.0`](https://redirect.github.com/fontsource/font-files/compare/90f4206a30838317875479a13ee5046613af42f7...02b28c06b126c852a5b966b44aef777ca923f6f6)
[Compare
Source](https://redirect.github.com/fontsource/font-files/compare/90f4206a30838317875479a13ee5046613af42f7...02b28c06b126c852a5b966b44aef777ca923f6f6)
</details>
<details>
<summary>fontsource/font-files
(@​fontsource/commit-mono)</summary>
###
[`v5.3.0`](https://redirect.github.com/fontsource/font-files/compare/90f4206a30838317875479a13ee5046613af42f7...02b28c06b126c852a5b966b44aef777ca923f6f6)
[Compare
Source](https://redirect.github.com/fontsource/font-files/compare/90f4206a30838317875479a13ee5046613af42f7...02b28c06b126c852a5b966b44aef777ca923f6f6)
</details>
<details>
<summary>fontsource/font-files
(@​fontsource/ibm-plex-mono)</summary>
###
[`v5.3.0`](https://redirect.github.com/fontsource/font-files/compare/40ecb0c337fd649924783a87783dc2e6639bb6f2...02b28c06b126c852a5b966b44aef777ca923f6f6)
[Compare
Source](https://redirect.github.com/fontsource/font-files/compare/40ecb0c337fd649924783a87783dc2e6639bb6f2...02b28c06b126c852a5b966b44aef777ca923f6f6)
</details>
<details>
<summary>fontsource/font-files
(@​fontsource/inconsolata)</summary>
###
[`v5.3.0`](https://redirect.github.com/fontsource/font-files/compare/40ecb0c337fd649924783a87783dc2e6639bb6f2...02b28c06b126c852a5b966b44aef777ca923f6f6)
[Compare
Source](https://redirect.github.com/fontsource/font-files/compare/40ecb0c337fd649924783a87783dc2e6639bb6f2...02b28c06b126c852a5b966b44aef777ca923f6f6)
</details>
<details>
<summary>fontsource/font-files
(@​fontsource/jetbrains-mono)</summary>
###
[`v5.3.0`](https://redirect.github.com/fontsource/font-files/compare/40ecb0c337fd649924783a87783dc2e6639bb6f2...02b28c06b126c852a5b966b44aef777ca923f6f6)
[Compare
Source](https://redirect.github.com/fontsource/font-files/compare/40ecb0c337fd649924783a87783dc2e6639bb6f2...02b28c06b126c852a5b966b44aef777ca923f6f6)
</details>
<details>
<summary>fontsource/font-files
(@​fontsource/source-code-pro)</summary>
###
[`v5.3.0`](https://redirect.github.com/fontsource/font-files/compare/9c74cedc2a2c1989d2faf595c62c23f617144a30...02b28c06b126c852a5b966b44aef777ca923f6f6)
[Compare
Source](https://redirect.github.com/fontsource/font-files/compare/9c74cedc2a2c1989d2faf595c62c23f617144a30...02b28c06b126c852a5b966b44aef777ca923f6f6)
</details>
<details>
<summary>tailwindlabs/tailwindcss (@​tailwindcss/vite)</summary>
###
[`v4.3.3`](https://redirect.github.com/tailwindlabs/tailwindcss/blob/HEAD/CHANGELOG.md#433---2026-07-16)
[Compare
Source](https://redirect.github.com/tailwindlabs/tailwindcss/compare/v4.3.2...v4.3.3)
##### Fixed
- Support `--watch --poll[=ms]` in `@tailwindcss/cli` when filesystem
events are unreliable or unavailable
([#​20297](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20297))
- Canonicalization: match arbitrary hex colors against theme colors
case-insensitively (e.g. `bg-[#fff]` and `bg-[#FFF]` β `bg-white`)
([#​20298](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20298))
- Prevent Preflight from overriding Firefox's native
`iframe:focus-visible` outline styles
([#​20292](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20292))
- Ensure `theme('colors.foo')` in JS plugins resolves correctly when
both `--color-foo` and `--color-foo-bar` exist
([#​20299](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20299))
- Ensure fractional opacity modifiers work with named shadow sizes like
`shadow-sm/12.5`, `text-shadow-sm/12.5`, `drop-shadow-sm/12.5`, and
`inset-shadow-sm/12.5`
([#​20302](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20302))
- Parse selectors like `[data-foo]div` as two selectors instead of one
([#​20303](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20303))
- Ensure `@tailwindcss/postcss` rebuilds when a preprocessor like Sass
changes the input CSS without changing the input file on disk
([#​20310](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20310))
- Ensure CSS nesting is handled even when Lightning CSS isn't run, such
as in `@tailwindcss/browser` and Tailwind Play
([#​20124](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20124))
- Prevent achromatic theme colors from shifting hue when mixed in polar
color spaces like `oklch`
([#​20314](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20314))
- Ensure `--spacing(0)` is optimized to `0px` instead of `0` so it
remains a `<length>` when used in `calc(β¦)`
([#​20319](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20319))
- Load `@parcel/watcher` only when needed in `@tailwindcss/cli --watch`
mode, so one-off builds and `--watch --poll` work when `@parcel/watcher`
can't be loaded
([#​20325](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20325))
- Use explicit platform fonts instead of `system-ui` and `ui-sans-serif`
so CJK text respects the page's `lang` attribute on Windows
([#​20318](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20318))
- Prevent `@tailwindcss/upgrade` from rewriting ignored files when run
from a subdirectory
([#​20329](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20329))
- Ensure earlier `@source` rules pointing to nested files are scanned
when later `@source` rules point to files in parent folders
([#​20335](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20335))
- Prevent `@tailwindcss/vite` from triggering full page reloads when
scanned files are processed by Vite but haven't been loaded as modules
yet
([#​20336](https://redirect.github.com/tailwindlabs/tailwindcss/pull/20336))
</details>
<details>
<summary>vitejs/vite-plugin-vue (@​vitejs/plugin-vue)</summary>
###
[`v6.0.8`](https://redirect.github.com/vitejs/vite-plugin-vue/blob/HEAD/packages/plugin-vue/CHANGELOG.md#small-608-2026-07-14-small)
##### Features
- **plugin-vue:** add forced vapor mode option
([#​766](https://redirect.github.com/vitejs/vite-plugin-vue/issues/766))
([d59c3c0](https://redirect.github.com/vitejs/vite-plugin-vue/commit/d59c3c04b0c5cd920bab5f0f6000bedfef901e1c))
##### Bug Fixes
- **deps:** update all non-major dependencies
([#​781](https://redirect.github.com/vitejs/vite-plugin-vue/issues/781))
([d369e5a](https://redirect.github.com/vitejs/vite-plugin-vue/commit/d369e5a57592ed525c4da5ef0458cb89a6977ad2))
</details>
<details>
<summary>vitest-dev/vitest (@​vitest/coverage-v8)</summary>
###
[`v4.1.10`](https://redirect.github.com/vitest-dev/vitest/releases/tag/v4.1.10)
[Compare
Source](https://redirect.github.com/vitest-dev/vitest/compare/v4.1.9...v4.1.10)
##### Β Β Β π Bug Fixes
- **browser**: Check fs access in builtin commands \[backport to v4] Β -Β
by [@​hi-ogawa](https://redirect.github.com/hi-ogawa), **Hiroshi
Ogawa** and **OpenCode (claude-opus-4-8)** in
[#​10680](https://redirect.github.com/vitest-dev/vitest/issues/10680)
[<samp>(5c18d)</samp>](https://redirect.github.com/vitest-dev/vitest/commit/5c18dd267)
- **vm**: Fix external module resolve error with deps optimizer query
for encoded URI \[backport to v4] Β -Β by
[@​SveLil](https://redirect.github.com/SveLil) and
[@​hi-ogawa](https://redirect.github.com/hi-ogawa) in
[#​10661](https://redirect.github.com/vitest-dev/vitest/issues/10661)
[<samp>(bae52)</samp>](https://redirect.github.com/vitest-dev/vitest/commit/bae52b511)
##### Β Β Β Β [View changes on
GitHub](https://redirect.github.com/vitest-dev/vitest/compare/v4.1.9...v4.1.10)
</details>
<details>
<summary>webpro-nl/knip (knip)</summary>
###
[`v6.27.0`](https://redirect.github.com/webpro-nl/knip/releases/tag/knip%406.27.0):
Release 6.27.0
[Compare
Source](https://redirect.github.com/webpro-nl/knip/compare/knip@6.26.0...knip@6.27.0)
- Add Temporal plugin
([`98191a0`](https://redirect.github.com/webpro-nl/knip/commit/98191a04aad0572bead191b101bfecef75067b0c))
- Handle -d alias for react-email dir argument
([`53d1fec`](https://redirect.github.com/webpro-nl/knip/commit/53d1fec3213db8d6a84dfa7a82050b122782e065))
- Expand pnpm script references with forwarded arguments
([`33e9799`](https://redirect.github.com/webpro-nl/knip/commit/33e9799df39450033061b9c4c07daf22f5d15721))
- Treat path-shaped script tokens as file references, extend known
global binaries
([`d9508c2`](https://redirect.github.com/webpro-nl/knip/commit/d9508c282ab81351beda4b2f859435b49f142c51))
- Mark tsdown neverBundle dependencies as referenced
([`5fd863b`](https://redirect.github.com/webpro-nl/knip/commit/5fd863ba12e68842f91ab1a94e7e30d1e571da92))
- Support TanStack Start in tanstack-router plugin
([`bd63cd8`](https://redirect.github.com/webpro-nl/knip/commit/bd63cd8af88dac03a98aa17f1add322f7bdf4a94))
- Mark vite ssr.external dependencies as referenced
([`91de938`](https://redirect.github.com/webpro-nl/knip/commit/91de938032062cb667bced0eadef9e67d1e20b1f))
- Expand bun, npm, and yarn script references with forwarded arguments
([`b5a3176`](https://redirect.github.com/webpro-nl/knip/commit/b5a31767a83e2cec3fb20c3b146d94c0412a4fd6))
- Add Latitude as sponsor
([`4267675`](https://redirect.github.com/webpro-nl/knip/commit/42676759cad5fb10ac96fd7c0457037e906f4b68))
- Regenerate plugin list in docs
([`1d2cdff`](https://redirect.github.com/webpro-nl/knip/commit/1d2cdffb4d4151f848c44e066eff43e9efbf44fe))
- Support Vue 2 template auto-imports (close
[#​1881](https://redirect.github.com/webpro-nl/knip/issues/1881))
([`2157f39`](https://redirect.github.com/webpro-nl/knip/commit/2157f3913f77216d268b2255899b6fc58609f9bb))
- Ignore block-commented stylesheet imports (resolve
[#​1880](https://redirect.github.com/webpro-nl/knip/issues/1880),
close
[#​1882](https://redirect.github.com/webpro-nl/knip/issues/1882))
([`a627f53`](https://redirect.github.com/webpro-nl/knip/commit/a627f53221547d5397d70722fcc2fd723889e579))
- Support TypeScript 5.0 narrowing for ssr.external
([`989292f`](https://redirect.github.com/webpro-nl/knip/commit/989292f79b9d19028bfc7ba988b74ba691041a45))
- Resolve local binaries from bun run
([`5e9a7f0`](https://redirect.github.com/webpro-nl/knip/commit/5e9a7f0286e2c2175bb299c514cc7b67a7cf24f1))
- Support Vitest inline project config extensions
([`f682327`](https://redirect.github.com/webpro-nl/knip/commit/f682327f6181f44e3924715efa7404aa39e532f0))
- Resolve package names from aliased node\_modules paths
([`c273d5f`](https://redirect.github.com/webpro-nl/knip/commit/c273d5fbc1b0d3eaf27b38c3f31d3a2a4b6b184a))
- Support Tailwind CSS URL imports
([`57a4770`](https://redirect.github.com/webpro-nl/knip/commit/57a4770447cc4c8f9ea4ffb9171c0c92eed4f255))
- Resolve unbuilt workspace package exports (close
[#​1877](https://redirect.github.com/webpro-nl/knip/issues/1877))
([`1b0a2c0`](https://redirect.github.com/webpro-nl/knip/commit/1b0a2c0ee784db165c7f92fe10397a6cb513c1cf))
- Skip unresolved imports of gitignored generated files
([`605c904`](https://redirect.github.com/webpro-nl/knip/commit/605c9049f73838beb1c6fee83c6a5421ae1a6d07))
- Attribute imports of aliased packages to the declared dependency
([`d05e775`](https://redirect.github.com/webpro-nl/knip/commit/d05e775c9a3a5676dc9eadcc1093f7d9983efd06))
- Flag unresolvable imports of transitive peer deps as unlisted (resolve
[#​1883](https://redirect.github.com/webpro-nl/knip/issues/1883))
([`18f0bff`](https://redirect.github.com/webpro-nl/knip/commit/18f0bff4ea683837f75ca744d3fbde52df49b297))
- Don't count ambient module declarations in script files as imports
([`e52aecf`](https://redirect.github.com/webpro-nl/knip/commit/e52aecfd7c022cbe72b1c11dde3f4968579ac409))
- Update rolldown snapshot
([`a63e927`](https://redirect.github.com/webpro-nl/knip/commit/a63e92788239062682603a5727ede371db7845c8))
- Remove obsolete workarounds from ecosystem tests
([`a521cf7`](https://redirect.github.com/webpro-nl/knip/commit/a521cf790d726c18e644e2e316cdc09eb654f607))
- Handle snapshotSerializers in Vitest plugin (resolve
[#​1884](https://redirect.github.com/webpro-nl/knip/issues/1884))
([`b597de9`](https://redirect.github.com/webpro-nl/knip/commit/b597de951f6f0e897afdbbbb8a7d0c06c8d2437d))
- Set the stage
([`c059c2b`](https://redirect.github.com/webpro-nl/knip/commit/c059c2be5803609924b852c2bf0cd90906a6a831))
###
[`v6.26.0`](https://redirect.github.com/webpro-nl/knip/releases/tag/knip%406.26.0):
Release 6.26.0
[Compare
Source](https://redirect.github.com/webpro-nl/knip/compare/knip@6.25.0...knip@6.26.0)
- ci: add path filters
([#​1871](https://redirect.github.com/webpro-nl/knip/issues/1871))
([`4249935`](https://redirect.github.com/webpro-nl/knip/commit/4249935adffe1b8eca9570fb325fa19cc8010584))
- thanks
[@​trueberryless](https://redirect.github.com/trueberryless)!
- Add CodeRabbit as gold sponsor
([`1da09fd`](https://redirect.github.com/webpro-nl/knip/commit/1da09fdc8f4d851f3cad30659200018bfb47a5e0))
- Fix up docs a bit more
([`39125a7`](https://redirect.github.com/webpro-nl/knip/commit/39125a7f473e006f61629781bff1ac4a050ca460))
- Don't report ambient declaration files as unused
([`aed361c`](https://redirect.github.com/webpro-nl/knip/commit/aed361c00a82829c2fc80a65a2f7af174316782e))
- Register oclif command files as entries
([`3b4d58c`](https://redirect.github.com/webpro-nl/knip/commit/3b4d58c9da83d93b6821a2b24f57b919ba886756))
- Add electron-vite plugin
([`d92107e`](https://redirect.github.com/webpro-nl/knip/commit/d92107ea504833b3ee7ad2b695e22d40528b24c3))
- Add esbuild plugin
([`ef3b601`](https://redirect.github.com/webpro-nl/knip/commit/ef3b601d957546554de2cb1fbff859f5bdafb741))
- Ignore more globally available binaries
([`8292981`](https://redirect.github.com/webpro-nl/knip/commit/829298129fdfd1b41a1f8dff5022b9fbbe25f454))
- Resolve `#`-imports to source when node condition is unbuilt (resolve
[#​1873](https://redirect.github.com/webpro-nl/knip/issues/1873))
([`f2713ed`](https://redirect.github.com/webpro-nl/knip/commit/f2713ed1e499ab286b86615d4b8ec34f711d1bd2))
- Ignore gh as a globally available binary
([`a6f0772`](https://redirect.github.com/webpro-nl/knip/commit/a6f0772e5db03a8412a3ac036756779f22d1a27d))
- Resolve Vitest benchmark files as entries
([`5742913`](https://redirect.github.com/webpro-nl/knip/commit/57429139f7e93f2a4bee35a53d450c4f45dea9ff))
- Extract shared Vue auto-import machinery into plugins/\_vue
([`7301075`](https://redirect.github.com/webpro-nl/knip/commit/73010753d432ae58eb12c7eec9c5723036009749))
- Scope compiler extensions per workspace
([`5e6f82b`](https://redirect.github.com/webpro-nl/knip/commit/5e6f82b963a83e5801484efbc01a4b6a9ce39ea3))
- Resolve auto-imported components in the Vue SFC compiler
([`009aad8`](https://redirect.github.com/webpro-nl/knip/commit/009aad8f9dab589898e5dba4c85fa41ea57c1a49))
- Add plugins for the Vue auto-import ecosystem
([`f638c83`](https://redirect.github.com/webpro-nl/knip/commit/f638c8302cdd26a63e477344a7cf44ef60af7788))
- Enable Vue SFC compiler on unplugin-vue and
[@​vitejs/plugin-vue](https://redirect.github.com/vitejs/plugin-vue)
([`9396ab1`](https://redirect.github.com/webpro-nl/knip/commit/9396ab159919147ef81dad16e365e531b32b4b12))
- Recognize vite-plugin-vue-meta-layouts (same layouts convention)
([`3ceee89`](https://redirect.github.com/webpro-nl/knip/commit/3ceee89228b0173eddc0466bf50e8181286ffebc))
- Add vite-plugin-pages and unplugin-icons plugins
([`9bc1754`](https://redirect.github.com/webpro-nl/knip/commit/9bc17540bcf8eb142474ed84837c1e2cebb7fe73))
- Add vite-plugin-pwa and
[@​intlify/unplugin-vue-i18n](https://redirect.github.com/intlify/unplugin-vue-i18n)
plugins
([`45dea0a`](https://redirect.github.com/webpro-nl/knip/commit/45dea0a397ed754c34617ed18deef4dc6f0ec0d4))
- Dog, food.
([`e1249ad`](https://redirect.github.com/webpro-nl/knip/commit/e1249ade2558d3801d85e2dc63349d884f3443d4))
- Update query snapshot
([`a45941d`](https://redirect.github.com/webpro-nl/knip/commit/a45941d2db381c04f8a38416533217ee84f77ca7))
- Don't misread Nitro route types as Vue component auto-imports
([`43aecd5`](https://redirect.github.com/webpro-nl/knip/commit/43aecd5414eb63bec3637f37ffac0c62a71e7e74))
- Read oxlint jsPlugins from vite-plus vite.config and .oxlintrc.json
([`589ffda`](https://redirect.github.com/webpro-nl/knip/commit/589ffda4e3a7d0c0d772191ad6f8d6e60dd298cf))
- Add vite-plus plugin for run.tasks and staged scripts
([`f041c19`](https://redirect.github.com/webpro-nl/knip/commit/f041c191e4e10dd181ad212b71079e3e10bcf341))
- Support
[@​vite-pwa/nuxt](https://redirect.github.com/vite-pwa/nuxt) PWA
config in nuxt.config
([`8fa7b11`](https://redirect.github.com/webpro-nl/knip/commit/8fa7b116690e37484798ad98eca002d26c0c80fe))
- Add
[@​vite-pwa/assets-generator](https://redirect.github.com/vite-pwa/assets-generator)
plugin
([`4254f7d`](https://redirect.github.com/webpro-nl/knip/commit/4254f7d53a284a4ea4612f650ae03495bcb0d582))
- Add [@​nuxtjs/i18n](https://redirect.github.com/nuxtjs/i18n)
plugin
([`dfb9acb`](https://redirect.github.com/webpro-nl/knip/commit/dfb9acbe5a08ebc85562f59a04e7f4bedb4925f1))
- Read plugin entries from vite.config options and index.html
([`d533da8`](https://redirect.github.com/webpro-nl/knip/commit/d533da8cc2b431d206eb7c55323ea51846eca0bb))
- Fix false positives in VitePress, next-mdx and unplugin-vue-i18n
plugins
([`b99702a`](https://redirect.github.com/webpro-nl/knip/commit/b99702a76e26b1943a16517f1633de3a0713c196))
- Respect optional peers in pnpm and Yarn packageExtensions
([`aab080b`](https://redirect.github.com/webpro-nl/knip/commit/aab080bbd436494316df045011ab22cbebca06b4))
- Detect babel plugins in the Storybook config
([`5dea975`](https://redirect.github.com/webpro-nl/knip/commit/5dea975adf9b88971ad56434132282a099c7d58c))
- Add shared AST helpers for imported calls and first property values
([`c84bb7a`](https://redirect.github.com/webpro-nl/knip/commit/c84bb7a87a17f1921c12bb02bcfd43f905cae595))
- Inline trivial resolveFromAST wrappers and normalize orval and sst
([`620079d`](https://redirect.github.com/webpro-nl/knip/commit/620079d1d07b69a69bbb67b6a6e68963b6c2fc21))
- Extract inline resolveFromAST implementations to dedicated files
([`b5231c1`](https://redirect.github.com/webpro-nl/knip/commit/b5231c12f915428067d790187d2dd4c44e2559d4))
- Normalize resolveFromAST files to a uniform contract
([`fc1ba0f`](https://redirect.github.coβ¦
The switcher read as buried under the logo, title, and tagline. Since the tagline directly above it is English, a non-English reader hit a sentence they may not read before finding their own language. It is now the first thing rendered.
Kept inside
<div align="center">rather than above it, so it stays centered instead of rendering left-aligned over a centered logo.Applied to all seven READMEs (
README.md+ 6 translations) so the copies do not drift β the Spanish one needed a different matcher since Spanish is bold rather than a link there.Pure move: 2 lines changed per file, no content edits. Full pre-push gate green (275s),
release-docs-identity11/11.Changelog
release-docs-identity(11/11).