## Why
Stripe checkout used Stripe-only tables. This extracts a shared payment
CORE. New checkout writes `payment_order`. Old Stripe tables stay so
in-progress Sessions can still settle.
This PR is [1/2]. [#2368](https://github.com/moeru-ai/airi/pull/2368) is
[2/2]. That PR archives leftover Stripe tables after in-progress
Sessions finish or expire.
## Changes
- Add `payment_order` and `provider_account`.
- Copy `stripe_checkout_session` into `payment_order`.
- Copy `stripe_customer` into `provider_account`.
- Keep `stripe_*` tables and `user_flux.stripe_customer_id`.
- New checkout writes `payment_order` and stores
`metadata.payment_order_id`.
- Webhook resolves new Sessions by `metadata.payment_order_id`.
- Webhook resolves older Sessions by `provider_order_id`, then by a
leftover `stripe_checkout_session` row.
- That leftover-row lookup covers Sessions opened before this deploy,
and rows written while 0023 runs.
[#2368](https://github.com/moeru-ai/airi/pull/2368) deletes it after
those Sessions finish or expire.
## Test plan
- [x] `pnpm exec vitest run
server/apps/api/src/services/domain/payment/tests/payment.test.ts
server/apps/api/src/routes/stripe`
- [x] `pnpm -F @proj-airi/api-server typecheck`
- [x] `git diff --check`
## Visual changes
No user-visible changes.
## Open: settle after account deletion
Account deletion stamps `payment_order.deletedAt`. A Checkout Session
that is still open can still pay after that stamp. This PR keeps `main`
behavior. `settle` loads the row by id, including a soft-deleted row,
then marks it `paid` and credits Flux.
Follow-up policy: if `deletedAt` is set, skip. Do not update the archive
row. Do not credit Flux. Do not insert a live `provider_account`. Stripe
remains the payment record. Skip matches the soft-delete rule: a deleted
row is gone, not write it again.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: RainbowBird <git@luoling.moe>
# Refactor: Move Provider Tests to provider-inference
## Summary
Move azure-openai and openrouter-ai provider tests from `stage-ui` to
`provider-inference` package, placing them closer to their
implementations.
## Changes
- **Moved test files**:
-
`packages/stage-ui/src/libs/providers/providers/azure-openai/index.test.ts`
→
`packages/provider-inference/src/providers/cloud/azure-openai/index.test.ts`
-
`packages/stage-ui/src/libs/providers/providers/openrouter-ai/index.test.ts`
→
`packages/provider-inference/src/providers/cloud/openrouter-ai/index.test.ts`
- **Simplified test imports**: Updated tests to use direct provider
definitions instead of `getDefinedProvider()`, removing unnecessary
lookup logic
- **Added dependency**: `@proj-airi/core-agent` as devDependency for
test utilities
## Benefits
1. Tests are co-located with their implementations
2. Direct imports are simpler and more maintainable
3. Better package organization following module boundaries
## Verification
```bash
# Run tests for provider-inference
pnpm -F @proj-airi/provider-inference exec vitest run
# Typecheck
pnpm -F @proj-airi/provider-inference typecheck
```
## Test Results
All tests pass after the refactoring.
## Summary
- Auth and API origin policy now trusts `*.moeru-ai.workers.dev`.
- The retired `*.kwaa.workers.dev` suffix is removed. Server-dev
stage-web previews on the moeru-ai Cloudflare account can complete OIDC
login.
## Test plan
- [x] `pnpm exec vitest run server/apps/auth/src/tests/auth.test.ts
server/apps/auth/src/tests/origin.test.ts
server/apps/api/src/utils/tests/origin.test.ts`
- [ ] After deploy, sign in from
https://server-dev-moeru-ai-airi.moeru-ai.workers.dev and confirm
`/auth/callback` is accepted.
## Visual changes
No user-visible changes.
Made with [Cursor](https://cursor.com)
Co-authored-by: Cursor <cursoragent@cursor.com>
## Summary
The desktop browser suite can mount `InteractiveArea` before its utility
styles settle, then scroll history while the reply composer is still
expanding. A pending Virtua tail scroll can replace the message that the
test selected and make the scrollport assertion fail.
This addresses the pre-existing browser-test failure reported during
#2506 ([CI
failure](https://github.com/moeru-ai/airi/actions/runs/34448585692/job/102804067154)).
- Scan desktop and shared Vue components before the initial test
stylesheet is served. Load the desktop UnoCSS config explicitly so
config discovery does not replace the scan.
- Give each fixture a window-sized host, reset the viewport, and dispose
the component, host, and Pinia after each test.
- Wait for the reply transition and virtual-list measurements to settle
before scrolling. Cover both 200 ms and 1 s transitions, and assert the
requested scroll position throughout the existing message-mount checks.
Only browser tests and their configuration change.
## Validation
The complete desktop browser suite passed with three shuffled seeds: **6
files, 65 tests per run**, with no skipped tests.
```sh
for seed in 2508 2506 2507; do
npm_config_registry=https://registry.npmjs.org/ NODE_OPTIONS=--no-experimental-webstorage \
pnpm exec vitest run --config apps/stage-tamagotchi/vitest.config.ts \
--project browser --sequence.shuffle --sequence.seed="$seed" --bail=1
done
npm_config_registry=https://registry.npmjs.org/ pnpm run typecheck
npm_config_registry=https://registry.npmjs.org/ pnpm run lint
git diff --check
```
Typecheck passed all 52 tasks. Lint passed with existing repository
warnings. ResizeObserver loop warnings remain visible; this change does
not suppress them. Linux CI validation is pending.
## Description
<!-- Please insert your description here and especially provide info
about the "what" this PR is solving -->
### As-is
Depending on the size, the Controls Island area gets cut off, making the
buttons in the cropped area inaccessible.
### To-be
The Controls Island is managed via scrolling, allowing interaction with
buttons regardless of the size.
If the main controls also exceed the available space, the entire
Controls Island becomes scrollable. Horizontal scrolling is available
for narrow windows.
#### narrow & smaill
https://github.com/user-attachments/assets/1a44ff4f-8fe2-4a05-96a8-59ebf7dafcff
#### somewhat generous size
https://github.com/user-attachments/assets/c73dbee4-8c8f-4937-ae1a-eda00178274c
## Linked Issues
<!-- Optional, if you have any -->
close#2400
## Additional Context
<!-- e.g. is there anything you'd like reviewers to focus on? -->
Although the size is somewhat less than ideal, the issue has been
resolved, and this serves as a universal solution.
---------
Co-authored-by: leafyy <wuqizq@outlook.com>
## Description
The Unit Test job reaches stage-ui, then fails and stalls in browser
tests. The same failures occur on the parent commit.
- Use the real consciousness store and Pinia refs in context bridge
tests. Check that input requests preserve temperature and top-p.
- Dispose each bridge and Pinia scope after every test, including failed
assertions. This prevents old listeners from receiving later stream
events.
- Create translated module stores during component setup and await card
initialization before drawer interactions. Dispose the fixture stores
after each test.
- Remove the Pinia and Mutex replacements from the context bridge
fixture.
This change only updates two test files.
## Verification
The original fixture reproduced three failures in the two affected
files. The updated files pass all 17 tests. The complete CI test command
passes 2,534 tests, with one skipped test. Stage-ui passes all 822
tests.
- `NODE_OPTIONS=--no-experimental-webstorage pnpm exec vitest run
--config packages/stage-ui/vitest.config.ts --project browser
packages/stage-ui/src/components/misc/character-switcher-drawer.browser.test.ts
packages/stage-ui/src/stores/mods/api/context-bridge.contract.browser.test.ts
--reporter=verbose`
- `NODE_OPTIONS=--no-experimental-webstorage pnpm run test:run`
- `pnpm run typecheck`
- `pnpm run lint`
- `sem diff --staged --no-cosmetics -v --file-exts .ts .tsx`, JSON diff,
and dependent analysis for `mountSwitcher`: no blocking findings.
- `git diff --cached --check`
GitHub CI also passes on this PR: [all nine CI jobs
succeeded](https://github.com/moeru-ai/airi/actions/runs/34306753310),
including [Unit
Test](https://github.com/moeru-ai/airi/actions/runs/34306753310/job/102324934330).
## Additional Context
[Reported CI
failure](https://github.com/moeru-ai/airi/actions/runs/34237304157/job/102098223378)
· [Parent commit
failure](https://github.com/moeru-ai/airi/actions/runs/34212756766/job/102017352566)
## Summary
- Add combined `Enable and Load` and `Disable and Unload` controls to
Plugin Host Debug.
- Keep enablement persistence and runtime lifecycle changes in one
sequential store action.
- Add tests for ordering, busy state, and failure handling.
- Localize the new controls and fallback error messages in English and
Simplified Chinese.
## Verification
- `pnpm exec vitest run --config packages/stage-ui/vitest.config.ts
--project node src/stores/devtools/plugin-host-debug.test.ts`
- `pnpm -F @proj-airi/i18n test`
- `pnpm -F @proj-airi/i18n build`
- `pnpm run build:packages`
- `pnpm typecheck`
- `pnpm lint`
- Manually tested the bundled `devtools-sample-plugin` in the Electron
Plugin Host Debug page. Both combined actions updated the persisted
registry and runtime session state without restarting the app.
## Visual changes
| Before | After |
|---|---|
| 
| 
|
| Plugin Host Debug | Plugin Host Debug with combined lifecycle controls
|
## Summary
- Send a serializable Live2D expression snapshot from the stage renderer
to the separate Settings window.
- Route expression changes back to the renderer that owns the loaded
model, with owner ID checks for stale-window isolation.
- Add browser regressions for the component boundary and the complete
BroadcastChannel round trip.
Fixes#2450
## Verification
- `pnpm typecheck`
- `pnpm lint`
- `../../node_modules/.bin/vitest run --project browser
src/components/scenarios/settings/model-settings/live2d.browser.test.ts`
- `node_modules/.bin/vitest run --config
apps/stage-tamagotchi/vitest.config.ts --project browser
apps/stage-tamagotchi/src/renderer/composables/model-settings-runtime.browser.test.ts`
- `./node_modules/.bin/electron-vite build`
- Vishot Electron capture with the issue Live2D fixture on the merge
base and this branch
- `sem diff upstream/main...HEAD --no-cosmetics -v --file-exts .ts .tsx`
## Visual changes
| Before | After |
|---|---|
|

|

|
| Settings / Models / Live2D expressions: empty list | Settings / Models
/ Live2D expressions: model entries are available |