Four-commit release. The CORS fix is the main deploy-blocker — without it the Firefox extension can't talk to FC at all (the operator hit this during the first install).
c7001f4 — fix(extension): CORS preflight for moz-extension:// + chrome-extension://
The extension's Test connection returned a generic NetworkError because /api/credentials POSTs with X-Extension-Key trigger a browser preflight OPTIONS request. Quart had no OPTIONS handler and no Access-Control-Allow-* response headers, so the preflight 405'd before the actual request. Browser blocked the fetch with no useful error.
Fix: two app-level hooks in create_app():
@app.before_request short-circuits OPTIONS from moz-extension:// / chrome-extension:// origins with 204
Whitelist is intentionally narrow (extension schemes only) — normal browser usage doesn't get permissive CORS. Five integration tests pin the contract.
85b640f — fix(views): close the 24-32px gap below TopNav across all 12 views
Every view's root <v-container> used py-6 (24px top + bottom), pushing the first content item well below where TopNav's fade-to-transparent gradient bottoms out. Operator-flagged the empty space. Switched to pt-2 pb-6 (8px top, 24px bottom unchanged) across all 12 views. PlaceholderView kept its larger bottom padding via pt-3 pb-8.
f827612 — fix(artist-header): close gap below TopNav + center the tab strip
ArtistHeader's top: 64px left a visible gap because TopNav's actual rendered height is ~48px (0.75rem padding + ~24px content). Dropped to top: 48px so the two bars sit flush. Added a right-side spacer cell so the tab strip is geometrically centered (1fr | auto | 1fr layout, mirroring TopNav).
ci.yml: dropped the pull_request: trigger — push: branches: [dev, main] already covered the same code; pull_request was duplicating CI runs on every dev push with an open PR.
build.yml: dropped dev from push triggers (operator confirmed they don't use :dev), added tags: ['v*'] trigger + Determine-tag logic for immutable per-version images (:v26.05.26.5). Extended the XPI-download step to fire on tag pushes so versioned images still carry the signed extension.
Net per hotfix cycle: 5 CI runs → 3 (no tag) / 4 (with tag).
Test plan
Deploy :latest
Extension Test connection from about:addons → expect green "Connected" instead of NetworkError
Browse any view — gap between TopNav and first content is ~8px, not ~24px
Artist view header sits flush against TopNav; tabs centered horizontally
Bonus: the v26.05.26.5 tag-push should now produce :v26.05.26.5 immutable image alongside :latest
## Summary
Four-commit release. The CORS fix is the main deploy-blocker — without it the Firefox extension can't talk to FC at all (the operator hit this during the first install).
### `c7001f4` — fix(extension): CORS preflight for moz-extension:// + chrome-extension://
The extension's `Test connection` returned a generic `NetworkError` because `/api/credentials` POSTs with `X-Extension-Key` trigger a browser preflight `OPTIONS` request. Quart had no `OPTIONS` handler and no `Access-Control-Allow-*` response headers, so the preflight 405'd before the actual request. Browser blocked the fetch with no useful error.
Fix: two app-level hooks in `create_app()`:
- `@app.before_request` short-circuits `OPTIONS` from `moz-extension://` / `chrome-extension://` origins with 204
- `@app.after_request` stamps `Access-Control-Allow-Origin` (mirroring the request origin), `Allow-Methods`, `Allow-Headers: Content-Type, X-Extension-Key`, `Max-Age: 86400`
Whitelist is intentionally narrow (extension schemes only) — normal browser usage doesn't get permissive CORS. Five integration tests pin the contract.
### `85b640f` — fix(views): close the 24-32px gap below TopNav across all 12 views
Every view's root `<v-container>` used `py-6` (24px top + bottom), pushing the first content item well below where TopNav's fade-to-transparent gradient bottoms out. Operator-flagged the empty space. Switched to `pt-2 pb-6` (8px top, 24px bottom unchanged) across all 12 views. `PlaceholderView` kept its larger bottom padding via `pt-3 pb-8`.
### `f827612` — fix(artist-header): close gap below TopNav + center the tab strip
ArtistHeader's `top: 64px` left a visible gap because TopNav's actual rendered height is ~48px (0.75rem padding + ~24px content). Dropped to `top: 48px` so the two bars sit flush. Added a right-side spacer cell so the tab strip is geometrically centered (1fr | auto | 1fr layout, mirroring TopNav).
### `3f0153c` — ci(workflows): dedupe + versioned image tags
- `ci.yml`: dropped the `pull_request:` trigger — push: branches: [dev, main] already covered the same code; pull_request was duplicating CI runs on every dev push with an open PR.
- `build.yml`: dropped `dev` from push triggers (operator confirmed they don't use `:dev`), added `tags: ['v*']` trigger + Determine-tag logic for immutable per-version images (`:v26.05.26.5`). Extended the XPI-download step to fire on tag pushes so versioned images still carry the signed extension.
Net per hotfix cycle: 5 CI runs → 3 (no tag) / 4 (with tag).
## Test plan
- [ ] Deploy `:latest`
- [ ] Extension Test connection from `about:addons` → expect green "Connected" instead of NetworkError
- [ ] Browse any view — gap between TopNav and first content is ~8px, not ~24px
- [ ] Artist view header sits flush against TopNav; tabs centered horizontally
- [ ] Bonus: the v26.05.26.5 tag-push should now produce `:v26.05.26.5` immutable image alongside `:latest`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Four-commit release. The CORS fix is the main deploy-blocker — without it the Firefox extension can't talk to FC at all (the operator hit this during the first install).
c7001f4— fix(extension): CORS preflight for moz-extension:// + chrome-extension://The extension's
Test connectionreturned a genericNetworkErrorbecause/api/credentialsPOSTs withX-Extension-Keytrigger a browser preflightOPTIONSrequest. Quart had noOPTIONShandler and noAccess-Control-Allow-*response headers, so the preflight 405'd before the actual request. Browser blocked the fetch with no useful error.Fix: two app-level hooks in
create_app():@app.before_requestshort-circuitsOPTIONSfrommoz-extension:///chrome-extension://origins with 204@app.after_requeststampsAccess-Control-Allow-Origin(mirroring the request origin),Allow-Methods,Allow-Headers: Content-Type, X-Extension-Key,Max-Age: 86400Whitelist is intentionally narrow (extension schemes only) — normal browser usage doesn't get permissive CORS. Five integration tests pin the contract.
85b640f— fix(views): close the 24-32px gap below TopNav across all 12 viewsEvery view's root
<v-container>usedpy-6(24px top + bottom), pushing the first content item well below where TopNav's fade-to-transparent gradient bottoms out. Operator-flagged the empty space. Switched topt-2 pb-6(8px top, 24px bottom unchanged) across all 12 views.PlaceholderViewkept its larger bottom padding viapt-3 pb-8.f827612— fix(artist-header): close gap below TopNav + center the tab stripArtistHeader's
top: 64pxleft a visible gap because TopNav's actual rendered height is ~48px (0.75rem padding + ~24px content). Dropped totop: 48pxso the two bars sit flush. Added a right-side spacer cell so the tab strip is geometrically centered (1fr | auto | 1fr layout, mirroring TopNav).3f0153c— ci(workflows): dedupe + versioned image tagsci.yml: dropped thepull_request:trigger — push: branches: [dev, main] already covered the same code; pull_request was duplicating CI runs on every dev push with an open PR.build.yml: droppeddevfrom push triggers (operator confirmed they don't use:dev), addedtags: ['v*']trigger + Determine-tag logic for immutable per-version images (:v26.05.26.5). Extended the XPI-download step to fire on tag pushes so versioned images still carry the signed extension.Net per hotfix cycle: 5 CI runs → 3 (no tag) / 4 (with tag).
Test plan
:latestabout:addons→ expect green "Connected" instead of NetworkError:v26.05.26.5immutable image alongside:latest🤖 Generated with Claude Code