drizzle derives `custom_widget_secret_definition_id_custom_widget_definition_id_fk`,
which is 65 characters. MySQL caps identifiers at 64 and rejects it with
error 1059, so migration 0040 created the key under a shortened name. The
schema never declared that name, so every `migration:mysql:generate` run
re-proposed dropping and recreating the key under the long one, producing a
migration that cannot be applied.
Declaring the shortened name explicitly makes the schema match what is
actually in the database. Only the mysql schema changes; sqlite and
postgresql keep their derived names and need no migration.
The parity test matched keys across dialects by generated name, which no
longer holds, so it now matches on what a key links - its columns, target
table and target columns - and compares the referential actions. That also
means it reads keys through getTableConfig and so covers table-level
declarations, not just inline .references() ones.
Adds a guard test asserting no mysql table, foreign key or index name
exceeds 64 characters, so the next long pairing fails in CI instead of
producing an unappliable migration.
Drops the feature-flags module, the /api/features endpoint and its test in
favour of calling api.assistant.getAvailability() from the root layout, the
same way the layout already calls api.user.getById().
The gate only needs one server-resolved boolean, and getAvailability
already computes exactly that. The separate helper duplicated the enabled /
modelId / api-key-required rule, so the two copies could drift.
A general feature-flag surface is still worth having, but it belongs at
boot time across every optional feature rather than as a per-request
database read introduced alongside the assistant.
Addresses two review findings.
The depth-first walk recursed once per message. A conversation is a single
parent chain and getThread loads the whole thread, so a long conversation
could exhaust the call stack; a 30,000 message chain already threw
RangeError. It now uses an explicit stack, pushing siblings in reverse so
the incoming order is preserved.
The cycle fallback appended unvisited messages without walking their
children, so for child -> a, a -> b, b -> a the child came out ahead of its
parent. Each remaining component is now entered at the earliest member of
its cycle in the incoming order and traversed from there, which leaves only
the cycle's own back-edge violating parent-before-child - unavoidable by
definition - instead of arbitrary messages.
AssistantProvider was imported statically by the root layout, so
assistant-ui, the Lexical composer, the Markdown renderer and Mermaid were
downloaded and evaluated on every page. The existing getAvailability gate
only skipped rendering, and the disabled branch still imported the board
widget, which pulls the whole conversation surface. An instance with the
assistant switched off paid the full cost, and so did the login page.
Measured on two production builds, /auth/login went from 42 chunks /
3.81MB including an 820KB assistant-ui chunk to 36 chunks / 2.40MB with
none, and the board page from 55 / 5.35MB to 56 / 4.56MB with none.
Adds getFeatureFlagsAsync as one source of truth for instance-level
feature switches, exposed both to the root layout and through a small
GET /api/features endpoint. Resolving the flag on the server is what makes
this work: next/dynamic never references the heavy chunk when the feature
is off, while enabled instances still server-render it, so there is no
loading flash and no remount.
getThread ordered purely by created_at, which is second-granular on MySQL
and SQLite. A user turn and the assistant reply routinely land in the same
second, so their relative order was whatever the storage engine returned.
The client rebuilds the conversation tree from parent_id, so a child ahead
of its parent corrupts the thread.
Adds an id tiebreaker for a deterministic database order, then a stable
topological pass that guarantees parents precede children while preserving
sibling order. Dangling parent references are treated as roots and cycles
are appended, so no message is ever dropped.
0042_snapshot.json spelled one table's check-constraint key
`checkConstraints` instead of `checkConstraint`. drizzle-kit rejects the
whole folder over it and exits 0 with only a printed message, so
`migration:mysql:generate` has silently produced nothing since #6192.
Every mysql migration authored since then was therefore hand-written with
no snapshot, including 0044 and 0045. Without those snapshots the next
generate diffs against 0043 and re-emits the assistant tables.
Adds the two missing snapshots and a test covering the invariants: journal
and .sql files agree both ways, every entry has a snapshot, indexes are
gap-free, and snapshots agree on the key spelling. The same test would have
caught the v1.70.0 release that shipped 0042_add_widget_secret.sql without
a journal entry, leaving upgraded MySQL instances with no widget_secret
table.
MySQL caps `text` at 65,535 bytes. A single base64 image attachment is
allowed up to ~1.34MB by the chat route, so appending such a message fails
with ER_DATA_TOO_LONG under the default strict mode. SQLite and PostgreSQL
are unaffected because their text types are effectively unbounded.
`mediumtext` holds 16MB, comfortably above the 2MB request cap.
The migration keeps the parenthesised default expression that every other
text default in this folder uses, because MySQL rejects a literal default
on TEXT columns.
E2E specs spin up Docker containers in parallel and intermittently time
out waiting for UI elements under shared-runner load. Limit parallel
workers to 2 and retry flaky failures twice so the container job can
complete and publish the PR preview image.
The customJsx E2E test occasionally failed at login with the form
showing 'This field is required' on the username, because the submit
button was clicked while the form was still hydrating (it is disabled
until mounted) and the field values were reset by the re-mount. Wait
for the submit button to become enabled before filling, and assert the
username value stuck before submitting.
The auto-fix workflow pushes a 'style: auto-fix lint and format' commit
after every user push, which triggers a new CI run that cancels the
in-progress run (concurrency cancel-in-progress) before the container
job can finish and publish the PR preview image. Mark the autofix commit
with [skip ci] so it does not restart CI, letting the user's run
complete and publish the image.
Remove the 'Connected to this Homarr instance' subtitle (and the running
state text) from the assistant panel header, keeping only the robot icon
and title.
RequestTelemetry derived thread message metadata inside the useAuiState
selector with messages.map(), returning a new array on every render.
useSyncExternalStore treats the new reference as a changed snapshot,
causing an infinite re-render loop (React error #185) while an assistant
message streams. Select the stable messages reference and memoize the
metadata derivation instead.