Skip to content

WZ: Restructure the three apps for code quality - #56

Merged
codebend3r merged 5 commits into
mainfrom
code-quality-restructure
Sep 21, 2026
Merged

codebend3r merged 5 commits into
mainfrom
code-quality-restructure

Conversation

@codebend3r

Copy link
Copy Markdown
Owner

A behaviour-preserving restructure across all three apps, from a full review of main. 43 findings, 40 applied, 3 deliberately skipped.

Baseline

  • the root format gate was failing on main: oxfmt discovered the admin-portal config as a nested root and formatted that app under the root pass, so the documented apps ignore was never in effect. The pass now runs with --disable-nested-config.

admin-portal

  • the sign-out-on-401 rule moved into one query client cache handler, deleting thirteen per-call guards, four load effects and the useAdminAuth hook
  • User.tsx went from 1033 lines to 134, split into MemberDetails, MemberControls, MemberHistory, PendingActionModal and a useMemberActions hook, with its stylesheet split the same way
  • seven pending-action states and six modal blocks became one PendingAction union and one modal
  • TierMenuButton, Spinner, InviteResultNotice, AsyncSection, ViewTabs and a shared Chart set each replace two to four copies
  • MetricChart.tsx went from 690 lines to 283 by extracting metricFrame.ts and timeAxis.ts
  • ten fetch-validate-throw blocks collapsed into one typed readMonitor reader, and the runtime guards live in lib/guards.ts

stripe-bridge

  • the 190-line webhook if/elif became a table of one handler per event type, with the idempotency ordering lifted unchanged
  • new config.py, invites.py, roster.py and alerts.py; admin.py went from 750 lines to 523
  • store.py migrations, placeholder writes and connection closing each happen in one place now

fleet-monitor

  • plays.py (1668 lines) is now a package of four modules plus a re-exporting __init__, so no call site changed
  • the shared query is materialised once per request instead of being re-evaluated in up to seven statements
  • fleet judgement moved out of api.py into fleet.py, cutting it from 748 lines to 381

Skipped, with reasons

  • deleting the unlinked ResetUser page: dead by every static signal but reachable by URL, so it wants your sign-off
  • moving the webhook off the event loop: correct in isolation, but it lets two deliveries of one event race past the idempotency check, so it belongs in its own PR with claim-first idempotency
  • parallelising in-host Plex reads: the sequencing is a documented load constraint, not an oversight

Verification

  • bun run verify green with 0/9 cache hits: root oxlint and oxfmt, then lint, SCSS lint, ruff, format check, typecheck and tests for all three projects
  • 526 admin-portal tests, 338 bridge tests, 432 monitor tests
  • the CI sanity-check import passes and both webhook routes are still registered

@netlify

netlify Bot commented Sep 20, 2026

Copy link
Copy Markdown

Deploy Preview for westeroz ready!

Name Link
🔨 Latest commit 2aec3ff
🔍 Latest deploy log https://app.netlify.com/projects/westeroz/deploys/6ab12d5719e3c20008fe6968
😎 Deploy Preview https://deploy-preview-56--westeroz.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

- run the root oxfmt pass with --disable-nested-config so the admin-portal config no longer pulls that app's files into the root check
- reformat the income hero change line by deriving one change object instead of a two-part guard with a comment inside the expression
- add lib/guards.ts as the one home for the runtime type guards every API module was declaring locally
- split plays.py (1668 lines) into a plays package: base, ledger, views and never_played, with __init__ re-exporting the public surface so no call site changes
- materialise the shared query as a temp table once per request instead of naming a window-function CTE in up to seven statements
- share one paged-plays query between user history and title history
- generate the quality CASE expressions from QUALITY_RANK so the ladder is written once
- move fleet judgement out of api.py into fleet.py, and metric history into series.py, cutting api.py from 748 to 381 lines
- read every env var through config, including the two auth reads
- drop the docker probe fields the parser documented as never populated, and the unreferenced ProbeFailure type
- move the sign-out-on-401 rule into one query client cache handler, deleting thirteen per-call guards, four load effects and the useAdminAuth hook
- split User.tsx from 1033 lines to 134 across MemberDetails, MemberControls, MemberHistory, PendingActionModal and a useMemberActions hook, with the stylesheet split the same way
- replace seven pending-action states and six modal blocks with one PendingAction union and one modal
- add lib/memberQueries.ts as the one home for the member query keys, options and optimistic cache patching
- extract TierMenuButton, Spinner and InviteResultNotice, each of which existed two or three times
- make the invite label rule one helper so the member page and the table agree that a member in dunning reads Re-invite
- route fetchMember through requestJson via a NotFoundError so it keeps the shared response guards
- derive the design reference status and tag lists from the model instead of hardcoding stale counts
- replace the two copies of AsyncSection and the three error helpers with one shared component, and let the fleet charts block be a plain section again
- merge ChartTabs into one ViewTabs component with an optional action, so the two tab strips stop duplicating their keyboard handling
- add components/Chart: one stylesheet, one tooltip, one legend, one frame and one scale, replacing four tooltips, three stylesheets and three margin constants
- split MetricChart from 690 lines to 283 by extracting metricFrame and timeAxis, and drop the while loop from the carry-forward pass
- collapse ten fetch-validate-throw blocks into one typed readMonitor reader, and import the shared guards
- narrow the history row kind to the union the monitor already sends, so the hand-written re-narrowing goes away
- derive the default play range from the range table, and write the url params from a table instead of a mutating closure
- share the page-count helper and the pager sandwich across the three paged panels
- replace the 190-line webhook if/elif with a table of one handler per Stripe event type, leaving the idempotency ordering untouched
- add config.py so the six env vars and the Wizarr client are read and built once, instead of four modules each doing it with different strictness
- add invites.py for the scope-then-mint pipeline the webhook, the admin reissue and the baseline sweep each had their own copy of
- move the pure member assembly into roster.py and reuse it in get_member, cutting admin.py from 750 to 523 lines
- collect every operator alert subject and body in alerts.py so the copy audit has one file to read
- fold the four ensure-column helpers and the three update-or-placeholder blocks in store.py into one of each, and close connections through a context manager
- type customer and member rows instead of reading them through get-or-empty chains
- share one scripts/py-tool.sh between both Python apps, and declare it as a cache input for lint:py and test
- update CLAUDE.md, the reconcile skill, the sales-agent comment and the billing doc to the modules that now own this logic
@codebend3r
codebend3r force-pushed the code-quality-restructure branch from cf8c3b5 to 2aec3ff Compare September 21, 2026 13:12
@codebend3r
codebend3r merged commit 70927c9 into main Sep 21, 2026
5 checks passed
@codebend3r
codebend3r deleted the code-quality-restructure branch September 21, 2026 13:14
codebend3r added a commit that referenced this pull request Sep 21, 2026
A behaviour-preserving restructure across all three apps, from a full
review of `main`. 43 findings, 40 applied, 3 deliberately skipped.

**Baseline**

- the root format gate was failing on `main`: oxfmt discovered the
admin-portal config as a nested root and formatted that app under the
root pass, so the documented `apps` ignore was never in effect. The pass
now runs with `--disable-nested-config`.

**admin-portal**

- the sign-out-on-401 rule moved into one query client cache handler,
deleting thirteen per-call guards, four load effects and the
`useAdminAuth` hook
- `User.tsx` went from 1033 lines to 134, split into `MemberDetails`,
`MemberControls`, `MemberHistory`, `PendingActionModal` and a
`useMemberActions` hook, with its stylesheet split the same way
- seven pending-action states and six modal blocks became one
`PendingAction` union and one modal
- `TierMenuButton`, `Spinner`, `InviteResultNotice`, `AsyncSection`,
`ViewTabs` and a shared `Chart` set each replace two to four copies
- `MetricChart.tsx` went from 690 lines to 283 by extracting
`metricFrame.ts` and `timeAxis.ts`
- ten fetch-validate-throw blocks collapsed into one typed `readMonitor`
reader, and the runtime guards live in `lib/guards.ts`

**stripe-bridge**

- the 190-line webhook if/elif became a table of one handler per event
type, with the idempotency ordering lifted unchanged
- new `config.py`, `invites.py`, `roster.py` and `alerts.py`; `admin.py`
went from 750 lines to 523
- `store.py` migrations, placeholder writes and connection closing each
happen in one place now

**fleet-monitor**

- `plays.py` (1668 lines) is now a package of four modules plus a
re-exporting `__init__`, so no call site changed
- the shared query is materialised once per request instead of being
re-evaluated in up to seven statements
- fleet judgement moved out of `api.py` into `fleet.py`, cutting it from
748 lines to 381

**Skipped, with reasons**

- deleting the unlinked `ResetUser` page: dead by every static signal
but reachable by URL, so it wants your sign-off
- moving the webhook off the event loop: correct in isolation, but it
lets two deliveries of one event race past the idempotency check, so it
belongs in its own PR with claim-first idempotency
- parallelising in-host Plex reads: the sequencing is a documented load
constraint, not an oversight

**Verification**

- `bun run verify` green with 0/9 cache hits: root oxlint and oxfmt,
then lint, SCSS lint, ruff, format check, typecheck and tests for all
three projects
- 526 admin-portal tests, 338 bridge tests, 432 monitor tests
- the CI sanity-check import passes and both webhook routes are still
registered
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