mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-11-13 12:24:46 +00:00
23 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0c637aa10a
|
🧪 test: Spec the First-Sentence Diagnostic Comparison in the Suppression Runner (#17165)
Some checks are pending
Backend Unit Tests / Build packages (push) Waiting to run
Backend Unit Tests / Codegraph select (push) Waiting to run
Backend Unit Tests / TypeScript type checks (push) Blocked by required conditions
Backend Unit Tests / Circular dependency checks (push) Waiting to run
Backend Unit Tests / Tests: api (shard 1/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 2/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 3/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: data-provider (push) Blocked by required conditions
Backend Unit Tests / Tests: data-schemas (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 1/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 2/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 3/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 4/4) (push) Blocked by required conditions
Codegraph E2E Votes / vote (full suite) (push) Waiting to run
Docker Dev Branch Images Build / publish (push) Waiting to run
Frontend Unit Tests / Client build recovery regression (push) Waiting to run
Frontend Unit Tests / Codegraph select (push) Waiting to run
Frontend Unit Tests / Build packages (push) Waiting to run
Frontend Unit Tests / TypeScript type checks (client) (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/chat (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/client (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 1/2) (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 2/2) (push) Blocked by required conditions
Frontend Unit Tests / Vite build verification (push) Blocked by required conditions
* test: Spec the First-Sentence Diagnostic Comparison in the Suppression Runner * test: Copy the Diagnostics Module into the Suppression Spec Synthetic Roots * test: Copy the Diagnostic Comparison Spec into the Suppression Synthetic Roots |
||
|
|
1e23b3545b
|
📝 refactor: Move Text-Like Message Parts Into @librechat/chat (#17087)
Some checks failed
Backend Unit Tests / Build packages (push) Waiting to run
Backend Unit Tests / Codegraph select (push) Waiting to run
Backend Unit Tests / TypeScript type checks (push) Blocked by required conditions
Backend Unit Tests / Circular dependency checks (push) Waiting to run
Backend Unit Tests / Tests: api (shard 1/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 2/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 3/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: data-provider (push) Blocked by required conditions
Backend Unit Tests / Tests: data-schemas (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 1/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 2/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 3/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 4/4) (push) Blocked by required conditions
Codegraph E2E Votes / vote (full suite) (push) Waiting to run
Docker Dev Branch Images Build / publish (push) Waiting to run
Frontend Unit Tests / Client build recovery regression (push) Waiting to run
Frontend Unit Tests / Codegraph select (push) Waiting to run
Frontend Unit Tests / Build packages (push) Waiting to run
Frontend Unit Tests / TypeScript type checks (client) (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/chat (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/client (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 1/2) (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 2/2) (push) Blocked by required conditions
Frontend Unit Tests / Vite build verification (push) Blocked by required conditions
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Main Promotion Policy Tests / Main promotion denial and fast-forward tests (push) Has been cancelled
* refactor: Move text-like message parts into @librechat/chat Text, EmptyText, CollapsibleText, Summary and AgentUpdate move into the package's components entry with their unit specs and read the app only through MessagePartsHost, which gains renderMarkdown, useSmoothStreaming, useAgent and renderAgentIcon. The app's markdown and agent-icon renderers register from the Parts barrel, so the host module keeps its import graph. The design-suppression runner compares a moved file's diagnostics by their first sentence: the remedy that follows names app-only compositions for client paths, so the same violation read as new after a move. * test: Cover text-like parts rendered from @librechat/chat in the browser |
||
|
|
a30b11b4c7
|
🧩 refactor: Route Message Parts Through a @librechat/chat Part Dispatcher and Host (#16976) | ||
|
|
e1b4e09d7c
|
🥟 ci: Type-Check Workspaces with bun check (#17009)
Some checks failed
Backend Unit Tests / Build packages (push) Waiting to run
Backend Unit Tests / Codegraph select (push) Waiting to run
Backend Unit Tests / TypeScript type checks (push) Blocked by required conditions
Backend Unit Tests / Circular dependency checks (push) Waiting to run
Backend Unit Tests / Tests: api (shard 1/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 2/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 3/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: data-provider (push) Blocked by required conditions
Backend Unit Tests / Tests: data-schemas (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 1/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 2/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 3/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 4/4) (push) Blocked by required conditions
Codegraph E2E Votes / vote (full suite) (push) Waiting to run
Docker Dev Branch Images Build / publish (push) Waiting to run
Frontend Unit Tests / Client build recovery regression (push) Waiting to run
Frontend Unit Tests / Codegraph select (push) Waiting to run
Frontend Unit Tests / Build packages (push) Waiting to run
Frontend Unit Tests / TypeScript type checks (client) (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/chat (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/client (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 1/2) (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 2/2) (push) Blocked by required conditions
Frontend Unit Tests / Vite build verification (push) Blocked by required conditions
Main Promotion Policy Tests / Main promotion denial and fast-forward tests (push) Has been cancelled
* ci: Type-Check Workspaces with bun check Make every workspace tsconfig valid under TypeScript 7 semantics while staying clean on the installed tsc 5.9: explicit types arrays (TS 7 no longer auto-includes node_modules/@types), es2017 target for data-provider, rootDir for api specs that import client contracts, and noUncheckedSideEffectImports off for the CSS and matchMedia mock imports. Fix the four client type errors newer DOM and Intl libs expose, and drop the fetch.preconnect copy in useRum that only existed to satisfy the leaked @types/bun globals. Switch the client typecheck script and the CI type-check steps to bun check (Bun 1.4.3); tsc --noEmit stays available as typecheck:tsc. * chore: Fall Back to tsc When bun check Is Unavailable Add scripts/typecheck.mts, which runs bun check when the installed Bun provides it and the repository's tsc otherwise, so contributors without Bun can still type-check. The client typecheck script and the static-checks TypeScript gate both resolve their checker through it, keeping local runs on the same checker CI uses whenever Bun is present. * ci: Type-Check the Chat Package Through the Shared Checker * ci: Run the Type Check When Only the Shared Checker Changes A change to scripts/typecheck.mts alone started neither frontend review nor the static-checks TypeScript gate, so a broken resolver could merge unexercised. Add it to the frontend-review triggers and to every typecheck project's paths. The suppression-baseline scenarios copy static-checks.mts into synthetic roots; copy its new typecheck.mts import alongside i18n.mts so the runner loads there. Add scenarios for the checker choice and its fallback. |
||
|
|
8e60a4185a
|
🧱 refactor: Judge Chat Package Imports on the Resolved Module (#16975)
Some checks are pending
Backend Unit Tests / Build packages (push) Waiting to run
Backend Unit Tests / Codegraph select (push) Waiting to run
Backend Unit Tests / TypeScript type checks (push) Blocked by required conditions
Backend Unit Tests / Circular dependency checks (push) Waiting to run
Backend Unit Tests / Tests: api (shard 1/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 2/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: api (shard 3/3) (push) Blocked by required conditions
Backend Unit Tests / Tests: data-provider (push) Blocked by required conditions
Backend Unit Tests / Tests: data-schemas (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 1/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 2/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 3/4) (push) Blocked by required conditions
Backend Unit Tests / Tests: @librechat/api (shard 4/4) (push) Blocked by required conditions
Codegraph E2E Votes / vote (full suite) (push) Waiting to run
Docker Dev Branch Images Build / publish (push) Waiting to run
Frontend Unit Tests / Client build recovery regression (push) Waiting to run
Frontend Unit Tests / Codegraph select (push) Waiting to run
Frontend Unit Tests / Build packages (push) Waiting to run
Frontend Unit Tests / TypeScript type checks (client) (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/chat (push) Blocked by required conditions
Frontend Unit Tests / Tests: @librechat/client (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 1/2) (push) Blocked by required conditions
Frontend Unit Tests / Tests: Ubuntu (shard 2/2) (push) Blocked by required conditions
Frontend Unit Tests / Vite build verification (push) Blocked by required conditions
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Main Promotion Policy Tests / Main promotion denial and fast-forward tests (push) Waiting to run
* refactor: Judge chat imports on the resolved target with one boundary rule scripts/eslint/chat.mjs replaces the import patterns and load selectors: every specifier is normalized (extension, trailing slash, /index, package name) before it is judged, across static imports, re-exports, import(), require(), import-equals and import types, so any spelling of the /react or /components entry or a UI package is rejected from core. A RuleTester table and a table-driven scenario cover the spellings. * fix: Anchor the chat boundary root to the config directory * fix: Keep bare app paths and bare core entries behind the chat boundary * fix: Treat aliased react paths in core as the /react entry |
||
|
|
c00c776e0e
|
📦 feat: Scaffold the @librechat/chat Package (#16964)
* feat: Scaffold the @librechat/chat package Adds packages/chat with core, react and components entries, a tsdown build, a node-environment jest config, turbo and root build wiring, import boundary rules, design-lint scope, and core types for the chat transport, stream, host and status. No code moves yet. * test: Cover the @librechat/chat boundaries as scenarios * fix: Run the chat suite in test:turbo and build it for the mock lane test:turbo now selects @librechat/chat, the mock e2e job builds the package the chat-package scenarios load, and the synthetic repository in the suppression scenarios carries the packages/chat/src root lint:design:record names. * chore: Build the chat package in frontend:ci too * chore: Run the chat suite in test:all * fix: Hold core TSX to the chat boundary and scan the package for cycles * fix: Apply the chat import boundary to JavaScript sources * fix: Keep nested core files off the components entry * fix: Close the remaining chat package gates and fence aborts by generation Blocks imports through the @librechat/frontend workspace name, adds the chat sources to the i18n scan, the CSS color gate and the client Tailwind content, and lets ChatAbortRequest carry the generation it means to stop. * fix: Keep JSX out of the chat core and carry abort and reconnect outcomes Rejects JSX under the core, copies the chat manifest before the Docker install, keys the client app build caches on the chat sources, fences reconnects by generation, and types the abort result the transport returns. * test: Give the CSS gate's scratch tree the chat root it now scans * fix: Close the remaining @librechat/chat gates and wiring Rejects import() and require() across the package boundaries, holds chat components to the literal-copy rule, compiles TSX in the package's jest transform, builds the package in Frontend Unit Tests, requires its test job for main promotion, adds it to the unused-dependency scan, and lists it wherever a sibling workspace is listed: Dockerfile.multi, the docker smoke paths, the remaining node_modules caches, the reinstall helper, the bun build chain and AGENTS.md. * fix: Tighten the chat boundaries and carry steering and Assistants aborts Template-literal import() and require() and self-imports through the package name are rejected, user-facing JSX attributes need localized text, the cycle scan resolves self-imports, Dockerfile.multi builds the package for the client stage, and the transport carries steer, cancelSteer, armSteer and the Assistants abort key. * feat: Match the chat transport and host contracts to the client's The transport now reports settled and replaced starts, the stream's protocol version and resumed flag, reconnect replay, generation status, paused-run resume with approvals or answers, abort and steer protocol versions, and the request error shape. The host config and auth carry what the client's ChatSettings now supplies: rename-while-running, model specs, retention mode and the user. * fix: Keep lazy React-entry loads out of the core and type the Assistants stop Relative import() and require() of the /react entry from core are rejected, and an abortKey stop resolves to the terminal conversation and run messages the Assistants route returns. * fix: Fence start controls by protocol and close extension-bearing entry loads Settled and replaced start results carry the protocol version, core loads of the /react or /components entry with a file extension are rejected, and test:packages:chat builds data-provider first. |
||
|
|
adb04422bf
|
🚦 feat: Lint Disabled Dims That Compose No Disabled Recipe (#16907)
* feat: Lint Disabled Dims That Compose No Disabled Recipe * fix: Catch Bracketed Disabled Variants and Read Recipes From References * fix: Skip Enabled-State Branches and False-Valued Disabled States * fix: Read Full-Opacity Recipes, Primitive Recipes and Compound Conditions * fix: Keep the Suppression Error Wording the Baseline Spec Reads * test: Add a lint scenario for the disabled-recipe rule * fix: Leave negated disabled selectors and conditions alone in the disabled-recipe rule * feat: Judge disabled recipes per branch, by import and by the element they restore * fix: Trust recipes only from the theme module and only through defaulted cva variants * fix: Resolve namespaced recipes through their import * fix: Read cva disabled variants, guarded recipes and trailing important in the disabled-recipe rule * fix: Read variant dims a disabled condition picks and recipes behind the same ternary * fix: Follow nested disabled guards and real cva defaults in the disabled-recipe rule * fix: Trust recipes only through class helpers and string concatenation * fix: Read cva compound variants only under compoundVariants and guard key lookups * fix: Require the recipe that can win for the faded element in the disabled-recipe rule * fix: Judge stacked disabled variants by their most distant element * fix: Match recipe markers, read || polarity, compound arrays, constant class strings and helper bindings * fix: Accept spelled recipes, default-imported primitives, inverted props and disabled-guarded recipes * fix: Check every chosen dim, trusted helper sources, data-disabled, recipe markers and guard identity * chore: Re-record the disabled-recipe backlog after rebasing onto dev |
||
|
|
6c8f659d54
|
🧮 fix: Read Math Function Channels in CSS Custom Property Triplets (#16924)
* fix: Read Math Function Channels in CSS Custom Property Triplets * fix: Collapse Nested Numeric Math Before the Triplet Check * fix: Read Exponents and Math Constants in Triplet Channels * fix: Read round() Strategies in Triplet Math Channels |
||
|
|
945a885394
|
🎚️ feat: Promote Stylesheet Colour Roles to Registry Colours (#16864) | ||
|
|
3ca084a6cf
|
🎨 refactor: Route Stylesheet Colours Through Theme Variables (#16823)
* refactor: Route Stylesheet Colours Through Theme Variables and Gate CSS Literals * fix: Run the Colour Check Through Its Own Script * fix: Read Numeric Fallbacks Inside Colour Function var() in the Colour Literal Check |
||
|
|
ad99250a35 | fix: Verify Sidebar Status and Project Name Loading | ||
|
|
abab0d3c6d
|
🪟 fix: Read the Static Checks Workflow With CRLF Line Endings (#16669)
workflowSelectsWhatItFilters() split .github/workflows/static-checks.yml with $-anchored multiline patterns, so on a Windows checkout with core.autocrlf the trailing \r kept every pattern from matching and the Design-rule suppressions gate failed every commit. Parse through workflowFilters(), which normalizes line endings first, and check in-script that the CRLF rendering reads the same lists, so CI on an LF checkout catches a parser that only reads LF. |
||
|
|
5961f767fd |
🔤 fix: Ship the Theme Font Faces With the Package (#16485)
* fix: ship the Inter, Roboto Mono and Inconsolata faces with the package
Move the woff2 files from client/public/fonts into packages/client/src/theme/fonts
and every @font-face rule (the app's Inter and Roboto Mono faces and
client/src/fonts.css) into theme/fonts.css, which tokens.css imports and the build
copies into dist with the files. Each url() names the file by a new
@librechat/client/fonts/* export: a relative path breaks under Vite with
@tailwindcss/postcss, because Vite inlines the import before Tailwind runs and
Tailwind reports every emitted declaration as coming from the root stylesheet.
The app loads the same files through the same import and still emits them to
assets/fonts with their licences, now read from the package; the $fonts alias is
gone. Inter and Roboto Mono gain their OFL texts beside Inconsolata's. The README's
consumer stylesheet moves the package stylesheet import above @config, where
Vite's inliner no longer skips it. The static checks' bundle freshness test reads a
subpath pattern export as the directory it maps into.
* docs: describe how the theme fonts resolve through the package export
* fix: leave subpath pattern exports out of the bundle freshness check
A pattern such as ./dist/fonts/* names no entry the design rules load, and
reading it as its directory made the freshness check depend on a directory the
synthetic build in suppression-baseline.spec.ts never dates. The scenario's
mirror of the manifest reading skips patterns the same way.
(cherry picked from commit
|
||
|
|
339a323ff1 |
🎨 chore: Enforce Design-System Rules With @shadcn/lint (#15981)
* 🎨 chore: Enforce Design-System Rules With @shadcn/lint
Registers @shadcn/lint for client/src and packages/client/src, resolving\n@librechat/client as the design system so a className that overrides what a\nprimitive owns is reported with the variants and sizes to use instead.\n\nFive rules run at error. no-unknown-classes stays off: it needs Tailwind v4.\nContracts record what callers legitimately own (text typography, skeleton\nsilhouette); eslint-suppressions.json records the 3,141 violations the tree\nalready carried, so the rules gate new code without a tree-wide migration.\n
* 🩹 fix: Close the Gaps Review Found in the Design-Rule Gate
Five findings against this pull request, and one that killed its CI:
- The lockfile entries this change added resolved to a private mirror, so every
job died in `npm ci`. They now resolve from registry.npmjs.org, with each
integrity hash checked against the tarball npmjs.org serves.
- The pre-commit hook ran ESLint twice and the first run had no
`--pass-on-unpruned-suppressions`, so it exited 2 on the suppression the fix
had just made unused and lint-staged never reached the command that tolerates
it. One invocation now does both; ESLint applies suppressions after fixes, so
it reports the post-fix state.
- `scripts/static-checks.mts` omitted that same flag, so `npm run static-checks`
failed locally on a diff the Static Checks lane passes.
- `lint:design:suppress` recorded a moved file's new path and left the old one
behind, because only `--prune-suppressions` removes it and ESLint refuses both
flags in one run. The script now chains the prune, and CLAUDE.md documents the
scoped recipe that does not re-baseline the whole surface.
- The design rules matched only `.ts`/`.tsx`, so the client's `.jsx` entry points
bypassed all five. Both globs now cover `.js`/`.jsx`; the one violation that
exposed — an inline `display: none` on App.jsx's silent-audio iframe — is fixed
at the source rather than recorded.
- A suppressions-only diff started no workflow and nothing read the baseline. It
is now a trigger path and its own filter group, and a new `suppressions` check
rejects a shape ESLint cannot load, a count no run can reach, a rule the plugin
does not define, and a path that no longer exists.
The count-neutral swap two reviewers raised is not closed: ESLint's baseline
format records a count, not violation identities, so replacing one suppressed
violation with another of the same rule in the same file is invisible here.
CLAUDE.md now says that instead of implying detection.
Four `@scenario`-tagged specs cover the developer-facing behaviour: the report a
caller gets on a primitive override, a `.jsx` file being checked, the fixing
commit staying green (with the counter-proof that it exits 2 without the flag),
the move recipe dropping the old path, and every locked tarball being fetchable
from the public registry.
* 🧪 fix: Close the Dormant-Entry and Gate-Trigger Gaps
The suppressions gate did not run when the file that implements it changed, a
count recorded against a path no design-rule lint reaches passed validation, the
documented re-record chain could not run under cmd.exe, and the lockfile
scenario failed a legitimate SheetJS upgrade. The trigger scenarios also ran
four full-tree sweeps inside one test that declared a 180s deadline for work
measured at 100-280s per sweep.
- scripts/static-checks.mts: `scripts/static-checks.mts` joins the suppressions
filter and the whole-record trigger, and a recorded path ESLint never reports
on is rejected as dormant.
- package.json/CLAUDE.md: `|| node -e ""` in place of `|| true`, which cmd.exe
does not define.
- e2e: the whole-record triggers are a matrix over a miniature repository (2-6s
each instead of 100-280s), the oversized scenario is split into five
behaviour-shaped ones, scratch baselines live in os.tmpdir(), and the lint
gate runs in one project because it has no viewport.
* 📏 chore: Re-Record the Counts Dev Moved Under the Backlog
Rebasing onto dev brought #16067, which restyled three files the recorded
backlog speaks for: MarketplaceSidebar.tsx gained one arbitrary value and two
`no-restyle` violations, ToolsMarketplaceDialog.tsx gained one, and SkillsDialog
lost one. Counts above the file fail the lint for whoever edits it next; the one
below leaves capacity a later violation would spend. Scoped re-record and prune
of those three paths, per the recipe in CLAUDE.md.
* 🧭 test: Run the Copied Runner From the Tree It Is Testing
The trigger matrix pointed the copied static-checks runner at a miniature
repository but invoked it from the real one. The runner derives its repository
from its own location and relativizes file arguments against the working
directory, so `eslint.config.mjs` resolved to a path outside the miniature tree,
the suppressions group read as "not affected by this diff", and the scenario
passed a check that never ran (verify recorded it as a failure: exit 0 where a
rejection was expected). `run` now takes a `cwd`, and both synthetic-root probes
use it.
* 🔇 fix: Reject a Disable Comment With Nothing Behind It Yet
Comparing the two ESLint runs finds a directive only while it is silencing
something. A `/* eslint-disable shadcn/no-raw-colors */` over a file with no raw
colour left both runs agreeing, so it landed unseen and then silenced the first
violation anyone added to that file. The configured run now also asks for unused
directives, which is how ESLint names a disable with nothing behind it: a
dormant design-rule disable and a dormant blanket disable are rejected, a
dormant directive for another rule is not. Neither root carries one today (0
reports across 2,241 files), so what this rejects is new ones.
The same pass also has to see the primitives' variants, so `validateSuppressions`
builds `packages/client/dist` before the directive check rather than inside the
capacity scan that follows it; without the metadata both runs report strictly
fewer design diagnostics and a comment silencing a `no-restyle` violation is
invisible to the comparison.
* 🧱 fix: Count the Library's Build Inputs as Sources
`designMetadataIsFresh` compared `packages/client/dist` with `packages/client/src`
alone, so a commit that changed only the library's manifest or its tsdown config
left every source older than the build and the runner skipped the rebuild. The
design rules then classified against output emitted under the old contract —
locally only, since CI always builds before validating, which is the shape of
divergence nobody sees until the lane disagrees with the commit that passed.
Both files already count as design metadata everywhere else (the suppressions
filter, the whole-record trigger); they now count in the freshness comparison
too. The scenario runs the miniature repository with an observable
`build:client-package`: a current build is not rebuilt, and touching either
input asks for one.
* ⏱️ test: Set the Times the Freshness Rule Compares
The rebuild scenario wrote the build output and then the manifest, and node
wrote both inside the same millisecond, so the manifest was not newer than the
build and the runner correctly declined to rebuild — the assertion failed on the
filesystem clock rather than on the rule. The scenario now sets each mtime it
depends on with `utimesSync`, which is what it meant to say.
* 🩹 fix: Restore the Write the Import Edit Dropped
Adding \ to the import list replaced \ instead of joining it, and nothing local caught it: no tsconfig covers e2e/, and the flat config leaves \ to TypeScript there. The mock harness did — every scenario in the file failed with ReferenceError — which is the point of running it. Typechecked the four scenario files directly this time.
* 🚫 fix: Reject Every Inline Escape, Not Only the Ones in Use
Codex found three more ways past the gate, one shape each. A blanket
\ busy silencing an unrelated rule is used, so ESLint's
unused-directive report never names it, and the configured-versus-ignored
comparison sees no design delta — it waits for the file's first design
violation and takes it. Inline \ over a
clean file is invisible the same way. And a nested baseline that drops an entry
for an unchanged source was checked against its remaining keys only, so the
violation it used to speak for went unread.
The directive check now has two halves: ESLint still answers what a comment is
silencing today, and the comments themselves are read out of the parser — not
the text, so a string that reads like a directive is not one — for a disable
naming a design rule, a blanket disable, or inline configuration naming a
design rule, whether or not anything is behind it yet. That supersedes
\, which could only see the unused ones.
Neither design root carries any of the three today.
\ unions the entries a diff removes into a nested baseline's
file list, and \ joins the design metadata: it
carries the \ mapping, the entry emit and the include/exclude that decide
what reaches the bundle the rules read, which is why the repository's own build
caches key on \.
* 🔐 fix: Make the Hook Select What the Lane Selects, and Distrust a Half-Built Bundle
Two more shapes of the same mistake — trusting a proxy for the thing itself.
The pre-commit group listed individual files while the Static Checks lane's
filter also names both design roots, so staging a primitive ran ESLint locally
and the whole-record sweep only in CI: the commit passed and the lane failed on
callers nobody staged. The hook's group is now the lane's filter pattern for
pattern, and the count-edit scenario reads both files and fails when the two
lists differ, so the next path added to one has to reach the other.
Freshness took the newest timestamp under \ as proof of a
finished build. The build empties that directory before it writes, so a build
that failed halfway leaves a recent directory over a bundle with no entry
point, and every later run skipped rebuilding it. The check now also requires
what \ promises inside \ — its entry fields
and every string leaf of \ — to exist.
* 🧾 fix: Give the CDN Exception to xlsx, Not to the Host
The lockfile rule allowed \ for anything and then compared
versions against the base branch, so regenerating the lockfile against that CDN
could move any package off the public registry at the same version and pass.
The exception now belongs to the package — \, at any version, nested or
not — and every other entry has to come from the registry, which drops the
version comparison and the shallow-clone caveat with it.
The rule is a function the scenario applies to the repository's lockfile and to
lockfiles written for the purpose: an xlsx upgrade on its CDN passes, lodash
rewritten onto the same CDN at the same version fails, a new package from a
private mirror fails, and a nested xlsx is still xlsx.
* ↩️ fix: One Local Entry Point, Asked of the Runner
The pre-commit group I added for the backlog and the design metadata was
redundant and slower: \ already ends by running the runner
over the staged diff with \, and that run
selects its groups from the same filters — including both design roots, and
including deletions, which lint-staged never hands to a task. The group made
the whole-record sweep run twice for one commit. It is gone.
The scenario now asserts the property instead of a copy of the list: the hook
still runs the runner over the staged diff, and for every path the lane's
suppressions paths-filter names, the runner reports that check as selected
(\), while a path outside both roots is not affected.
* 🗺️ fix: Assert Where the Rules Are Asked, Not Only What They Found
A report cannot say where the rules were asked. Narrowing the design block over
a subtree that happens to be clean today — \, say —
produces no new diagnostic, no changed count, and no baseline entry to inspect,
so every gate stays green while the styling written there next goes unread.
The new scenario asks the resolved flat configuration instead: for one
representative path of each source extension in every directory under both
roots, all five design rules must be at error in the app and at the library's
own profile inside \ (overriding a primitive is off there;
colour and inline geometry still hold), and server code must carry none of
them. 1,184 paths, answered through ESLint's API in one process in under a
second — \ would have been eleven minutes of subprocesses.
* 🎯 fix: Spend the Budget Once — Reject a Swap Inside the Allowance
The recorded count is a budget per file and rule, and until now it could be
respent: removing one raw colour while adding a different one keeps the total
equal, so ESLint suppresses the new violation inside the old allowance and the
count checks see nothing move. The backlog was documented as unable to tell
that apart, and it was the one hole Codex kept finding its way back to.
What the totals cannot distinguish the diagnostics can. For every design source
a diff touches, the runner now lints that file's version at the base — through
ESLint's API, one process for the whole diff — and reports a design-rule
message the head has that the base did not, naming it. A violation that only
moved keeps its message and is not reported; a file with no version at the base
has no allowance to hide in and the count checks already answer for it.
Measured on the checkout: swapping one `fill="#AFC1FF"` for `fill="#BEEF11"`
in a file recording five raw colours is rejected in 4.9s naming the new one,
and the unchanged file passes. The scenario proves all three states — recorded,
moved, swapped — in a miniature repository with a history of its own, and
CLAUDE.md no longer claims the swap is undetectable.
* 🧰 fix: The Root Manifests Are Build Inputs Too
\ is defined in the root manifest and runs with the
toolchain the root lockfile pins, and both already select the suppressions
check — but neither counted as an input to bundle freshness, so a commit that
changed the build definition could validate locally against output made by the
previous one while CI rebuilt. They count now, beside the library's own
manifest, build config and compiler options, and the rebuild scenario drives
the lockfile case through the miniature repository's observable build.
* 🧭 fix: Read the Base From the Merge Base, and Give a New File No Allowance
Two holes in the swap check, both about what "the base" means.
It read each changed file's prior version from the base branch's tip, while the
changed-file set comes from \. With \ moving under a branch,
that calls a violation dev added since the branch diverged this change's doing,
and forgives one this change added that dev happens to carry. It resolves the
merge base once and reads from there.
And a file with no version at the base was skipped, so adding a source with a
violation and recording it in the same commit passed as "already recorded".
A file the change adds has no allowance to inherit: every design violation in
it is new. Renames are followed (\), so the
documented re-record of a moved path stays green — its violations came with the
file.
The scenario now walks recorded, moved, swapped, added-with-its-own-entry and
renamed through one miniature repository with a history of its own.
* 🔗 fix: One Base Commit, Asked Once
\ resolved the merge base; \ still read
the baseline from the base branch's tip, so the two checks in the same run
disagreed about what "before" meant — and an entry the base branch edited after
the branch diverged entered \ as though this change had edited it.
\ resolves it once and both ask it.
* 🔤 fix: A Suppressed Message Carries Its Text
\ omitted \, and the swap check reads it
— the union of messages and suppressed messages is what a file's diagnostics
are, and what the baseline silences is exactly what that check has to see.
Nothing local caught it: node strips types without checking them and no
tsconfig covers scripts/, so the runner is now typechecked directly
([41m [0m
[41m[37m This is not the tsc command you are looking for [0m
[41m [0m
To get access to the TypeScript compiler, [34mtsc[0m, from the command line either:
- Use [1mnpm install typescript[0m to first add TypeScript to your project [1mbefore[0m using npx
- Use [1myarn[0m to avoid accidentally running code from un-installed packages, clean).
* ⚖️ fix: Let the Record Grow, Out Loud
The swap check reported every violation a change added, which also forbade the
one path CLAUDE.md sanctions: add the violation and raise the count, so the
debt arrives as a line in the diff a reviewer reads. What separates that from a
swap is not the violation, it is whether the record said so — a swap keeps the
count still.
The check now charges each new diagnostic against how much that entry's count
grew between the merge base and here, and reports only what the growth does not
cover. Measured: a swap at an unchanged count fails, the same violation with
the count raised passes, and raising nothing fails naming the class. CLAUDE.md
says which of the three is which.
* 🚧 fix: A File the Change Adds Brings No Allowance
Letting the record grow to cover a new violation is right for a file that
already existed — the raised count is the line a reviewer reads — but it also
let a brand-new source arrive with its own entry, which is the backlog
recording what the tree never owed. Growth applies only where there is
something to grow: a file present at the merge base, renamed or not. A file the
change adds owes everything in it, and the message says so.
* ✏️ test: Assert the Wording the Added-File Rule Uses
The scenario still expected "is new here" for a file the change adds, which now
gets its own message — the backlog records what the tree owed, not what a new
file brings. The harness caught it; the assertion follows the rule.
* 🧮 fix: Ask Git What Existed, and Where Its Entry Lived
Three faults in the new swap check, one cause: it inferred from content what
only git can answer.
A tracked empty file at the base reads as absent, so an existing file was
treated as newly added and its recorded growth refused. A renamed file's
growth was measured at the new key, where the base has no entry at all, so the
move itself looked like room to spend. And a base commit missing from the clone
made every changed file look new, silently.
Existence is now \ succeeding, the allowance is read under the name
the entry had (the rename map already resolved the source; it resolves the
entry too), and a base commit that is not in the clone is reported rather than
guessed at. Measured: a rename carrying a new violation with its count raised
at the new key fails, a plain rename passes, a swap at an equal count fails.
* 🗃️ fix: A Deleted Baseline Is a Deleted Baseline, Nested or Not
The root baseline's deletion failed the check; a nested one was quietly dropped
from the target list by the same \ filter that was meant to skip
paths never present. What a nested baseline silences goes back to being
reported — or stops being — and nothing else reads it, so its deletion is the
same failure and now says which file is missing.
* 📚 fix: A Baseline in the Diff Is Read in Full
The comment said a baseline named in the diff is checked in full; the code
checked only the entries the diff moved, because \ made
\ non-empty and the full-record branch required it to be empty. So a
count edit never revalidated the rest of the record — dormant paths and stale
counts in unedited entries went unread. Editing the baseline now reads every
path it records.
And the rename map keyed on git's raw line, so a CRLF checkout left a \ on
the new path and the rename never matched — the file read as added and its
inherited allowance was refused. The lines are trimmed.
* 🧷 fix: Lint the Base Under the Name It Had, and Compare a Commit With Its Parent
Two last seams in the swap check.
A renamed file's base contents were linted under the new path, so moving a
component out of \ — where overriding a primitive is the
library's own business — into \ made the destination's stricter
rules apply to both sides: the diagnostics the move itself introduces looked
inherited, and a matching entry would have hidden them. The base is linted as
the path it had.
And \, the documented single-commit mode, resolved its
comparison to \ — the commit against itself, which forgives everything.
It resolves to the commit's parent.
* 🔖 fix: Quotes Do Not Hide an Inline Rule Configuration
ESLint reads \ exactly as it reads
the unquoted key, but the comment scan required the colon to follow the rule id
directly, so the quoted form passed over a file that does not violate the rule
yet — and then silences the first violation a primitive or config fan-out
creates there. Over a violating file the two-run comparison always caught it;
the dormant case is the one that slipped. The scan accepts either quoting, and
the scenario carries the quoted spelling beside the rest.
* 🧪 perf: A Spec Is Not Component Metadata
Both roots the design rules police are full of specs — every primitive in
`packages/client/src/components` has one — and editing any of them ran the
two-root suppression sweep, because the metadata predicate asked only whether
the path was a lintable source under a metadata root. A spec is neither: the
flat config turns all five design rules off inside it and the library's
tsconfig keeps it out of the bundle the caller rules resolve primitives
through, so it carries no `cva` variant and can report no design diagnostic of
its own.
`packages/client/src/components/Button.spec.tsx` through the runner:
59.7s before, 0.0s after.
The same `DESIGN_TEST_SOURCE` test is applied everywhere the invariant holds:
the freshness input no longer treats a spec as something `dist` is built from
(directory mtimes stay, so an added or deleted source still forces a rebuild),
the directive gate no longer fails a commit over a comment in a file where the
design rules are already off, and the base-vs-head lint no longer fetches a
spec's base version to ask a question with one possible answer.
* 🔎 fix: A Build That Reports Success Is Not a Build
Two gates trusted a signal instead of the condition it stands for.
`buildClientPackage` returned on the build's exit code. A build that exits 0
while leaving `packages/client/dist` incomplete or older than its sources left
`designMetadataIsFresh()`'s answer exactly as it found it, and the suppression
lint that follows then classified callers against a bundle describing something
else — reporting fewer violations than CI, which is the failure the rebuild
exists to prevent, reached through the branch meant to fix it. The freshness
question is now asked again after a successful build, and a bundle still stale
is an error naming `packages/client/dist`.
The workflow's `on.pull_request.paths` named only the root
`eslint-suppressions.json` while the inner `suppressions` filter names
`**/eslint-suppressions.json`. A diff touching only a nested baseline outside
`api|client|config|packages|scripts` — `e2e/fixtures`, say — started no
workflow at all, so `validateSuppressions()` never ran over exactly the nested
baselines it advertises. The trigger carries both spellings now.
The miniature repository's `build:client-package` writes the bundle its
manifest promises instead of only a marker, so the rebuild rows assert a
finished build rather than a claim of one, and a build that writes nothing is
its own row.
* 🧭 fix: Ask the Tool, Don't Reconstruct Its Answer
Four findings on the last head, and three of them were the next variant of a
family this branch had already patched once. The shared cause is one habit:
the gate re-derived a fact from a cheaper proxy instead of asking whatever owns
it, and every round found another case the proxy got wrong.
An inline `eslint` configuration comment was matched by the spelling of its
rule key, so `shadcn/x`, `"shadcn/x"`, `"shadcn\/x"` and `"shadcn\u002fx"` —
one key, four spellings, all of which ESLint honours — needed four regexes.
The key is now decoded before it is compared, which ends the family rather
than adding the fourth.
Bundle freshness reduced a set of outputs to the newest file anywhere under
`dist`. A resolver loads one entry, not a directory, so a build that rewrote
the ESM bundle and left the CJS one or the type declarations behind passed
while `@shadcn/lint` read the stale one. Every declared output is compared
now, which is the same reason their existence was already checked one by one.
Target discovery read `git diff --name-only` with rename detection, which
names one side. `git mv eslint-suppressions.json backlog.json` therefore
selected no group that owns the source, and the baseline's disappearance was
reported by nothing. `--no-renames` at the single funnel makes a rename two
paths; the one caller that wants the pairing asks for it separately.
The fourth was the parity assertion added last round, sitting in the mock
Playwright lane — whose own trigger excludes `.github/workflows` apart from its
own file, so a workflow-only diff never ran the assertion guarding it. The
claim moved into the runner, where the file that can break it already
activates the group, and the scenario now exercises the runner both ways over
a copy of the real workflow instead of reimplementing its parser.
* 📝 docs: Say Which Swaps the Ratchet Misses
The ratchet paragraph claimed a swap arrives as a failure, full stop. Two do
not, and a reader deciding whether to trust the gate needs both.
A swap whose diagnostic message equals the one it replaced is invisible,
because the comparison keys on the message and some rules name the property
without the value — `Inline style sets display.` is one message for `none` and
for `flex`. And a swap the diff itself causes, by changing a primitive or the
config, is invisible because both versions are linted under the head's design
inputs.
Tracked at berry-13/LibreChat#70 and berry-13/LibreChat#69.
* 📏 chore: Re-Record the Count Dev Moved Under the Backlog
`dev` fixed one `shadcn/no-restyle` violation in
`client/src/components/SharePoint/SharePointPickerDialog.tsx`, so the rebased
branch records 8 where the file now has 7. An entry with slack is exactly what
this change added a check for: the unused 1 would silence the next violation
anyone writes there, and the gate says so rather than letting it through.
`npm run lint:design:prune` drops the one count; the trailing newline it strips
is restored by prettier, the way the pre-commit hook's `*.json` group does.
* 📝 docs: Drop a Recorded Count That Dev Keeps Moving
The backlog total moves with every rebase that picks up new violations from
`dev`, so a number written into CLAUDE.md is stale by the next re-record — it
read 3,230 against a baseline holding 3,232. The recipe does not need the
figure; `eslint-suppressions.json` is the record.
* 📏 chore: Re-Record the Counts Canary Moved Under the Backlog
Retargeting onto canary changes the tree the design rules read, so five
files carry different violation counts than the recorded backlog claims:
Wakeup and SubagentConversation shed no-restyle violations and picked up
a require-static-classes one, and Ledger, UnifiedSidebar and ChatRoute
each gained one. Re-recorded those entries and pruned; nothing else in
the baseline moved.
* 🧱 fix: Build the Primitives Before Recording What They Own
`lint:design:record` was the one exposed design script that linted without
building `packages/client/dist` first. The rules read each primitive's `cva`
variants out of that bundle, so a fresh checkout or a primitive change left
the recording run classifying against a library that does not exist yet and
writing counts lower than the ones CI reports.
Moved the build onto the record script, where the other three already have
it, and dropped it from the `lint:design:suppress` wrapper so the wrapper
still pays for exactly two builds. A build failure inside the wrapper is
still loud: the record step cannot rewrite the baseline when the build it is
chained to fails, and the prune that follows does not swallow its own.
* 📏 chore: Re-Record the Counts Canary Moved Under the Backlog
Rebasing onto canary picks up PR #15798, which rewrote the agent marketplace
components, so eighteen recorded entries no longer describe the files they
name: AgentDetail is gone, AgentCard traded its restyle and arbitrary-value
violations for inline styles, AgentDetailContent grew to eighteen restyles,
and CopyLink, GridSkeleton, Marketplace, MineFilterToggle, SortDropdown and
LoadingDots arrived with violations nothing had recorded.
Re-recorded and pruned; every entry that moved belongs to a file that commit
touched, and nothing else in the baseline changed.
* 🧪 test: Assert the Record Asks for a Build Before It Writes
The build-freshness scenario covers the runner. Nothing covered the other
entry point: `npm run lint:design:record`, which writes the budget every
later change is measured against and, until now, linted whatever bundle
happened to be on disk.
The miniature repository's `build:client-package` appends to a log, so the
scenario can ask whether the record built before it wrote, and whether a
build that fails still stops the wrapper now that the wrapper no longer
carries a build of its own.
---------
Co-authored-by: berry-13 <berry-13@users.noreply.github.com>
(cherry picked from commit
|
||
|
|
76dbb931a1
|
🧤 docs: Define Safe Backend Error Contracts in AGENTS.md (#16443)
Co-authored-by: Lia <lia@librechat.ai> |
||
|
|
d369c649ed
|
🪥 chore: Run CI's Static Checks on Each Commit's Diff (#15303)
* 🪝 chore: Run Static Checks on Every Commit Adds `scripts/static-checks.mts`, a local port of the Static Checks CI job (.github/workflows/static-checks.yml) scoped to the files in a diff. It resolves the changed-file list, applies the same `dorny/paths-filter` groups the job uses, and runs whichever checks those paths activate — ESLint, Prettier, import order, ESLint config validation, package.json validation, and (behind `--full`) config migration tests, unused i18n keys and depcheck. Like the job, every selected check runs even after one fails and the failures are summarized at the end. The pre-commit hook keeps lint-staged for the per-file layer, which verifies the exact staged content of partially staged files, then runs the script for everything lint-staged cannot cover. lint-staged now uses the job's ESLint invocation, so warnings fail locally the way they fail CI. The slow gates stay opt-in (`npm run static-checks:full`, or `STATIC_CHECKS_FULL=1`) to keep commit latency unchanged. Hooks were never installed: `config/prepare.js` existed but no `prepare` script called it, so the hook only ran where `core.hooksPath` had been set by hand. Replaces it with an inline `prepare` — both Dockerfiles run `npm ci` after copying only the manifests, so a `node config/prepare.js` step would fail the image build, and husky is absent from `--omit=dev` installs. The i18n scan is a single pass over the source identifiers rather than one grep per key, verified to flag exactly the same keys as the CI loop (including the substring and dynamic-key cases) in 0.5s instead of 14s. * 🩹 fix: Address Codex Round 1 on the Static Checks Runner Activate gates from the unfiltered changed-path list. `dorny/paths-filter` matches deleted paths too, so gating on the `--diff-filter=ACMRTUXB` list the per-file steps use let a delete-only commit — the last reference to a translation key, say — slip past the i18n and depcheck gates. The two lists are now derived separately, the way CI derives them. Pass `-m` to `git diff-tree` in `--commit` mode. Without it a merge commit emits no paths at all, so `--commit <merge-sha>` reported "Nothing to check"; a real merge in this repo's history goes from 0 to 97 files. Build the workspaces the config suite imports instead of skipping when `dist` is absent. The three `dist` directories are gitignored and `npm ci` does not produce them, so a fresh checkout reported a pass for a gate that never ran — and an existing `dist` could be stale. Each is a sub-second tsdown build. Resolve a global depcheck through a shell on Windows, where npm exposes it as `depcheck.cmd` and `spawnSync` cannot see the shim. Skip dot directories when walking for imports. `.claude/worktrees/` can hold a full checkout per branch — 117 on this machine — and the root-wide scan behind the depcheck gate walked every one of them. Records the remaining boundary in the header: the per-file checks see exact staged content via lint-staged, while the tree-wide gates read the working tree, as running them by hand would. * 🩹 fix: Address Codex Round 2 on the Static Checks Runner Treat an unresolvable checker as a failure. ESLint and Prettier missing meant the runner printed "All affected static checks passed" without having linted anything; only depcheck, which CI installs globally and this documents as optional, may still skip. Catch per-check exceptions. The runner promises that every selected check runs even after a failure, but a throw — a malformed translation JSON, say — escaped and cancelled the checks after it. Each is now recorded as that check's failure; verified that depcheck still runs after i18n throws. Restrict `--commit` to the checked-out commit. Paths came from the named commit while contents came from the working tree, so an older revision was scored against the wrong file contents: a file added then deleted vanished, and one modified since was read at its newer contents. It now fails with a pointer to `--against`. Reject unknown options. `--ful` silently ran the fast tier and `--commmit HEAD` treated `HEAD` as a file path, both exiting 0 and implying gates had run. Cover the runner in CI. `scripts/**` was absent from the workflow's trigger paths, so a PR touching only the script the pre-commit hook now depends on got no Static Checks run — and ESLint has no flat-config match for `scripts/**/*.mts`, so nothing else loads it either. Adds the trigger path, a `runner` filter group and a step that runs the script against the PR's own diff. * 🔗 feat: Add Circular Dependency and TypeScript Gates Both already run in CI as jobs of the Backend Unit Tests workflow; this brings them to the local runner so they land before a push rather than after. Circular dependencies (`node config/circular-deps.mjs`) is fast enough at 0.9s to sit in the per-commit tier, gated on the same paths that trigger the CI job. TypeScript stays opt-in behind `--full`: the five projects cost between 2.3s and 20.9s each, which is too much per commit. Each project declares the paths that can affect it — its own sources plus its upstream packages — so an edit to data-provider still typechecks data-schemas, api, packages/client and client, while an `api/**`-only change runs none of them, since no typechecked project includes that directory. The builds a project's imports resolve through are made first, mirroring the CI jobs' dependency on the build artifacts, through a helper the config suite now shares. Also addresses codex round 3: Reject conflicting target selectors. `--against origin/dev package.json` silently checked only the file, and `--against <bad-ref> --commit HEAD` never resolved the bad base, so a caller could believe a range had been checked. Require a clean worktree in `--commit` mode. The HEAD-only restriction was not enough: contents still come from the working tree, so an uncommitted edit was scored against the named commit — an invalid uncommitted package.json failing a valid HEAD, or an uncommitted fix masking a defect in it. The summary now names how many checks were skipped rather than reporting a bare pass, and a typecheck failure carries the stale-workspace-build hint — inside a git worktree `librechat-data-provider` resolves to the main checkout, whose dist can predate the branch and shows up as missing properties. * 🩹 fix: Address Codex Round 4 on the Static Checks Runner Diff `--against` from the merge base. A two-dot diff reports the base branch's own commits in reverse once it advances, so `--against origin/dev` scored 64 files for a branch that changed 7, activating gates for files the branch never touched. Three dots makes the documented PR-style command mean what it says. Typecheck on root manifest changes. Both review workflows trigger their TypeScript jobs on package.json and package-lock.json, because a dependency or @types bump breaks compilation on its own; the local filter ignored them, so `static-checks:full` passed where CI would fail. Validate every workspace manifest. The list mirrored the four the CI step happens to name, so a malformed packages/api, data-provider or data-schemas manifest passed validation in the revision modes, which have no lint-staged pass behind them. Both lists now cover all seven. Make the runner smoke execute a check. `--list` never runs one, and a script-only PR activates no group, so the CI coverage added for exactly that case could pass with the execution path untouched. It now runs against an explicit target. * 🩹 fix: Address Codex Round 5 on the Static Checks Runner Activate the JSON gate for every manifest it validates. Round 4 added the four workspace manifests to the validation list but not to the filter that turns the gate on, so a malformed packages/data-provider or data-schemas manifest still passed when it was the only changed file — the list grew and the trigger did not. The same two entries also feed the unused-package calculation, reached through api/package.json's @librechat/data-schemas dependency. Include the owning workflows in the imported gates' filters. Circular dependencies and TypeScript come from the review workflows, both of which list their own YAML in `on.paths` and therefore rerun those jobs when the workflow changes; locally the gates stayed inactive, so a change to how they are built or invoked could bypass the local equivalent. Added to the group filters and to the per-project predicates, since a workflow-only change would otherwise activate the group and then select no project. Bound command batches by characters rather than file count. Windows caps a command line at 32767 characters, far below POSIX ARG_MAX, and a count does not bound that: 400 of this repository's longer paths already come to 30176 characters before the executable and fixed arguments. Verified that a list spanning several batches still reports a defect in its final file. |
||
|
|
c77e6a5ad2
|
🌉 fix: Preserve Missing Locize Translations (#14917) | ||
|
|
5ca667c258
|
🛡️ fix: Validate Translation Contracts Against English (#14914) | ||
|
|
6755544cee
|
🌍 ci: Harden Locize Translation Sync (#14784) | ||
|
|
b807292997
|
📉 perf: Bound Early Event Buffering for Detached Generations (#14612)
Some checks failed
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Has been cancelled
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Has been cancelled
GitNexus Index / index (push) Has been cancelled
GitNexus Index / post-index (push) Has been cancelled
* 📉 perf: Bound Early Event Buffering for Detached Generations
A generation streaming with no attached subscriber re-entered buffering
mode on every disconnect and retained each emitted event in
earlyEventBuffer for its remaining duration. A single 26-minute detached
run (~58,800 tool-argument deltas) grew the heap past 2 GiB with GC cost
climbing alongside it, while client reconnects always resume from
durable state and discard that local buffer anyway.
- Close the early buffer after the first attachment drains it in Redis
mode; the durable chunk log and pub/sub own recovery from then on,
matching how cross-replica subscribers already attach.
- Enforce hard bounds (5,000 events / 8 MB estimated) in both modes; on
overflow the buffer is discarded and closed, with recovery falling back
to the durable chunk log (Redis) or resume snapshot (in-memory).
- Add a generation_stream_early_buffer_overflows_total counter and
earlyBufferedEvents/Bytes gauges on getRuntimeStats() for visibility.
- Add incident-shaped regression tests and update specs that pinned the
old post-disconnect re-buffering contract.
* fix: redirect post-overflow first attachments to resume recovery
A buffer discarded by the overflow guard left the initial non-resume
SSE attachment with nothing to replay, silently omitting pre-attach
output until the final event. Track the overflow on the runtime and
close such attachments with the existing reconnect signal instead: the
client already re-attaches with resume=true on transport failure and
its sync frame reconstructs the discarded output from durable/snapshot
state. Adds no per-event work; the check is one boolean per attachment.
* fix: enforce buffer bounds when restoring canceled resume captures
Captured emissions restored by a resume canceled before activation
bypassed the early-buffer hard cap, so one oversized restoration could
persist past the limits with no later emission to trip the guard.
Restoration now applies the same overflow-and-close behavior through a
shared helper, and the restore-cap spec fails before this change
(5 events / ~10MB retained) and passes after.
* chore: add Redis management scripts and update package.json for Redis commands
|
||
|
|
ad4ed67070
|
🟦 chore: Convert Activity-Label Eval Harness to TypeScript (#14530) | ||
|
|
a07c0e4ae8
|
🧪 chore: Add the Activity-Label Prose Eval Harness (#14527)
Grades fast-model activity-label headers against a fixed corpus so instruction changes are measured rather than eyeballed on one conversation. This existed untracked while the continuity work was developed; committing it because it is the only reproducible record of WHY `ACTIVITY_INSTRUCTION` is ordered and capped the way it is. - captured.json: 9 real production payloads pulled verbatim from Langfuse with the headers that shipped. Irreplaceable — traces age out. - corpus.js: 17 cases / 28 steps. The captured run replays as one sequence, plus synthetic cases for the modes it never exercised (all-failed, partial, parallel batches, rapid near-duplicates, entry overflow, truncated output, error-shaped success). Multi-step cases chain each generated label into the next step's context, which is what makes cross-batch redundancy measurable at all. - prompt.js: faithful port of the SDK's buildActivityLabelPrompt so synthetic cases render the bytes production sends, plus a previousLabelCap knob for continuity-window experiments. - variants.js: single-factor instruction variants. The baseline is read from the BUILT package (workspace resolution, then dist, then LABEL_EVAL_DIST) so a variant can never be graded against a stale copy of the shipped instruction. - checks.js: length/punctuation/markdown/tool-echo/count-echo, plus overlap split into `restate` (adds nothing over an earlier header) vs `template` (same frame, new payload — often fine). - run.js / rescore.js: live runner on the production wire shape (max_tokens 256) and an offline re-grader, so metric fixes never require re-spending on the API. Results are gitignored — regenerable, and 292K of the 364K. A full sweep is ~$0.03 per variant and ~45s. Findings are recorded in the README, two of them counter-intuitive: enumerating acceptable opening verbs ANCHORED the model rather than diversifying it (Confirmed 18→23, opener diversity halved), and diverse examples alone changed nothing. Sentence order is load-bearing, so a tidying reshuffle of ACTIVITY_INSTRUCTION regresses real output. |
||
|
|
bfb6b224d2
|
🔧 chore: Update ESLint config, Import Sorting script, Test Sharding, Bump @librechat/agents (#13552)
* 🔧 chore: Update ESLint config, add import sorting script, Test Sharding, Bump `@librechat/agents`
* Change 'no-nested-ternary' rule from 'warn' to 'error' in ESLint config
* Add new scripts for sorting imports in the project
* Update lint-staged configuration to include import sorting
* Modify GitHub Actions workflows to support sharding for unit tests
* chore: remove nested ternary expressions
* refactor: Extract scale multiplier logic into a separate function in CircleRender component
* refactor: Simplify auto-refill rendering logic in Balance component for better readability
* refactor: Improve width style handling in DataTable components for clarity and maintainability
* chore: remove CircleRender component
* delete: Remove CircleRender component as it is no longer needed in the project
* chore: Bump @librechat/agents to version 3.2.31 and update Node.js engine requirement
* Update @librechat/agents dependency from 3.2.2 to 3.2.31 in package-lock.json, api/package.json, and packages/api/package.json
* Change Node.js engine requirement from >=20.0.0 to >=24.0.0 in @librechat/agents
* chore: Add import sorting check to ESLint CI workflow
* Implement a new job in the GitHub Actions workflow to verify import ordering on changed files.
* The job checks for changes in specific file types and reports any import order drift, providing instructions for local fixes.
|