diff --git a/.specs/plans/frontend-modal-identity-badges.md b/.specs/plans/frontend-modal-identity-badges.md new file mode 100644 index 00000000..bb503a93 --- /dev/null +++ b/.specs/plans/frontend-modal-identity-badges.md @@ -0,0 +1,80 @@ +# Plan: Frontend Modal Identity and Badges + +## Architectural Decisions + +- **Dependency**: This plan starts only after `frontend-modal-runtime` Phase 2 supplies `AksrtModalRuntime`, loader/cache/state contracts, telemetry marks, and the rollout reader. +- **Authentication snapshot**: `AuthApply` and `ApplicationRepository` expose one current-user snapshot containing verification state, latest application, form payload, allowed next action, and poll metadata. The REST handler and owner permission callback reuse that loaded application instead of querying by UUID twice. +- **External provider boundary**: Aliyun/Tencent validation and refresh stay behind `ApplicationService`. Provider HTTP work starts after the shell is visible, is cancellable/visibility-aware where possible, and cannot prevent the modal from opening. +- **Badge canonical path**: The JSON REST path is the only new badge read/write path. User-center and public author entry points share the same privacy-aware DTO; old HTML AJAX modal registrations and post-wear remote reopening are removed after cutover. +- **Privacy**: Cache keys include user/object/context and public-display mode. Authentication evidence remains owner/capability protected and no-store; public badge data follows `list_for_user()` privacy rules. No private DTO is persisted in browser storage. +- **Domain cache**: Badge owned/wear maps and authentication snapshots use short in-memory/request-context memoization. Any submit, review, wear, or status transition invalidates the affected tag before rendering the next view. +- **Admin boundary**: Existing native authentication review and badge management remain the operational surface. This plan does not move them into the Admin SPA. + +--- + +## Phase 1 — Authentication status appears quickly and refreshes safely + +**User stories**: US8, US9, US13. + +### What to build + +Replace the three independent SSR facade reads in `inc/helpers/user-center-renderers/profile-auth.php` with one typed snapshot from `src/User/AuthApply.php`/`src/User/ApplicationRepository.php`. Shape the current identity REST response around the same snapshot and reuse the application loaded by `owns_application()` in `src/Identity/RestController.php` inside get/refresh/manual handlers. Update `assets/js/routes/user-center-auth.js` to open the unified shell immediately, render snapshot state, defer provider refresh until the modal is open or the user explicitly refreshes, pause while hidden, apply backoff, and cancel stale refreshes. Preserve the existing create, manual evidence, provider callback, nonce, owner, capability, and sensitive no-store boundaries. + +### Done when + +- [ ] `profile-auth.php`, `AuthApply`, and `ApplicationRepository` produce one active/latest/verified snapshot per request; browser harness scenario `auth-snapshot-query-count` with `SAVEQUERIES` shows no repeated identical application SELECT for the active panel. +- [ ] `RestController` owner permission and get/refresh/manual handlers reuse one loaded application per request; `php -l src/User/AuthApply.php src/User/ApplicationRepository.php src/Identity/RestController.php src/Identity/ApplicationService.php` passes. +- [ ] Browser scenario `auth-modal-deferred-refresh` verifies shell-first rendering, no polling while the modal is closed, pause on hidden visibility, minimum interval/backoff, cancellation on close, and a useful error when the provider exceeds its timeout. +- [ ] Browser/API scenarios cover create, provider-pending, processing, verified, rejected/manual retry, evidence upload, duplicate submit, unauthorized owner, and sensitive evidence isolation; existing native review changes are reflected on the next snapshot. + +--- + +## Phase 2 — Badge modal uses one cached JSON path + +**User stories**: US10, US11, US12, US13. + +### What to build + +Refactor `src/User/Badges.php` so `list_for_user()`, `get_owned_map()`, and `get_wear()` share one context and do not rebuild owned/wear dictionaries repeatedly. Remove duplicate `Badges::init()` calls from `functions.php`. Update `assets/js/user-badges.js` to use the runtime loader, short cache, skeleton/error/retry states, and tag invalidation after wear. Replace the user-center and public author HTML/AJAX modal entry points in `inc/helpers/user-center-renderers/profile-header.php`, `templates/parts/user/author-hero.php`, and `assets/js/core/remote-modal.js` with the canonical privacy-aware JSON path. The old `wp_ajax(_nopriv)_aksrt_*badge*modal` handlers and post-wear remote reopen must disappear after this cutover. + +### Done when + +- [ ] `SAVEQUERIES` scenario `badge-read-query-count` shows one shared owned/wear/dictionary read context per badge response, and a repeated open for the same user/context uses the runtime cache without cross-user reuse. +- [ ] `assets/js/user-badges.js` displays skeleton/empty/error/retry states, re-renders the current list after wear, and no longer calls `AksrtRemoteModal.openRemote` after a successful wear; `node --check assets/js/user-badges.js assets/js/core/remote-modal.js` passes. +- [ ] `git grep -n "aksrt_user_badge_info_modal\|aksrt_single_badge_info_modal\|get_info_link"` shows no active target entry after the canonical path is enabled, and `functions.php` contains one badge singleton initialization. +- [ ] Browser/API scenarios cover no badges, earned/unearned/in-progress/detail, wear success/failure, public author privacy, self/manager visibility, unauthenticated access, REST nonce, and no leakage through cache keys. + +--- + +## Phase 3 — Authentication and badges are independently releasable + +**User stories**: US1, US2, US3, US11, US12, US13, US14. + +### What to build + +Connect identity and badge loaders to the per-domain rollout values from `frontend-modal-runtime`. Make successful authentication transitions, manual review results, badge wear, and badge management invalidate the appropriate runtime tags and update header/profile signals without a full reload. Exercise the two domains as separate gray releases, then remove the old domain-specific modal branches once each switch is permanently enabled. + +### Done when + +- [ ] The authentication and badge rollout values independently select the new loader path; toggling one does not change the other and does not create a second data store or dual-read branch. +- [ ] `php tools/audit-admin-contracts.php` exits 0 and `node --check assets/js/routes/user-center-auth.js assets/js/user-badges.js` passes after the final route/schema changes. +- [ ] Desktop/mobile scenarios verify shell/ready/error budgets, state refresh behind the modal, focus/keyboard behavior, no horizontal overflow, and no stale private data after logout or account switch. +- [ ] The gray-release checklist records shell/ready/error/status-mismatch metrics and demonstrates pausing the next rollout stage when identity or badge errors exceed the agreed threshold. + +--- + +## Out of Scope + +- 修改会员价格、会员权益、支付网关规则、订单账务、退款核算、认证资格/审核政策或徽章授予条件;这些规则只允许被新的流程正确调用和展示。 +- 新增支付渠道、替换支付供应商、在浏览器保存支付凭证,或改变支付回调/异步结算机制。 +- 新增公开认证资料库、向其他用户暴露认证文档、跨用户查看订单或徽章数据。 +- 把订单、售后、认证审核或徽章运营工作台迁移进配置型 SPA;日常运营仍属于原生 WordPress 后台。 +- 全站页面导航、用户中心信息架构、品牌设计系统或所有非弹窗页面的全面重做;本期只统一弹窗运行时和四类流程所需的轻量体验。 +- 强制页面加载时预取所有弹窗数据、长期持久化敏感弹窗数据、建立订单/认证/徽章的第二套事实存储或长期保留旧新双路实现。 +- 以新增兼容别名、旧 option 双读、静默降级或数据迁移层解决旧实现问题;流程切换后以新的规范化链路为唯一实现。 +- 建立独立的数据仓库、复杂转化漏斗或第三方用户行为追踪系统;本期观测只服务于弹窗性能、错误和业务状态闭环。 + +## Open Questions + +- 认证云供应商是否支持在请求取消/页面隐藏时安全停止远端刷新;若不支持,客户端只能停止轮询并让服务端回调/下次打开完成状态确认。 +- 徽章公开作者入口最终是否必须支持未登录访问;默认保留现有公开可见 DTO,但统一路径仍必须执行业务层 privacy 过滤。 diff --git a/.specs/plans/frontend-modal-loading-upgrade.md b/.specs/plans/frontend-modal-loading-upgrade.md new file mode 100644 index 00000000..4438355c --- /dev/null +++ b/.specs/plans/frontend-modal-loading-upgrade.md @@ -0,0 +1,84 @@ +# Plan: Frontend Modal Loading Upgrade + +This is the coordination plan for the three executable plans below. Implementation work should run in dependency order; the detailed phase checklists live in the linked plan files. + +## Architectural Decisions + +- **Execution split**: The shared modal runtime is a prerequisite. Identity/badges and orders/VIP can then proceed in parallel, each with its own domain API, UI, query optimization, and gray-release evidence. +- **One canonical runtime**: `AksrtModalRuntime` owns shell, skeleton, lifecycle, cancellation, cache, state, focus, and telemetry. Domain scripts only provide loaders and view models. +- **Canonical data boundaries**: Keep `aksrt/v1`, existing domain services, native operational admin, and `AKSRT_Settings` as the boundaries. No duplicate facts, compatibility reads, or long-lived old/new modal implementations. +- **No core table replacement**: Use existing order, identity, user-meta, payment, and badge data. The orders plan may add the required composite index through the existing DB migrator and schema-version process. +- **Staged release**: Roll out identity/badges first, then orders, then VIP payment. Each domain has an independent temporary switch and must meet the performance/error budget before the next switch is enabled. + +--- + +## Phase 1 — Shared modal runtime is ready for domain work + +**Detailed plan**: `.specs/plans/frontend-modal-runtime.md` + +**User stories**: US1–3, US12–14. + +### What to build + +Complete both runtime phases: baseline/rollout observability and the cancellable shell/loader/cache/state contract. This phase is a foundation exception because all four existing flows currently own incompatible modal shells and request lifecycles; the runtime is demoable through the existing remote-modal entry path before domain JSON migrations begin. + +### Done when + +- [ ] `.specs/plans/frontend-modal-runtime.md` Phase 1 and Phase 2 checklists are checked, `php tools/audit-admin-contracts.php` exits 0, and `node --check` passes for all runtime JS. +- [ ] Browser scenarios `modal-baseline`, `modal-shell-lifecycle`, and `modal-cache-dedupe` show shell-first behavior, cancellation, deduplication, invalidation, focus restoration, and cold/warm metrics. +- [ ] `git grep` inventory identifies every old remote-modal caller and labels the remaining domain migration owner before either business plan starts. + +--- + +## Phase 2 — Identity/badges and orders/VIP have vertical slices + +**Detailed plans**: `.specs/plans/frontend-modal-identity-badges.md` and `.specs/plans/frontend-modal-orders-vip.md` + +**User stories**: US4–11, US13. + +### What to build + +Run the identity/badge plan and orders/VIP plan in parallel after Phase 1. Each plan must finish its read path, write path, query/API/UI integration, permission handling, and browser regression scenarios before its gray-release phase. + +### Done when + +- [ ] `.specs/plans/frontend-modal-identity-badges.md` Phases 1–2 and `.specs/plans/frontend-modal-orders-vip.md` Phases 1–2 are checked with their named `SAVEQUERIES`, PHP, JS, API, and browser scenarios passing. +- [ ] Identity/badge and order/VIP loaders use independent cache/invalidation tags and cannot reuse another user or domain's payload. +- [ ] The native order/after-sale and identity/badge operational surfaces remain usable; no operational workbench is added to the Admin SPA. + +--- + +## Phase 3 — All domains are gray-released and old paths are removed + +**Detailed plans**: the Phase 3 sections of all three detailed plans. + +**User stories**: US1–14. + +### What to build + +Release identity/badges, then orders, then VIP payment using independent switches. Compare shell/ready/error/status-mismatch metrics against the PRD budgets, pause the next domain on regression, update user-center summaries after successful writes, and remove old HTML/AJAX/domain-specific modal calls after each domain is permanently enabled. + +### Done when + +- [ ] The Phase 3 checklist in each detailed plan is checked, with `php tools/audit-admin-contracts.php`, changed-file `php -l`, and changed-file `node --check` all passing. +- [ ] Desktop and mobile evidence shows approximately 100ms shell visibility, 200ms cache-hit content, p75 ≤800ms cold content, and p95 ≤1.5s initial budget, with errors/status mismatches recorded per domain. +- [ ] `git grep` confirms no active target old HTML badge modal, duplicate VIP purchase/poll coordinator, hard-coded order REST root, or bypassing domain loader remains after cutover. +- [ ] The final release record identifies the removal version for temporary rollout settings and confirms no compatibility alias, old option double-read, second fact store, or long-lived dual implementation remains. + +--- + +## Out of Scope + +- 修改会员价格、会员权益、支付网关规则、订单账务、退款核算、认证资格/审核政策或徽章授予条件;这些规则只允许被新的流程正确调用和展示。 +- 新增支付渠道、替换支付供应商、在浏览器保存支付凭证,或改变支付回调/异步结算机制。 +- 新增公开认证资料库、向其他用户暴露认证文档、跨用户查看订单或徽章数据。 +- 把订单、售后、认证审核或徽章运营工作台迁移进配置型 SPA;日常运营仍属于原生 WordPress 后台。 +- 全站页面导航、用户中心信息架构、品牌设计系统或所有非弹窗页面的全面重做;本期只统一弹窗运行时和四类流程所需的轻量体验。 +- 强制页面加载时预取所有弹窗数据、长期持久化敏感弹窗数据、建立订单/认证/徽章的第二套事实存储或长期保留旧新双路实现。 +- 以新增兼容别名、旧 option 双读、静默降级或数据迁移层解决旧实现问题;流程切换后以新的规范化链路为唯一实现。 +- 建立独立的数据仓库、复杂转化漏斗或第三方用户行为追踪系统;本期观测只服务于弹窗性能、错误和业务状态闭环。 + +## Open Questions + +- 部署是否已有统一的浏览器 RUM/Server-Timing 收集端;默认先提供 marks、响应头和现有部署观测,不新增主题指标表。 +- 复合订单索引与临时 rollout 设置的删除版本需要在最终发布记录中确认。 diff --git a/.specs/plans/frontend-modal-orders-vip.md b/.specs/plans/frontend-modal-orders-vip.md new file mode 100644 index 00000000..ce086a07 --- /dev/null +++ b/.specs/plans/frontend-modal-orders-vip.md @@ -0,0 +1,83 @@ +# Plan: Frontend Modal Orders and VIP + +## Architectural Decisions + +- **Dependency**: This plan starts only after `frontend-modal-runtime` Phase 2 supplies the canonical shell, loader, cache/state, cancellation, and telemetry contracts. +- **Order view model**: `UserOrders` and `OrderCenterRest` return a minimal list/detail view model with server-derived `allowed_actions`, status version, updated time, and invalidation tags. The front end never derives payment/cancel/after-sale eligibility from local status. +- **Order query shape**: Combine list/stat work where possible, batch resource decoration, and add the missing `user_id/status/created_at` index through `AKSRT_DB_Migrator` with a schema version bump. No new order table or duplicate fact store. +- **Reconciliation boundary**: `Gateway::reconcile_order()` remains the authority for pending external orders but runs after the detail shell is visible and only when needed. A normal completed/non-external detail must not wait for remote provider HTTP. +- **VIP JSON path**: Replace server-generated full HTML from `VipServiceModal` with a minimal JSON view model. `pay-vip.js` and the VIP shop in `credits.js` share one purchase coordinator; catalog, member state, order object, and payment attempt are reused within a request. +- **Payment safety**: Payment gateways and `PaymentOrchestrator` stay server-side. Client polling is bounded, backoff-aware, visibility-aware, cancellable, and terminated by a terminal order state. Core price, eligibility, ledger, callback, and VIP fulfillment rules are unchanged. +- **API configuration**: All order/VIP requests use the globally injected API root and REST nonce. Remove `order-center.js` hard-coded REST construction and do not add compatibility route aliases. +- **Release**: Order and VIP rollout switches are independent. Old paths may remain inactive while a domain is not released, but each domain has one canonical implementation after its switch is permanently enabled. + +--- + +## Phase 1 — Order details appear before remote state reconciliation + +**User stories**: US6, US7, US11, US13. + +### What to build + +Optimize `src/Payment/UserOrders.php` and `src/Payment/OrderRepositoryCore.php` so the order list does not perform a fixed list plus four COUNT queries and resource rows do not cause post/meta N+1 work. Add the composite order index in `src/Core/DBMigratorPart2.php` and bump the schema version through the existing migrator. Shape `src/Payment/OrderCenterRest.php` responses around minimal list/detail view models and preserve owner checks. Update `src/Payment/Gateway.php` so pending external reconciliation is deferred or represented as a refresh state after the shell is visible. Replace `order-center.js` hard-coded `siteUrl/wp-json` construction with injected `apiUrl/restUrl`, then move detail, pay, cancel, refresh, and invalidation through `AksrtModalRuntime`. + +### Done when + +- [ ] `SAVEQUERIES` scenario `order-list-query-count` verifies that the normal first page no longer issues list + four independent COUNT queries and that resource-order decoration has no per-row post/meta N+1; empty/overflow pages remain correct. +- [ ] `DBMigratorPart2` defines the `user_id/status/created_at` query index and the active schema version is bumped; after migration, `SHOW INDEX` or the WordPress DB harness verifies the index. +- [ ] Browser scenario `order-detail-shell` verifies summary skeleton first, ordinary detail does not wait for provider HTTP, pending reconciliation reports refresh state, and detail/status/list update after pay/cancel/after-sale responses. +- [ ] `order-center.js` reads the injected API root, and `php -l src/Payment/UserOrders.php src/Payment/OrderRepositoryCore.php src/Payment/OrderCenterRest.php src/Payment/Gateway.php src/Core/DBMigratorPart2.php` plus `node --check assets/js/order-center.js` pass. +- [ ] API/browser cases cover another-tab status changes, unauthorized order numbers, duplicate action clicks, timeout/retry, pagination, and no cross-user cache reuse. + +--- + +## Phase 2 — VIP purchase reaches payment result through one coordinator + +**User stories**: US4, US5, US11, US13. + +### What to build + +Change `src/Content/VipServiceRest.php` and `src/Content/VipServiceModal.php` from full HTML modal responses to a minimal JSON view model that contains the server-calculated catalog, member state, payment methods, allowed actions, and version metadata. Refactor `src/Content/VipServicePurchase.php` and `src/Payment/PaymentOrchestrator.php` to reuse the catalog/member/order context instead of reading the same data after create and again during pay. Consolidate the remote modal path in `assets/js/pay-vip.js` and the embedded shop path in `assets/js/credits.js` into one purchase/poll coordinator; keep the existing `credits-vip.php` and `vip-tab.php` entry points but route their actions through it. Replace the fixed 40 × 3-second loop with bounded backoff, visibility handling, abort, and terminal-state invalidation. + +### Done when + +- [ ] `SAVEQUERIES` scenario `vip-purchase-query-count` shows no duplicate member-level/catalog/order reads for modal load and purchase create/pay; `VipServiceRest` reuses the catalog user level instead of recomputing it. +- [ ] `pay-vip.js` and `credits.js` share one VIP purchase/poll coordinator; `git grep -n "setInterval\|pay/content/order\|pay-vip/modal" assets/js/pay-vip.js assets/js/credits.js` shows no second fixed 40 × 3-second loop or old full-HTML injection path. +- [ ] Browser scenario `vip-purchase-flow` covers not-enrolled, active, expired, renewal/upgrade, package/price, points exchange, gateway redirect, cancel, failure, async success, duplicate click, and return to order detail. +- [ ] Payment response inspection confirms no gateway secret reaches the browser; existing gateway, order callback, VIP fulfillment, core price, and entitlement rules pass regression scenarios. +- [ ] `php -l src/Content/VipServiceRest.php src/Content/VipServiceModal.php src/Content/VipServicePurchase.php src/Payment/PaymentOrchestrator.php` and `node --check assets/js/pay-vip.js assets/js/credits.js` pass. + +--- + +## Phase 3 — Orders and VIP are independently gray-releasable + +**User stories**: US1–US7, US11–US14. + +### What to build + +Connect order and VIP coordinators to the independent rollout switches, invalidate user-center order/VIP summaries after successful writes, and verify native order/after-sale operations remain the operational backend. Release orders first, measure shell/ready/error/status mismatch, then release VIP payment only after the order slice is stable. Remove each domain's old modal calls once its switch is permanently enabled. + +### Done when + +- [ ] Order and VIP rollout values independently select the new loaders; toggling one does not change the other or create a second order/member fact store. +- [ ] `php tools/audit-admin-contracts.php` exits 0, all changed PHP files pass `php -l`, and `node --check assets/js/order-center.js assets/js/pay-vip.js assets/js/credits.js` passes. +- [ ] Desktop/mobile scenarios verify shell/ready budgets, cache-hit and cold-load p75/p95, payment/order state synchronization, permission errors, retry behavior, focus, and no horizontal overflow. +- [ ] Gray-release evidence shows orders reaching the agreed 100ms shell, 200ms cache-hit, p75 ≤800ms, and p95 ≤1.5s initial budgets before VIP is enabled; rollback is demonstrated by disabling only the affected domain switch. + +--- + +## Out of Scope + +- 修改会员价格、会员权益、支付网关规则、订单账务、退款核算、认证资格/审核政策或徽章授予条件;这些规则只允许被新的流程正确调用和展示。 +- 新增支付渠道、替换支付供应商、在浏览器保存支付凭证,或改变支付回调/异步结算机制。 +- 新增公开认证资料库、向其他用户暴露认证文档、跨用户查看订单或徽章数据。 +- 把订单、售后、认证审核或徽章运营工作台迁移进配置型 SPA;日常运营仍属于原生 WordPress 后台。 +- 全站页面导航、用户中心信息架构、品牌设计系统或所有非弹窗页面的全面重做;本期只统一弹窗运行时和四类流程所需的轻量体验。 +- 强制页面加载时预取所有弹窗数据、长期持久化敏感弹窗数据、建立订单/认证/徽章的第二套事实存储或长期保留旧新双路实现。 +- 以新增兼容别名、旧 option 双读、静默降级或数据迁移层解决旧实现问题;流程切换后以新的规范化链路为唯一实现。 +- 建立独立的数据仓库、复杂转化漏斗或第三方用户行为追踪系统;本期观测只服务于弹窗性能、错误和业务状态闭环。 + +## Open Questions + +- 订单/支付环境是否提供现有 Server-Timing 或网关状态监控收集端;默认使用响应头和现有部署观测,不新增主题内指标表。 +- 复合订单索引在生产大表上的部署窗口需要由发布流程确认;迁移必须遵循现有 `AKSRT_DB_Migrator` 原子升级约束。 diff --git a/.specs/plans/frontend-modal-runtime.md b/.specs/plans/frontend-modal-runtime.md new file mode 100644 index 00000000..31d7cf83 --- /dev/null +++ b/.specs/plans/frontend-modal-runtime.md @@ -0,0 +1,70 @@ +# Plan: Frontend Modal Runtime + +## Architectural Decisions + +- **Canonical runtime**: Refactor `assets/js/core/remote-modal.js` into the single `AksrtModalRuntime` seam for shell creation, lifecycle, focus, loading, error, cancellation, and refresh. Domain scripts provide loaders and view-model renderers; they do not create competing modal shells. +- **Loader contract**: A loader receives a domain context and abort signal and returns a minimal view model plus render/update callbacks. It may read only the active modal's data. +- **Cache contract**: Use in-memory, user-scoped cache entries with short TTLs, in-flight request deduplication, explicit invalidation tags, and complete clearing on logout/account switch. Do not persist private modal payloads in localStorage. +- **State contract**: All domains use the same `idle`, `loading`, `ready`, `empty`, `submitting`, `success`, `error`, `unauthorized`, and `stale` states. Skeletons match the final layout and never represent business truth. +- **API boundary**: Existing `aksrt/v1` domain routes remain the service boundary; HTML modal responses are replaced domain by domain with minimal JSON view models. Every changed REST route keeps a `permission_callback`; AJAX is not a new data path. +- **Rollout**: Add a temporary per-domain rollout section under the existing Tools configuration surface, read through `AKSRT_Settings`. One canonical new implementation is enabled per domain; the switch is for staged release, not a compatibility data layer. +- **Telemetry**: Use browser `performance.mark/measure` and server `Server-Timing`/existing deployment observability by default. Do not add a private payload or payment/identity-content event store in this foundation plan. +- **Build boundary**: Frontend assets are plain theme JavaScript, not the Admin SPA bundle. Run `node --check` for changed JS and run the admin contract audit only when the rollout schema/controller changes. + +--- + +## Phase 1 — Modal latency is measurable and rollout is controllable + +**User stories**: US1, US2, US3, US14. + +### What to build + +Create the typed rollout reader and configuration contract, then add a small telemetry module that marks the existing VIP, order, identity, and badge open flows without changing their business behavior. Establish cold-cache and warm-cache baselines for click-to-shell, click-to-first-content, click-to-ready, request count, SQL count, response size, and error outcome. The rollout reader must be consumed through `AKSRT_Settings` and the existing Tools schema/controller path, not a raw option read. + +Primary integration points: + +- `AKSRT_Settings` and the Tools schema/controller for `frontend_modal_rollout`. +- `assets/js/core/modal-telemetry.js` (new) and the existing feature enqueue/config injection in `src/Core/ScriptsPart1c.php` and the surface script enqueue modules. +- Existing flows in `assets/js/pay-vip.js`, `assets/js/order-center.js`, `assets/js/routes/user-center-auth.js`, and `assets/js/user-badges.js` for baseline marks only. + +### Done when + +- [ ] The Tools schema, REST response, and `AKSRT_Settings` reader expose independent VIP, order, identity, and badge rollout values; `php tools/audit-admin-contracts.php` exits 0. +- [ ] `assets/js/core/modal-telemetry.js` emits click, shell, first-content, ready, action-start, action-end, and error marks without including payment credentials, order contents, or identity evidence. +- [ ] Browser scenario `modal-baseline` records cold and warm measurements for all four current entry points, and the baseline artifact names the request/SQL counts used for later comparison. +- [ ] `node --check assets/js/core/modal-telemetry.js assets/js/pay-vip.js assets/js/order-center.js assets/js/routes/user-center-auth.js assets/js/user-badges.js` passes. + +--- + +## Phase 2 — Any modal opens with a cancellable shell and reusable cache + +**User stories**: US1, US2, US3, US12, US13. + +### What to build + +Refactor `AksrtRemoteModal` in `assets/js/core/remote-modal.js` into the canonical runtime while preserving its existing visual shell as the first implementation. Add focused runtime modules for loader registration, cache/state, and telemetry integration. The runtime must support a skeleton before data, JSON or HTML during the migration boundary, abort signals on close/replacement, request deduplication, explicit invalidation, focus return, keyboard/overlay close, mobile bottom-sheet behavior, and retryable errors. Wire the existing generic remote-modal trigger set through this runtime and inventory every remaining caller before domain migrations begin. + +### Done when + +- [ ] `assets/js/core/remote-modal.js` exposes one canonical `AksrtModalRuntime.open/close/refresh` lifecycle and the new loader/cache/state modules are imported by the changed entry point; no domain script creates a second global modal host. +- [ ] `node --check assets/js/core/remote-modal.js assets/js/core/modal-runtime.js assets/js/core/modal-cache.js assets/js/core/modal-state.js` passes, using the actual new filenames if the implementation keeps the runtime in the existing file. +- [ ] Browser scenario `modal-shell-lifecycle` verifies shell visibility within the 100ms initial budget, skeleton/error/retry states, ESC and overlay close, focus return, request cancellation, and mobile bottom-sheet behavior. +- [ ] Browser scenario `modal-cache-dedupe` verifies one read for concurrent/repeated opens, invalidation after a successful write, and no reuse after logout/account switch; `git grep -n "AksrtRemoteModal\|data-toggle=\"AksrtRemoteModal\"\|data-aksrt-remote-modal"` produces an explicit migration inventory. + +--- + +## Out of Scope + +- 修改会员价格、会员权益、支付网关规则、订单账务、退款核算、认证资格/审核政策或徽章授予条件;这些规则只允许被新的流程正确调用和展示。 +- 新增支付渠道、替换支付供应商、在浏览器保存支付凭证,或改变支付回调/异步结算机制。 +- 新增公开认证资料库、向其他用户暴露认证文档、跨用户查看订单或徽章数据。 +- 把订单、售后、认证审核或徽章运营工作台迁移进配置型 SPA;日常运营仍属于原生 WordPress 后台。 +- 全站页面导航、用户中心信息架构、品牌设计系统或所有非弹窗页面的全面重做;本期只统一弹窗运行时和四类流程所需的轻量体验。 +- 强制页面加载时预取所有弹窗数据、长期持久化敏感弹窗数据、建立订单/认证/徽章的第二套事实存储或长期保留旧新双路实现。 +- 以新增兼容别名、旧 option 双读、静默降级或数据迁移层解决旧实现问题;流程切换后以新的规范化链路为唯一实现。 +- 建立独立的数据仓库、复杂转化漏斗或第三方用户行为追踪系统;本期观测只服务于弹窗性能、错误和业务状态闭环。 + +## Open Questions + +- 部署环境是否已有 RUM/Server-Timing 收集端?默认先交付浏览器 marks 与响应头,不在主题内新增持久化指标表。 +- `frontend_modal_rollout` 的临时开关在所有领域切换完成后的删除版本,需要在发布记录中确定。