Skip to content

feat(chat): Claude parity visual uplift - #1161

Merged
sakibsadmanshajib merged 4 commits into
mainfrom
feat/claude-visual-parity
Aug 25, 2026
Merged

sakibsadmanshajib merged 4 commits into
mainfrom
feat/claude-visual-parity

Conversation

@sakibsadmanshajib

Copy link
Copy Markdown
Owner

Summary

Three committed slices on the Open WebUI fork moving the chat surface toward the Claude visual register:

  1. Theme states and an in shell 404 (4e0779d): app shell carries explicit light, dark, and high contrast theme states via data-theme on app.html, General settings gains a theme selector, and an in shell hivePath catch-all route replaces browser-default 404s.
  2. Model identity in the composer with catalog copy (8560dd7): model identity moves from navbar into the composer; GET /v1/models gains additive display_name/summary fields on control-plane and edge-api catalog types; ModelItem.svelte renders the purpose line per alias; new QuickStartChips.svelte; Placeholder simplified accordingly.
  3. Serif document register and reading measure (f2c9a1e): Source Serif 4 variable font ships as local woff2 assets under hive.css @font-face; assistant messages and project pages adopt a serif document register with reading-measure constraints; tokens extended in packages/hive-tokens/tokens.css.

The Go changes are additive optional fields on GET /v1/models (omitempty), backward compatible for existing API consumers.

Verification

  • go build + go test green on ./internal/catalog/... for control-plane and edge-api (toolchain image).
  • Token reference audit: every --hv-* token referenced by new CSS exists in tokens.css.
  • Font files validated by woff2 magic bytes; class anchors verified (.message-listitem, .group wrapper, chat-assistant role class).
  • Full open-webui image build executed on this branch and passing: docker build -f deploy/docker/Dockerfile.open-webui -t hive-open-webui:v0.10.2-branded . exited 0, with the final-stage read-back assertion printing "hive: shell present, removed surfaces absent" (SourceSerif4-Variable, HankenGrotesk-Variable and data-hive-nav all found in the built bundle; removed surfaces absent). One earlier attempt failed on a transient PyPI metadata fetch inside the frontend build; the retry passed clean from cached layers.

Test plan

  • Go unit tests pass
  • Bundle assertion mechanism verified at source level
  • Full open-webui image build passes with the fail-loud bundle assertions
  • Visual proof capture at merge time (orchestrator)

Known risks

  • The frontend build fetches PyPI metadata live (pyodide wheel prefetch) and one of two attempts hit a transient socket failure; the Dockerfile has no retry wrapper, so a flaky-network rebuild could fail the same way and a rerun clears it.
  • Serif rendering is verified at bundle level (fonts present and referenced); pixel-level rendering across all three theme states still needs eyes at merge.

Two families now split the type system: Hanken Grotesk carries every
control surface, Source Serif 4 carries the document half, meaning the
chat greeting, destination page titles, and assistant prose in the
transcript.

Assistant prose is set at 19px on a 52rem measure shared by the
composer, the message rows and the turn separators, so the conversation
reads as one column. Machine output inside a reply (code, tables,
buttons) stays in the grotesque or mono explicitly, because a blanket
serif over the subtree would put it into code blocks too.

Message toolbars reveal on hover via opacity rather than visibility so
keyboard focus still reaches copy and regenerate; coarse pointers and
prefers-reduced-motion get explicit handling.

Source Serif 4 chosen for a real weight axis plus a true italic;
synthetic oblique on a text serif would be seen constantly in emphasis.
Three woff2 subsets with unicode-range so a Latin page fetches one file.
The image build asserts the serif face is present in the shipped bundle
alongside the existing brand typeface check.
The model selector moves out of the page header into the composer's
control row, bound through Placeholder and Chat so a selection sticks
to the conversation. On the chat home it was also the empty state's
headline, which is why every session opened with a model slug instead
of a greeting; the headline is now the person, set in the serif display
register.

The picker menu flips open upward when the trigger sits near the bottom
of the window, anchored to a fixed lower edge so it never measures or
jumps. Each row renders the alias's one sentence purpose line from the
catalog: control-plane now carries display_name and summary as additive
omitempty fields on GET /v1/models, edge-api re-serialises them
verbatim, and strict OpenAI clients see no new keys when an alias has
neither.

Quick-start chips sit under the composer on the chat home. Static four,
seed the composer and return focus rather than submitting, because a
chip that spends credits on one click gives no chance to edit.
The theme control is System, Light, Dark, the whole set. oled-dark
overwrote four gray ramp custom properties with raw hex inline styles,
which silently discarded the warm charcoal the brand palette is defined
in; her was an upstream easter egg. Stored legacy values fold onto dark
or light so existing accounts still resolve.

data-theme is now written alongside the dark class in all three places
themes change (boot script, settings applyTheme, layout event), because
the Hive token layer keys its dark register off data-theme while
upstream utilities read the class. Without it a light OS user choosing
Dark got upstream surfaces with Hive navigation on top.

An unmatched URL now renders inside the product shell instead of
SvelteKit's bare root error page. It has to be a catch-all page rather
than a group error file: with no matching route there is no layout for
an error page to render inside. Specific routes always win over the
rest parameter, and auth, share and error routes outside (app) keep
their own treatment.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 7 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1480f390-115c-4c5f-b999-c0ab992cc0e6

📥 Commits

Reviewing files that changed from the base of the PR and between 34c90fd and 9728d8d.

⛔ Files ignored due to path filters (3)
  • vendor/open-webui/static/assets/fonts/SourceSerif4-Variable-italic-latin.woff2 is excluded by !**/*.woff2
  • vendor/open-webui/static/assets/fonts/SourceSerif4-Variable-latin-ext.woff2 is excluded by !**/*.woff2
  • vendor/open-webui/static/assets/fonts/SourceSerif4-Variable-latin.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (23)
  • apps/control-plane/internal/catalog/service.go
  • apps/control-plane/internal/catalog/types.go
  • apps/edge-api/internal/catalog/client.go
  • deploy/docker/Dockerfile.open-webui
  • docs/proof/claude-parity-uplift-1161/capture-log.md
  • packages/hive-tokens/tokens.css
  • vendor/open-webui/src/app.html
  • vendor/open-webui/src/lib/components/chat/Chat.svelte
  • vendor/open-webui/src/lib/components/chat/MessageInput.svelte
  • vendor/open-webui/src/lib/components/chat/Messages/ResponseMessage.svelte
  • vendor/open-webui/src/lib/components/chat/ModelSelector/ModelItem.svelte
  • vendor/open-webui/src/lib/components/chat/ModelSelector/Selector.svelte
  • vendor/open-webui/src/lib/components/chat/Navbar.svelte
  • vendor/open-webui/src/lib/components/chat/Placeholder.svelte
  • vendor/open-webui/src/lib/components/chat/Settings/General.svelte
  • vendor/open-webui/src/lib/hive/QuickStartChips.svelte
  • vendor/open-webui/src/lib/hive/hive.css
  • vendor/open-webui/src/lib/hive/projects/ProjectDetail.svelte
  • vendor/open-webui/src/lib/hive/projects/ProjectsList.svelte
  • vendor/open-webui/src/routes/(app)/[...hivePath]/+page.svelte
  • vendor/open-webui/src/routes/(app)/artifacts/+page.svelte
  • vendor/open-webui/src/routes/+layout.svelte
  • vendor/open-webui/src/routes/s/[id]/+page.svelte

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Text transcript of the visual proof run: standalone branded image
against a stub OpenAI compatible backend, four surfaces captured, no
credential-bearing URLs in any frame.
@sakibsadmanshajib

Copy link
Copy Markdown
Owner Author

Visual proof

Visual proof against hive-open-webui:v0.10.2-branded built from 4e0779d, running standalone on a throwaway network with a stub OpenAI compatible backend. (1) Light theme transcript: assistant prose in Source Serif 4 at the shared reading measure, chrome in Hanken Grotesk. (2) Model picker opened from the composer chip: each alias renders its catalog purpose line under its name. (3) Serif close-up: true italic emphasis plus a code span that stays mono. (4) Dark full window after a localStorage dark reload through the boot script that now writes data-theme alongside the class. Capture log: docs/proof/claude-parity-uplift-1161/capture-log.md

pr1161-20260825165050-3386-parity1161-01-transcript-light.png

pr1161-20260825165054-13730-parity1161-02-model-picker.png

pr1161-20260825165057-19119-parity1161-03-serif-closeup.png

pr1161-20260825165100-28015-parity1161-04-dark-fullwindow.png

@sakibsadmanshajib
sakibsadmanshajib merged commit fe7eccd into main Aug 25, 2026
27 checks passed
@sakibsadmanshajib
sakibsadmanshajib deleted the feat/claude-visual-parity branch August 25, 2026 17:10
@sakibsadmanshajib

Copy link
Copy Markdown
Owner Author

Visual proof

Post-deploy live confirmation on https://chat-hive.scubed.co (deployed fe7eccd): signed-in chat rendering the serif assistant register, reasoning disclosure line, and the composer model-identity control (Hive Auto) from the parity uplift. Captured headless against the running box after the CF colo maintenance window cleared.

pr1161-20260825185531-8440-hive-live-1161.png

sakibsadmanshajib added a commit that referenced this pull request Aug 25, 2026
## Summary

Adds the committed text capture log for PR #1161's post-deploy live
screenshot, per the visual proof rule: the image itself lives as a
permanent asset on the visual-proof-assets release, while the scanned
text log must live under docs/proof/ so npm run lint:proof-tokens
actually covers it.

The log records the 2026-08-25 capture against
https://chat-hive.scubed.co running the deployed sha fe7eccd, taken
with headless chromium via Playwright, signed in through the admin
one-time-token magiclink mint documented in docs/live-test-auth.md (no
password touched; ssh tunnel to box GoTrue used for the admin mint step
only). It lists what the capture shows (serif assistant register,
reasoning disclosure, composer model identity Hive Auto, Hive nav
sidebar, credits line), points to the uploaded release asset and the
inline PR comment, and states the credential posture: no token-bearing
URL in pixels or log, session state file deleted after capture.

## Test plan

- [x] npm run lint:proof-tokens passes (169 files under docs/proof/
scanned)
- [x] Log content matches the actual capture and artifact URLs
- [x] Docs-only change, no code touched
sakibsadmanshajib added a commit that referenced this pull request Aug 25, 2026
…) (#1184)

## Summary

Chat parity wave 2. Five of the six dispatched slices were already
landed on main by #1161 earlier today; this PR carries the one remaining
slice and records where each of the six stands.

1. **Fixed reading measure and hairline turn rules**: shipped in #1161
(slice 3). The transcript, composer column and turn separators share
`--hv-measure`, with 1px rules between message rows via
`.message-listitem + .message-listitem`.
2. **Four quick-start chips under the composer**: shipped in #1161
(slice 2) as `hive/QuickStartChips.svelte`. Chips seed the composer via
`setText` and focus it; nothing auto-sends.
3. **Catalog purpose subtitles in the model picker**: shipped in #1161
(slice 2). `ModelItem.svelte` renders the catalog `description` (the
catalog summary carried additively by GET /v1/models) as a subtitle line
per alias.
4. **404 and loading states inside the app shell**: 404 shipped in #1161
(slice 1) as the `[...hivePath]` catch-all rendering branded chrome;
in-shell slow-load state is the existing sidebar-plus-spinner block in
`(app)/+layout.svelte`. The pre-hydration splash image remains an
upstream asset, unchanged here.
5. **Theme control at exactly three options**: shipped in #1161 (slice
1). General settings offers System, Light and Dark only; the choice
persists to localStorage and System honors `prefers-color-scheme`.
6. **Knowledge nav (#1109), new in this PR**: the row pointed at
`/workspace/knowledge`, which sits behind the workspace layout's
permission guard. That guard bounces a non-admin without the
workspace.knowledge permission straight home, so the click navigated and
the person landed back where they started: the row highlighted and did
nothing. A new top-level `/knowledge` route outside that guard renders a
read-only index of the caller's own bases from `GET /api/v1/knowledge/`
(verified-user endpoint); admins and accounts holding the
workspace.knowledge permission are forwarded to the full workspace
surface. Nav data, tests and hive.css styles updated together.

## Verification

- Full frontend unit suite: `npx vitest run`, 12 files, 119 tests
passed.
- Image build from this worktree: `docker build -f
deploy/docker/Dockerfile.open-webui -t hive-open-webui:parity2 .` exited
0; final assertion line printed exactly: `hive: shell present, removed
surfaces absent`.
- Token audit: every `--hv-*` variable referenced by `hive.css` resolves
to a definition in `packages/hive-tokens/tokens.css`; zero unresolved
references.
- Class anchors preserved: `.message-listitem`, `chat-assistant`,
`data-hive-nav` untouched.

## Test plan

- [x] Frontend unit tests pass
- [x] Image build passes with fail-loud bundle assertions
- [x] Token audit clean
- [ ] Visual proof capture at merge time (orchestrator)

## Known risks

- The read-only index shows only what `GET /api/v1/knowledge/` returns
for that caller; a tenant user sees bases shared with them through
groups. If product later wants inline document listing from this page,
it is a follow-up.
- #1056 (knowledge admin short-circuit across tenants) is being handled
separately on the backend router; the frontend surface here consumes the
same endpoint.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added a Knowledge page accessible from the main navigation.
* Administrators and permitted users can access full knowledge-base
management.
* Other signed-in users can view accessible knowledge bases, including
descriptions and relative update times.
  * Added loading, error, and empty states for the knowledge-base list.

* **Bug Fixes**
* Updated navigation highlighting to correctly recognize Knowledge pages
and subpages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
sakibsadmanshajib added a commit that referenced this pull request Aug 25, 2026
Four defects on the home surface, found by querying the deployed box's DOM
rather than reading a screenshot, plus one rough edge in the run path.

The greeting and the four quick-start chips shipped in #1161, deployed, and
were invisible. Chat.svelte's landing branch read `$settings?.landingPageMode
=== 'chat' || <messages exist>`, so an account that had ever flipped an
upstream personalisation toggle skipped Placeholder.svelte entirely and landed
on upstream's ChatPlaceholder: model name over a placeholder string, no
greeting, no chips. Nothing was broken and nothing logged; a stored setting was
quietly deleting two features. Hive has one home, so the toggle no longer
decides whether it exists, and the Interface row that set it goes with the
branch it drove rather than staying on as a control that changes nothing.

The model menu overflowed the window. It anchored its left edge to the trigger
and let its own width run off the right, which is what the model chip in the
composer's control row does at a normal desktop width; the panel's max-width
clamps how wide it can be and cannot move it back inside. It now anchors right
edge to right edge when the trigger is in the right half of the window, which
is what the reference does and needs no measurement, so there is still no
render-then-move jump. Measured on the box before: right edge 36px past the
viewport. After: inside it.

The Artifacts index had no title of its own, so its empty state was the first
thing on the surface and sat flush against the top of the pane. It gets a page
head in the same shape the Knowledge index uses, and the states below it now
sit in a column flex, which is what lets their `m-auto` centre at all.

A cowork run's row was titled "New Chat". Titles are generated by a follow-up
completion the run path does not make, so the conversation list stopped being
readable the moment there were two runs. The row takes the brief's first line.

Tests: home-surface.test.ts pins the landing branch, the greeting and chip
selectors a DOM check can find, and the absence of the removed control.
Placeholder.svelte and Settings/Interface.svelte join the fixture list the
frontend runner copies, since the pins read their sources.
sakibsadmanshajib added a commit that referenced this pull request Aug 25, 2026
…on (#944) (#1193)

Closes #944.

## What this is

D-045 rules that Cowork is a two segment control inside the chat
composer, that a run IS a conversation, and that the agent surface gets
no navigation row of its own. The shipped product still had the design
the owner rejected: an Agents destination you navigate to, a separate
form on a page the conversation cannot see, and a run that comes back
only as a row in a list. This lands the frontend half of that ruling.

## The composer becomes mode aware

A `Chat | Cowork` radiogroup sits immediately right of the plus button,
in the same rail as the model chip. It is a `radiogroup` with two
`radio` children rather than two buttons, so it announces as "Chat,
selected, 1 of 2" and moves with the arrow keys, with a roving tabindex
so Tab moves past the control rather than through it. Switching it
changes what the next message does and navigates nowhere. Nothing in the
change path touches the draft, so a half written brief survives a toggle
in either direction.

Selecting Cowork grows a second row welded to the bottom of the same
composer container, separated by a hairline rather than a gap, and drops
the voice mode button while keeping dictation.

## A run rendered as a conversation

This is the load bearing part. The run reuses the ordinary chat
machinery rather than a parallel one: the same `history`, the same
`initChatHandler` that creates the chat and puts it in the sidebar list,
the same `saveChatHandler` that persists it, and the same transcript
component that renders a chat. The chat is created before the task is
submitted, so a run takes a row in the conversation list from the moment
it is sent rather than only once it answers.

The assistant turn carries the run's state while it is queued or running
and the run's own summary once it settles, and it stores the task id, so
reopening the conversation picks a run back up. `loadChat` marks any
assistant turn left mid flight as done, which is the right recovery for
an interrupted completion and the wrong one for a run, because a run
does not stop when the tab closes.

## Sidebar grammar that goes with it

The Agents navigation row is gone and Artifacts takes its place in the
destination set, which is the other half of D-045's sidebar grammar and
had an index shipped by #1141 with nothing linking to it. The `/agents`
route itself survives, unlinked, so runs submitted before the composer
mode existed are still reachable by URL. The conversation list loses its
date bucket headers and its heading becomes "Chats and tasks", which is
exactly what #944's second acceptance criterion needs: a run and a chat
in one list under one heading. New Chat becomes the sidebar's one filled
primary row.

Knowledge keeps its row for now, and the file says why rather than
leaving a silent contradiction with D-045 ruling 2: Projects cannot hold
RAG collections yet, so removing the row today would take the only route
to them away and put nothing in its place, which is the failure mode
#944 warns about for the Agents row.

## What is deliberately not here, and why

Two controls the reference's second row carries are absent, for the
reasons #944 sets out itself:

- The autonomy control ships with the `waiting_for_confirmation`
collapse in `engine.go` or not at all. With Auto selected there are no
approval rows, so shipping it now would be a control with no observable
effect.
- The project or folder picker ships only if a run can be bound to a
workspace or a collection. `POST /v1/agent/tasks` accepts `pack` and
`instructions` and nothing else, so there is nothing for it to set, and
a picker that sets nothing is worse than no picker.

The Pack control is deleted rather than moved. `agent_kind` still
carries the distinction on the wire; the Home composer in Cowork mode
sends the knowledge work pack, and the coding pack belongs to a Code
panel this shell does not have.

## Backend ceiling, named rather than worked around

edge-api exposes a task's status and its final `result_summary_ref` and
nothing in between. There is no per step or event feed and no read by
id. Two consequences, both marked in the code with the upgrade path:

1. The run is followed by polling `GET /v1/agent/tasks` and filtering,
because there is no `GET /v1/agent/tasks/{id}`. Fine at demo volume,
wrong at scale.
2. The inline tool lines D-045 describes ("Used Claude in Chrome (2
actions)") and the right hand Progress / Working folder / Context panel
cannot be populated by any frontend until that endpoint exists. They are
not stubbed, faked or half drawn here.

This PR is frontend only by instruction: `vendor/open-webui/src/` and
nothing else. No `.py`, no `owui-patches/`, no Go, no
`apps/web-console/`, no CI config.

## Verification

- `scripts/test-owui-hive-frontend.sh`: 13 files, 139 tests passed, and
all 13 Hive components compile against svelte@5.56.0, the version the
image build resolves.
- New tests: `coworkMode.test.ts`, 19 cases. Pure helpers for the mode,
the pack derivation, the radiogroup's key handling and the run-to-turn
projection, plus source pins asserting the toggle is mounted immediately
after the plus button, that the second row appears only in Cowork, that
voice mode drops and dictation does not, and that the submit path
branches on the mode and goes through the ordinary chat machinery. The
source pins are the ones that matter here: a correct module nobody
mounted is the failure a unit test over pure helpers cannot see.
- `nav.test.ts` updated: it now asserts the absence of an Agents row and
the presence of Artifacts, rather than the reverse.

## Visual proof

Posted as a comment on this PR via `scripts/post-pr-visual-proof.sh`.
Capture log committed under `docs/proof/`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Cowork mode for launching sandboxed knowledge-work tasks with
visible progress.
  * Added Chat/Cowork switching with keyboard accessibility.
* Cowork results, errors, timeouts, and resumed active tasks now appear
in conversations.
  * Added an Artifacts destination with an updated page header.

* **UI Improvements**
* Renamed “Recents” to “Chats and tasks” and simplified the conversation
list.
  * Added responsive Cowork guidance and updated sidebar styling.
* Improved model menu positioning and removed the landing-page mode
setting.

* **Bug Fixes**
* Cowork submissions now reject blank instructions and attachments
without losing entered content.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---

## Second commit: four home-surface defects found by querying the
deployed DOM

These came out of a live re-score of the box and are folded in here
because they
are the same surface and the same build.

**The chat home was gone on some accounts.** The greeting and the four
quick-start chips shipped in #1161, deployed, and were invisible.
`Chat.svelte`'s
landing branch read `$settings?.landingPageMode === 'chat' || <messages
exist>`,
so an account that had ever flipped an upstream personalisation toggle
skipped
`Placeholder.svelte` entirely and landed on upstream's
`ChatPlaceholder`: model
name over a placeholder string, no greeting, no chips. Nothing was
broken and
nothing logged; a stored setting was quietly deleting two features.
Measured on
the box before this branch: `[data-hive-quickstart]` null,
`.hv-greeting` null.

Both components were correct and both were mounted, so this is not dead
code in
the wrong component: it is a branch reached by nobody. Hive has one
home, so
`landingPageMode` no longer decides whether it exists, and the Interface
row that
set it goes with the branch it drove rather than staying on as a control
that
changes nothing.

**The model menu overflowed the window.** It anchored its left edge to
the
trigger and let its own width run off the right, which is what the model
chip in
the composer's control row does at a normal desktop width; the panel's
`max-width` clamps how wide it can be and cannot move it back inside. It
now
anchors right edge to right edge when the trigger is in the right half
of the
window, which is what the reference does and needs no measurement of the
menu,
so there is still no render-then-move jump. Before: right edge 36px past
the
viewport. After: inside it.

**The Artifacts index had no title**, so its empty state was the first
thing on
the surface and sat flush at the top of the pane. It gets a page head in
the same
shape the Knowledge index uses, and the states below it now sit in a
column flex,
which is what lets their `m-auto` centre at all. Before: title null,
first child
at y=0. After: title present, first child at y=526.

**A cowork run's row was titled "New Chat."** Titles are generated by a
follow-up
completion the run path does not make, so the conversation list stopped
being
readable the moment there were two runs. The row takes the brief's first
line.

## The Knowledge contradiction, recorded rather than resolved

D-045 ruling 2 eliminates Knowledge as a destination in favour of
Projects and
Artifacts. #1184 gave Knowledge a real destination in the shell,
deliberately, to
close a dead-nav bug (#1109). The fork therefore contradicts its own
governing
decision right now. This PR does not resolve that: it leaves the row
alone and
says so in `nav.ts`, because Projects cannot hold RAG collections yet
and
removing the row today would take the only route to them away and put
nothing in
its place, which is the failure mode #944 warns about for the Agents
row. Flagged
for the owner rather than settled here.

## Visual proof

Two comments on this PR, both posted with
`scripts/post-pr-visual-proof.sh` to
the permanent `visual-proof-assets` release. The capture log, the
substrate, the
session mechanism and every DOM reading quoted above are committed under
`docs/proof/cowork-composer-mode-2026-08-25/`.

Substrate, stated because "ran it locally" is not one: the frontend
under test is
this branch's own build from `docker build -f
deploy/docker/Dockerfile.open-webui
--target frontend`, the exact stage the deploy image uses, served
against the
live demo box's backend. `before` frames come from the box's deployed
bundle,
`after` frames from this branch's, same backend.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant