Skip to content

Release: FusionLab task-mode IA redesign (Direction C) - #50

Merged
obvious-autobuild[bot] merged 11 commits into
mainfrom
release/task-mode-ia-redesign-20261011-170103
Oct 12, 2026
Merged

obvious-autobuild[bot] merged 11 commits into
mainfrom
release/task-mode-ia-redesign-20261011-170103

Conversation

@obvious-autobuild

@obvious-autobuild obvious-autobuild Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Release: FusionLab task-mode IA redesign (Direction C)

Release intent: reorganize the FusionLab web UI around the three jobs it serves — Replay (watch a real shot), Compare (shots side by side), Explore (the model) — per the approved spec (Obvious Blueprint art_ZVpZN0l7), folding in the audit's hygiene fixes: one name per concept ("catalog", not "database"), MJ units everywhere, honest loading/error states, an edit drawer that keeps shot context visible, a Ctrl/Cmd+K command palette, and a hash-backed router (Back/Forward + shareable deep links). No backend changes — all work lands in web/ and lessons.json.

Verification: complete — V1–V10 from the spec, executed on the integrated branch at the release tip 73b4048 (see the sweep table below). Capability parity (V8): PASS, 72 controls, all reachable at depth ≤ 2 — V8 capability-parity checklist.

Branch: release/task-mode-ia-redesign-20261011-170103
Progress: 8/8 PRs complete (7 child lanes merged; this promotion audited and ready)

Planned Work

Review bots should verify that the release branch implements each planned subtask below, not just that the code is generally safe. For each row, compare the final diff and linked child PR against the expected outcome.

Subtask Expected outcome Status PR
Router, workspaces registry, header & welcome web/workspaces.js hash router; tabs Replay/Compare/Explore; welcome pick persists landing merged #58
Shared shot-load state machine web/shotload.js generalizes the compare chip machine; load chip on Replay merged #63
Replay edit drawer + toolbar regroup anchored what-if as role=dialog drawer; state cell stays live; USD errors surface merged #66
Compare workspace absorbs the catalog db tab + compare overlay unified in tab-compare; catalog naming; MJ units merged #67
Explore workspace sandbox in tab-explore with a deep link into the Replay drawer merged #60
Command palette web/palette.js native dialog, Ctrl/Cmd+K, all registries indexed merged #65
Lessons copy, a11y tokens, redundancy trim lessons.json label-driven copy; focus-visible + reduced-motion; limit proximity ≤2 merged #68
Promotion (this PR) integrate main (11 backend commits since branch-off), capability-parity audit (V8), V1–V10 sweep → squash to main complete —

Merged PRs

PR Title Summary
#58 feat(router): three workspaces behind one hash grammar Single-writer hash router (#/replay/{shot}?t= · #/compare/{ids}?ref= · #/explore, legacy #db/#sandbox/query deep links preserved); ARIA tabs Replay/Compare/Explore; welcome-pick landing persisted incl. reload-regression fix (bcf8301); provenance legend collapses <760px fixing the 375px title clip; --focus-ring token. pytest 352 passed + 1 skipped, ruff/mypy clean, CI 3/3 at bcf8301; QA evidence tc-1..6 uploaded.
#60 feat(explore workspace): add the edit-intent deep link to Replay Explore entry action "Edit a real shot instead →" → #/replay/30166?edit=1; router honors ?edit=1 by opening today's anchored what-if (drawer task will own it, call site commented); label-driven sandbox→Explore copy in lessons.json/glossary.json, tab:sandbox routing fields and anchors untouched (L3). pytest 352 passed + 1 skipped, CI 3/3 at e226638; QA records tc-1..4 attached.
#63 feat(shotload): shared shot-load state machine with a visible load chip web/shotload.js: one fetch lifecycle for every shot (queued → fetching → ready | error, AbortController supersede lands back in queued, errorOf()/onChange()); single-shot loads render a status-cluster chip with Retry · Drop on failure (V6); compare chips migrated onto the machine. pytest 352 passed + 1 skipped, CI 3/3 at 946687b; QA tc-1/tc-2/tc-4 incl. a genuine both-chips-fetching capture. Pre-existing stale compare failure-header behavior documented for the compare lane.
#65 feat(command palette): Ctrl/Cmd+K search over workspaces, shots, lessons, glossary web/palette.js: native <dialog> over every workspace, opened with Ctrl/Cmd+K or the header stub; case-insensitive search over workspaces, the /shots catalog (fetched once, cached), 43 lesson titles, 47 glossary terms (definitions shown in place), and actions running existing public surfaces only; navigation writes the URL grammar via workspaces.js; focus scoped to the dialog, restored to the opener (Esc and cancel). pytest 352 passed + 1 skipped, CI 3/3 at 02c5775; QA tc-1..3 (shot load, focus restore, lesson + glossary jumps).
#66 feat(drawer): anchored what-if becomes a right-side editor drawer over Replay 420px modal drawer (role=dialog, aria-modal, opener focus return) replaces the what-if cell swap; state cell, limit gauges and logbook stay live underneath; edits persist across close/reopen, reset restores the measured programme, ?edit=1 auto-opens; toolbar regrouped into Transport / Shot actions / Status clusters, wrap-safe at any width, all data-guide anchors kept; USD export failures surface in the fallback banner + transient status chip; stored energy in MJ on every Replay surface incl. compare traces and ΔW strip. pytest 352 passed + 1 skipped, ruff/mypy clean, CI 3/3 at 8035ea7; QA tc-1..3 (drawer flow via screenshots + DOM assertions — recordings wrote sub-second stubs this session, documented in the PR body; USD-error banner evidence; V7 lesson-1 walkthrough passed).
#67 feat(compare): the Compare workspace absorbs the catalog The Shot database tab is gone: catalog search/table/clouds/surrogate holdout + the compare bar live in #tab-compare, with the side-by-side read (stored energy in MJ, worst limit, ΔW vs reference, Thomson at the shared cursor); Replay preloads its on-screen shot as the reference via #/compare/{id}; Share copies the view URL; the stale-reference header bug (handed off from PR #63) is fixed; Retry re-fetches all failed chips; a /db outage degrades only the clouds panel. Zero "database" labels remain in web/ (verified at tip). pytest 352 passed + 1 skipped, ruff/mypy clean, CI 3/3 at f994688; QA tc-1..4 (deep-link boot, Share recording, Replay preload, error path).
#68 fix(ui): converge copy, focus/motion tokens and limit encodings Finished the sandbox→Explore label sweep incl. the agent accept string; repaired style.css's corrupted tail and removed the last hardcoded :focus-visible override (one shared --focus-ring rule); prefers-reduced-motion collapses gauge/spotlight tweens and gates the two JS smooth scrolls; insight() caps limit proximity at two encodings (names the closest limit, references gauges + limits strip instead of restating numbers). ARIA tablist/tab/tabpanel with roving tabindex + arrow keys verified in the accessibility tree. pytest 352 passed + 1 skipped, ruff/mypy clean, CI 3/3 at 9cef3e0; QA tc-1..4 (walkthrough, panel help, insight copy, focus ring). Known follow-ups: 3D auto-orbit and figure-jitter render-loop motion (outside the CSS-token brief), tracked in the release audit.

Release audit (this PR) — what landed after the child lanes

  1. main integration. The 11 backend commits main gained since branch-off (refactor: non-blocking remote job dispatch (HARN-4) #61 remote dispatch, feat(compute): multi-host compute profiles (HARN-5) #62 compute profiles, feat(data): bring-your-own-data — shot-bundle import through a DataSource seam #64 bring-your-your-own-data) merged into the release branch (integration commit 7ecf77a, 12 web/replay.js conflicts resolved). Import-bundle, CSV-export, benchmark/progress and archive-abort capabilities were carried forward with string shot-ID routing for imported bundles; a wording regression caught in validation ("a distance, never a forecast") was restored pre-push.
  2. Two post-integration fixes found by the audit sweep:
    • 02c16c3 — CSV-export failures surface in the UI (they had inherited main's console-only handler): dlExport takes an onErr callback; the Replay caller renders the fallback banner + status flash, the Compare caller uses the bar's own button-text idiom; usdErr/usdSeen renamed exportErr/exportSeen since the banner now carries both export kinds.
    • 73b4048 — mobile wrap: at 375px the compare bar and its action cluster could not wrap and pushed the page 200px past the viewport; flex-wrap on .cr-hint and .cmp-actions keeps every compare control inside the container-query column.
  3. V1–V10 verification sweep (QA records tc-1..tc-8 on this PR, tested head 73b4048 unless noted):
Criterion Result Evidence
V1 pytest/ruff/mypy green PASS — 476 passed, 1 skipped at every tip; CI 3/3 on 7ecf77a, 02c16c3, 73b4048 GitHub checks
V2 deep link boots Compare, both chips ready PASS tc-1 (clean re-capture)
V3 Back/Forward + Share reproduces the view PASS tc-2
V4 drawer keeps the state cell live; edits persist PASS tc-3
V5 palette finds shots/lessons/glossary; Esc closes PASS tc-4
V6 uncached-shot load states (fetching → error → Retry/Drop) PASS tc-5
V7 guided-study regression (lesson walkthrough + panel "?") PASS tc-6
V8 capability parity ≤ 2 levels PASS — 72 controls checklist
V9 naming/units greps + visible export-failure surfaces PASS — no "database" labels; MJ everywhere; USD + CSV banners, Compare "Export failed" tc-7 (at 02c16c3 + banner re-verified at tip)
V10 mobile 375px header + all three workspaces usable PASS after 73b4048 — no page-level horizontal scroll tc-8
  1. Known follow-ups (documented, not promotion blockers): 3D auto-orbit and figure-jitter render-loop motion sit outside the prefers-reduced-motion token coverage — render-loop work, not CSS tokens; and the agent panel remains code-only until FUSIONLAB_AGENT_ENABLED + an API key are set (unchanged from the audit).

Human author: Tapan Ganatma Nakkina ([email protected])

🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact

Test Evidence

V10 mobile 375px: header title unclipped, all three workspaces usable in the container-query column, no page-level horizontal scroll after the compare-bar wrap fix
V10 mobile 375px: header title unclipped, all three workspaces usable in the container-query column, no page-level horizontal scroll after the compare-bar wrap fix — result
V10 mobile 375px: header title unclipped, all three workspaces usable in the container-query column, no page-level horizontal scroll after the compare-bar wrap fix — result
V10 mobile 375px: header title unclipped, all three workspaces usable in the container-query column, no page-level horizontal scroll after the compare-bar wrap fix — result

obvious-autobuild Bot and others added 2 commits October 11, 2026 19:10
* feat(router): three workspaces behind one hash grammar

One route() owns which workspace is visible and the URL grammar —
#/replay/{shot}?t= · #/compare/{ids}?ref= · #/explore — with hashchange
and popstate handling, a persisted landing (last visited, else the
welcome-modal pick, else Replay), legacy '#sandbox' and ?shot=/?compare=
deep links kept working, and data-guide anchors plus the window.FusionLab
/ fusionlab:* contracts untouched (showTab is now a hash-writing alias).

The compare chip machine's public hook (FusionLab.replay.compare) lets
the router mount #/compare/{ids}; catalog shot clicks route through the
grammar; the legacy srchSetUrl/cmpUrl URL writers preserve the hash they
used to strip; and a welcome-modal pick no longer stomps a deep-linked
route (it only navigates off the bare default landing).

Mirrors the section-id renames in the UI-contract tests and serves the
new module from the static-assets test.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* style(header): task-mode tabs with ARIA, focus ring, mobile legend collapse

Header tabs become Replay / Compare / Explore with role=tablist/tab,
aria-selected, aria-controls and arrow-key roving focus; the sections
become tab-replay / tab-compare / tab-explore. A --focus-ring token adds
:focus-visible styling app-wide, the disabled ⌘K button stakes the
palette's header slot, and the provenance legend (its tags repeat on
every panel) collapses below 760px so the title stops clipping on
mobile — the audit defect captured in fl_AmO2E2fS. The welcome modal
gains an "Explore the model" pick that persists its landing.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* fix(welcome): keep the welcome pick working across reloads of a bare landing

Reloading a bare landing stranded the welcome-modal pick on Replay: the
boot's own canonicalizing replaceState wrote '#/replay' into the URL, so
on the next boot route() read that app-written hash as an explicit route
and defaultLanding stayed false — the pick stored its landing but never
navigated. Root cause, not symptom: a hash the app itself wrote must not
look like one the user asked for. Leave the bare landing bare; the hash
carries state only once the user navigates (deep links, #sandbox and the
legacy ?shot=/?compare= entry points are unchanged).

Verified in-browser: bare boot -> pick 'Explore the model' -> '#/explore'
applied; reload -> '#/explore' restored, modal suppressed; reload BEFORE
picking -> pick still navigates. pytest 352 passed + 1 skipped, ruff and
mypy clean.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

---------

Co-authored-by: Obvious <[email protected]>
Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
The router PR (#58) already re-homed the what-if sandbox into #tab-explore
with its data-guide anchors intact; this completes the Explore workspace
task (spec What-we-build section 2, L1):

- Explore gains the visible path into the anchored editor: an
  "Edit a real shot instead" link that deep-links to
  #/replay/30166?edit=1 (a real anchor, so Back/Forward and
  Share work through the router's grammar, not around it).
- The router honors edit=1 on #/replay/{shot}: until the drawer
  change owns ?edit=1 (L2), it opens today's anchored what-if on
  the replayed shot via the existing F.replay.whatIf hook - no
  new app path.
- Label-driven lesson/glossary copy: user-facing prose that named
  the old "sandbox" surface now says Explore; the lesson routing
  fields ("tab": "sandbox") and every data-guide anchor name are
  untouched (L3 contract).

Co-authored-by: Obvious <[email protected]>
Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
obvious-autobuild Bot and others added 8 commits October 11, 2026 20:22
…ip (#63)

* feat(shotload): shared shot-load state machine with a visible load chip

Generalize the compare chip fetch machine into web/shotload.js as THE one
shot-load lifecycle (spec L4): queued -> fetching -> ready | error per shot,
AbortController supersede by generation, honest per-shot errors. A superseded
fetch lands back in 'queued' — an abort is not a failure.

Single-shot loading now runs through the machine and renders its states as a
load chip in Replay's status cluster ("loading shot #30192..."; on error the
reason plus Retry/Drop), closing the silent cold-load gap the audit found.
getReplay stores each cached promise with the signal it was created under so a
new-signal fetch never adopts an in-flight promise an older signal is about to
abort, and a failed fetch only evicts its own cache entry.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* refactor(compare): read the compare chips from the shared shot-load machine

The compare fan-out's hand-rolled state Maps (cmp.st/cmp.err/cmp.ctrls) are
gone: per-shot fetch state now lives in web/shotload.js, the same machine a
single pick uses. cmpFetch becomes a thin payload router — supersede and honest
errors are the machine's to record — and Cancel returns in-flight chips to
'queued' instead of marking them 'failed: fetch cancelled', matching the spec's
state table (an abort is a supersede, not a failure). Selection changes drop
the previous batch's machine state, so re-entering compare refetches exactly
as before; payload ownership (cmp.data, replayCache) is unchanged.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

---------

Co-authored-by: Obvious <[email protected]>
Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
…ons, glossary (#65)

Native <dialog> palette (web/palette.js): case-insensitive substring search
over the three workspaces, the shot catalog (/shots, fetched once and cached),
the 43 lesson titles and 47 glossary terms, plus actions — share this view,
OpenUSD export, start/resume the guided study, open the edit drawer, open the
catalog. Keyboard-first (arrows/Enter/Esc), focus trapped by native showModal
and restored to the opener on close; runs every action through existing public
surfaces (router hash grammar, window.FusionLab hooks, replay/app handlers) —
no new app paths, no dependencies, ES5-compatible IIFE.

Companion edits: header ⌘K stub enabled with aria-haspopup/aria-expanded;
palette styles from the existing token set (no new palette); a small additive
window.FusionLab.guided.start(lessonId) hook in guide.js so a lesson item opens
that exact lesson through the same jump the per-panel "?" buttons use.

Co-authored-by: Obvious <[email protected]>
Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
…r Replay (#66)

* feat(edit drawer): anchored what-if becomes a modal drawer over Replay

Replace the cell swap (setWhatIf hiding the state cell's children) with a
right-side <dialog> drawer (~420px, role=dialog, aria-modal): the shot state,
limit gauges and logbook underneath stay live and follow every edit, because
scrub() repaints them from the what-if result without any hiding.

- Entry: the toolbar button is now "Edit this shot" (aria-haspopup=dialog,
  aria-expanded mirrors state); Esc and the drawer's close button both close
  natively, and focus returns to the opening control (wi.opener).
- Sliders start from the measured programme (data-zero), persist across
  close/reopen (persistent DOM + memoized gate), and "Reset edits" restores.
- Gate failure opens the drawer just enough to carry a one-line error with a
  working Retry button, instead of an unopenable hidden panel.
- Drawer styling composes from the token system: --drawer token, anchored to
  #tab-replay, single-column .wi-grid inside.
- ?edit=1 now owns the drawer (workspaces.js comment); lesson 8.1 copy names
  the new label.

data-guide anchors and the window.FusionLab / fusionlab:* contracts unchanged.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* style(toolbar clusters): three labelled groups — transport, shot actions, status

The toolbar row mixed transport, document actions and infrastructure status in
one flat line (audit H1). It now reads in three labelled clusters:

- transport: shot select, play, speed, time scrubber
- shot actions: Edit this shot, OpenUSD, Share (copy this view's URL), Field
  lines (toggle — off stops line fetches and clears them from the 3D panel,
  q95 chips go quiet), Compare… (navigates to the Compare workspace)
- status: load chip, transient flash chip (role=status), compute chip

Share reproduces the current view (explicit hash routes pass through; the bare
landing builds #/replay/{id}?t=SECONDS). The 3D panel gains a visible note
that magnetic-surface reconstruction is lossy — field lines are indicative.
Also restores the drawer CSS block lost from the previous commit's style.css
(the --drawer token had survived, the rules had not). Clusters wrap as units
in the ≤1100px container query. data-guide anchors unchanged.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* fix(usd error surfacing): failed OpenUSD exports reach the banner and status chip, not console only

A failed export previously did console.error and nothing else (audit
state-handling gap): the button's aria-busy was the sole signal, and only
while the request was in flight. Now renderFallback is the single painter
for the notice row — a compute fallback outranks a failed export, which
outranks the onboarding hint; one notice per grid row, with the reason
escaped. The banner takes an error severity (red edge) when it carries the
export failure; the status chip flashes wait → ok/bad through the new
role=status flash element. Dismiss is per-visit; a clean /compute or a
successful export re-arms the row. Recovery paths unchanged.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* fix(units): stored energy in MJ on every Replay surface

The W strip, its live value, the shot-state readout, the compare traces and
the ΔW difference strip all multiplied the MJ data by 1e3 and labelled it kJ,
while the catalog table and the search filters already used MJ — adjacent
surfaces flipped the scale of the same quantity (audit consistency finding).
One unit now: the arrays render directly, the ΔW difference drops the ×1e3,
and the guided-study lessons read 0.01 MJ / 0.13 MJ. The sliceState contract
field W_kJ is unchanged (guided-study surface, L3).

Also: the SHOT ACTIONS cluster could overflow its box and paint over the
status cluster when the toolbar ran tight — the bar and cluster bodies now
wrap as whole units at any width, and the time label stays on one line.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* fix(drawer): anchor the edit drawer to the right edge at full height

The UA stylesheet gives modal dialogs inset:0 and height:fit-content; with a
fixed width, LTR resolution takes the UA's left:0 over the rule's right:0 and
the fixed height defeats the top/bottom stretch — the drawer rendered as a
shrink-wrapped panel at the room's top-left instead of a 420px right sheet.
left:auto and height:auto restore the intended anchor (verified in-browser:
x=1005, w=420, h=900 at 1440x900).

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

---------

Co-authored-by: Obvious <[email protected]>
Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
* feat(compare): the comparison reads in the Compare workspace

Fold the comparison read into #tab-compare: the compare bar, the chips
and a new "Side by side" panel (W, worst limit, dW difference, compared
Thomson profiles) render in the Compare workspace, above the catalog
search and clouds; the Replay room keeps its four single-shot strips
and loses the in-grid compare chrome (bar row, dW strip, cmp-on grid).
Replay's "Compare..." action navigates to #/compare with the shot on
screen preloaded as the reference chip; one-id compare routes mount
the reference and prompt for more; legacy ?compare= links canonicalize
into the hash grammar and land here. The bar gains Share (copies the
URL - the route, filters and selection) and "Retry failed" now refetches
every failed chip; re-pointing the reference to an already-ready shot
repaints the bar so a stale failure header cannot survive the move.

Single-shot picking no longer re-points a live compare - the two
workspaces are independent. The /db failure catch now blanks only the
catalog-clouds panel so the bar, search and side-by-side read survive
a /db outage (JS-07 assertion updated to the targeted handler).

NO SEMANTIC CHANGES to the compare flow: per-shot chip states, abort-
on-supersede, reference semantics, 2-6 shot limit and the shared time
cursor are unchanged; the cursor now spans both workspaces' time panels.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* refactor(catalog): remove the last "database" label from lesson copy

The guided-study prose still said "the machines in that database" for
the scaling-law fits' source catalog; the surface itself is already the
catalog. Lesson tab values and data-guide anchors (db, db-tau, db-ops,
holdout) are untouched - the router normalizes them and the anchors are
the guided-study compatibility contract.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* fix(compare): a one-shot compare shows the reference's curves

startCompare's sub-two-shot early return painted the bar but never drew
the read panel, so a "Compare..." preload or a one-id deep link showed
three empty labelled boxes. Below two shots the read now shows the
reference's own measured curves (W, worst limit, Thomson at the cursor);
the dW difference strip stays hidden until a second shot makes a
difference to read. Verified in the browser against this head.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

---------

Co-authored-by: Obvious <[email protected]>
Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
* chore(copy): finish the sandbox → Explore label sweep

The sibling PRs renamed the surfaces in the lessons themselves (0.2, 0.3,
4.8, 7.1, 8.1) but left three "sandbox" stragglers in web/: two comments
(figures.js preset note, guide.js lab-help note) and one user-facing
string (the agent's protocol-accept confirmation). Part of the V9
final sweep; no behavior change.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* feat(a11y): one focus token, reduced-motion collapses panel motion

- style.css: repair the tail left by a sibling merge (a stray "auto; }"
  fragment and duplicated .srch-rows/.bad/Explore/mobile-header rules);
  drop the one hardcoded :focus-visible override so every interactive
  element shares the --focus-ring token; add @media
  (prefers-reduced-motion: reduce) collapsing the two orientation
  tweens (gauge bars, spotlight dim).
- guide.js, replay.js: the two smooth scrolls (spotlight, catalog panel
  jump) jump instantly under the same query — the preference otherwise
  could not reach them from CSS.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

* fix(redundancy): the insight paragraph references the limits, not their numbers

Limit proximity was rendered four times on one screen: the three gauges,
the limits strip, the strip value's color, and the insight prose — the
prose being the third restatement of the same numbers. Keep the gauges
(the instant) and the limits strip (the run); the prose now names which
limit is closest and points at those two. Lesson 5.5, which described
the panel's prose, drops "and when" to match.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>

---------

Co-authored-by: Obvious <[email protected]>
Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
…redesign-20261011-170103

# Conflicts:
#	web/replay.js
The merge brought main's CSV export with a console-only failure path —
the exact pattern the release fixed for OpenUSD. dlExport now takes an
onErr callback; the Replay caller renders the fallback banner + status
flash, the Compare caller uses the bar's button-text idiom. usdErr/
usdSeen renamed to exportErr/exportSeen since the banner now carries
both export kinds.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
Human author: Tapan Ganatma Nakkina ([email protected])
…dths

At 375px the compare bar (.cr-hint) and its action buttons could not
wrap, pushing the page 200px past the viewport (the catalog table
scrolls in .srch-rows, so it was never the offender). flex-wrap on the
bar and on .cmp-actions keeps every compare control inside the
container-query column — the V10 acceptance criterion.

Co-authored-by: Tapan Ganatma Nakkina <[email protected]>
Human author: Tapan Ganatma Nakkina ([email protected])
@obvious-autobuild
obvious-autobuild Bot marked this pull request as ready for review October 12, 2026 00:26
@obvious-autobuild
obvious-autobuild Bot requested a review from ganatma October 12, 2026 02:45

@ganatma ganatma left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Obvious

@obvious-autobuild
obvious-autobuild Bot merged commit 4c8d1d8 into main Oct 12, 2026
3 checks passed
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.

2 participants