Skip to content

refactor(ui)!: remove the classic v2 shell — v3 is the only UI (R3a) - #871

Merged
byrongamatos merged 2 commits into
mainfrom
feat/r3-v2-removal
Jul 11, 2026
Merged

refactor(ui)!: remove the classic v2 shell — v3 is the only UI (R3a)#871
byrongamatos merged 2 commits into
mainfrom
feat/r3-v2-removal

Conversation

@byrongamatos

@byrongamatos byrongamatos commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Step 0 of the core-frontend ES-module migration (R3a). Deletes the legacy v2 shell so the rest of the migration only has to be made — and verified — once.

Why this goes first

/ has served static/v3/index.html since 0.3.0; static/index.html is the legacy shell behind /v2 / FEEDBACK_UI=v2. Both shells load the same static/app.js. So every later R3a step — exposing app.js's window.* contract, the defer ordering fix, the type="module" flips — would otherwise land in two HTML files and be booted twice. Removing the fallback now halves that surface before any of it is touched.

What's in it

  • Delete static/index.html; index() serves v3 unconditionally; /v2 route and the FEEDBACK_UI v2/legacy branch removed. /v3 stays as a back-compat alias.
  • Latent bug fixed on the way out. The old guard read if getenv_compat("FEEDBACK_UI") or getenv_compat("FEEDBACK_UI") in ("v2", "legacy") — the left operand is truthy for any non-empty value, so FEEDBACK_UI=v3 actually served the v2 shell.
  • static/tailwind.min.css regenerated. The content globs (./static/**/*.{html,js}) scanned the deleted file, so v2-only utility classes are purged — without this the tailwind-fresh CI job (which rebuilds and git diff --quiets the committed CSS) goes red.
  • Constitution → 1.3.0: Principle II's frontend file list now names static/v3/index.html. Governance requires the amendment to ride with the code change, plus the CHANGELOG migration note and the CLAUDE.md update — all here.
  • Docs de-flagged (plugin-v3-ui.md, plugin-styles.md, CLAUDE.md, the v3 shell's own header comment) — they still described v3 as opt-in.

Tests

4 suites read the v2 shell, not 1 — three build the path via path.join(__dirname,'..','..','static','index.html'), which a literal grep for static/index.html never sees. Worth knowing for the next person auditing consumers.

File Change
tests/test_plugin_runtime_idempotence.py retarget → v3 (this is the capability-registry-before-app-runtime script-order test — the invariant the later defer/module steps hinge on, so it's kept and retargeted, not dropped)
tests/js/alpha_warning_banner.test.js retarget → v3 (identical #library-section + banner markup)
tests/js/song_restart.test.js, tuning_display.test.js, tuning_targets.test.js drop the V2 halves — each is a paired duplicate of a V3 test that stays
tests/test_env_compat.py used FEEDBACK_UI only as an arbitrary subject for the SLOPSMITH_*FEEDBACK_* shim; retargeted to FEEDBACK_DEMO_MODE, assertions unchanged

Verification

  • pytest 2395 passed, 4 skipped. npm run test:js 1028/1028.
  • ESLint: 0 errors (9 pre-existing max-lines warnings, unchanged).
  • Live uvicorn: / → 200 (v3 markers present), /v3 → 200, /v2 → 404.
  • Headless browser boot: bus live (feedBack.on/emit), capabilities.version === 1, showScreen/playSong/loadPlugins all present, library renders. Zero real console errors — the only 4xx are three /api/song/<id>/art 404s, which are the handler's own {"error":"no art"} for seeded packs that genuinely ship no cover art (pre-existing, unrelated).
  • R0 plugin rails intact: stems/studio/editor register with script_type=module + min_host, /api/plugins/stems/src/main.js → 200, conditional GET → 304, deeper src/transport.js → 200.
  • Codex preflight: 0 issues.

Migration

FEEDBACK_UI=v2 / =legacy and /v2 are gone — unset the variable and use /. No chart, settings, or plugin data changes and no plugin API changes; v3 reuses the same engine (app.js, highway.js, playSong, showScreen, the capability registry).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • The redesigned v3 interface is now the default and only supported UI.
    • Both / and /v3 serve the v3 interface.
  • Removed
    • Removed the classic v2 interface, the /v2 route, and legacy UI-selection options.
    • Plugins no longer need to support multiple UI versions.
  • Bug Fixes
    • Fixed UI-selection behavior where legacy settings could cause the wrong interface to load.
  • Documentation
    • Updated UI, plugin, migration, and project guidance to reflect v3-only support.
  • Tests
    • Updated browser checks to validate the v3 interface.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The v3 shell is now served from / and retained at /v3; the v2 shell and related fallback behavior are removed. Documentation, styling references, and tests are updated to reflect the v3-only frontend.

Changes

v3 UI default

Layer / File(s) Summary
Route and shell transition
server.py, static/v3/index.html, static/app.js, static/highway.js, static/tailwind.min.css
The root route always serves the v3 shell, /v3 remains as a back-compat alias, the classic shell is removed, and frontend asset references and compiled styles are updated.
Project guidance and release records
.specify/memory/constitution.md, CHANGELOG.md, CLAUDE.md, docs/plugin-styles.md, docs/plugin-v3-ui.md, tailwind.config.js
Project metadata, changelog entries, guidance, plugin documentation, and Tailwind comments describe v3 as the only UI and document removal of v2 paths and flags.
Frontend and environment test alignment
tests/js/*, tests/test_env_compat.py, tests/test_plugin_runtime_idempotence.py
Tests use the v3 HTML fixture, validate v3 transport markup, remove v2-only checks, and cover demo-mode environment compatibility behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main breaking change: removing the classic v2 shell and making v3 the only UI.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/r3-v2-removal

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

Deletes `static/index.html`, the `/v2` route, and the `FEEDBACK_UI` v2/legacy
opt-out. `/` and `/v3` both serve `static/v3/index.html`, which has been the
default since 0.3.0.

This is step 0 of the core-frontend ES-module migration (R3a). Both shells load
the same `static/app.js`, so every later step of that migration — exposing the
window contract, the `defer` ordering fix, the `type="module"` flips — would
otherwise have to be made and verified twice. Removing the fallback now halves
that surface before any of it is touched.

Incidentally fixes a latent bug in `index()`: its guard read
`if getenv_compat("FEEDBACK_UI") or getenv_compat("FEEDBACK_UI") in ("v2", "legacy")`,
whose left operand is truthy for *any* non-empty value — so `FEEDBACK_UI=v3`
actually served the **v2** shell.

- `static/tailwind.min.css` regenerated: the content globs scanned the deleted
  file, so v2-only utility classes are now purged (CI's tailwind-fresh job
  rebuilds and diffs it).
- Constitution amended to 1.3.0 — Principle II's frontend file list now names
  `static/v3/index.html`.
- Tests: 4 suites read the v2 shell (3 via a constructed `path.join` that a
  literal grep misses). Their v2 halves are paired duplicates of v3 tests that
  stay, so they are dropped; `alpha_warning_banner` and the capability-registry
  script-order test retarget to `static/v3/index.html`.

BREAKING CHANGE: `FEEDBACK_UI=v2` / `=legacy` and the `/v2` route are gone.
Unset the variable and use `/`. No chart, settings, or plugin data changes, and
no plugin API changes — v3 reuses the same engine.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 128-134: Align the plugin documentation with the v3-only route
contract: in CLAUDE.md, update the checklist to verify both / and /v3 as v3
entrypoints; in docs/plugin-v3-ui.md, remove the obsolete wording that labels /
as v2. No code changes are required.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 88f43574-5421-4189-bae1-c349e915ed8e

📥 Commits

Reviewing files that changed from the base of the PR and between 5e30138 and ef2cead.

📒 Files selected for processing (18)
  • .specify/memory/constitution.md
  • CHANGELOG.md
  • CLAUDE.md
  • docs/plugin-styles.md
  • docs/plugin-v3-ui.md
  • server.py
  • static/app.js
  • static/highway.js
  • static/index.html
  • static/tailwind.min.css
  • static/v3/index.html
  • tailwind.config.js
  • tests/js/alpha_warning_banner.test.js
  • tests/js/song_restart.test.js
  • tests/js/tuning_display.test.js
  • tests/js/tuning_targets.test.js
  • tests/test_env_compat.py
  • tests/test_plugin_runtime_idempotence.py
💤 Files with no reviewable changes (4)
  • tests/js/tuning_targets.test.js
  • tests/js/song_restart.test.js
  • static/index.html
  • tests/js/tuning_display.test.js

Comment thread CLAUDE.md
The v3-only rewrite updated the intro paragraphs but left three lines that
still instructed plugin authors to verify in 'both / (v2) and /v3' — now the
same shell. Historical 'in v2 it was X' contrasts are kept: they still orient
authors whose plugins also ship to users on older cores.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos
byrongamatos merged commit 9d0bf95 into main Jul 11, 2026
5 checks passed
byrongamatos added a commit that referenced this pull request Jul 11, 2026
One attribute. #871/#872/#874/#875 exist to make this line safe.

app.js's 385 top-level `function` declarations stop being implicit `window`
properties: 87 stay reachable via the explicit contract (#874's Object.assign
block + the 47 pre-existing `window.X = X` assignments), and 298 become
module-private. Verified NO unexposed name is read from outside app.js.

Strict mode (modules are always strict) checked ahead of the flip: app.js parses
clean as `sourceType: module` (no octal, dup params, `with`), and has no implicit
globals, no `eval`/`new Function`, no top-level `this`. `registerShortcut` is
called bare at 15 top-level sites but is assigned at `window.registerShortcut`
(app.js:10387) before its first call (10648), and a bare identifier in a module
still resolves through the global object — verified `typeof
window.registerShortcut === 'function'` in the browser.

HARD GATE — app.js IS the plugin loader:
  - /api/plugins script_type passthrough: editor/stems/studio = "module"
  - /api/plugins/stems/src/main.js -> 200; conditional GET -> 304 (live-edit ETag)
  - deep graph: stems/src/transport.js, editor/src/state.js -> 200
  - window.loadPlugins present; 5 plugin screens mount; the 3 migrated plugins
    injected as <script type="module">
  - 37 capability participants, 14 compatibility shims, bus + capabilities v1

Every one of the shell's 336 inline handlers resolves on window under module
scope, and the A-Z rail / pagination execute 6/6 with no ReferenceError. A/B
against origin/main: the ONLY unresolvable handler is `editorToggleStemMixer`,
which is equally broken on main (a dead handler in the editor plugin — not
defined anywhere in its source; pre-existing, flagged separately).

Codex preflight raised a [P1] claiming restartCurrentSong / requestExitSong /
editRegionInEditor / returnToEditorFromHighway would ReferenceError — FALSE
POSITIVE. It scanned only #874's new Object.assign block and missed app.js's 47
scattered `window.X = X` assignments; all four are at app.js:7086/7204/8492/8511
and all four resolve as `function` in the browser with app.js loaded as a module.

pytest 2396, node 1032/1032, ESLint 0 errors, tailwind-fresh clean.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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