# M32 Page View Architecture Refactor ## Status - Plan: `M32_PAGE_VIEW_ARCHITECTURE_REFACTOR` - Status: `COMPLETE` - Activated: 2026-08-25 - Baseline: `9b010b3` (`M31_MVP_RELEASE_READINESS_COMPLETE`) - Owner authorization: the Owner requested a scalable, isolated code architecture and asked that page Views be split out instead of leaving all composition in `entry_shell.gd`. ## Objective Establish a durable page architecture for future expansion without changing the accepted MVP behavior, visuals, input, localization or persistence. The target is an explicit one-way presentation flow with isolated Views, pure page controllers, application/domain services and an explicit route/context boundary. This is an architecture migration, not a feature or visual redesign. ## Target Architecture ```text Composition Root / App Flow -> Screen Router + Page Context -> Page View (Godot Control; emits user intent only) -> Page Controller / Presenter (pure ViewData + command eligibility) -> Application Command Boundary -> Domain Service (rules, receipts, transactions) -> Repository / Save -> immutable state snapshot -> Controller snapshot -> View.render(ViewData) ``` ### Ownership rules 1. **View** owns nodes, layout, focus and local visual state only. It does not load/save files, call Company/Contract/Battle services, inspect `facade`, or decide economy/progression rules. 2. **Page Controller/Presenter** receives explicit state and presentation inputs, returns a deep-copied `ViewData` snapshot and command eligibility. It does not access Godot nodes or persistence. 3. **Application command boundary** translates a View intent into a named operation and routes it to the owning Domain Service. It owns no duplicate game rules. 4. **Domain Service** remains the only authority for mutations, receipts, conflict/idempotency behavior and Save writes. 5. **Router/Context** owns screen identity, caller, return route, focus and presentation mode. It carries no Company or Battle authority. 6. **Compatibility adapters** remain outside the current path and cannot be imported by new Views or Controllers. ### Why this is not strict MVVM Godot does not provide a reliable project-wide observable binding layer here. The project will use a **Passive View + Presenter/ViewData + Domain Service** pattern with explicit one-way refreshes. This gives MVVM-like isolation without introducing fragile two-way binding or a global event bus. ## Player/System Stopping Point - The player sees the same accepted first-cycle pages and routes as M31. - The first extracted View renders the same nodes, text, focus, touch targets, responsive geometry and callbacks through the new View contract. - A View can be unit-tested with a fake context and ViewData without constructing Company, Save or historical facade state. - A controller/service regression proves that the extracted View has not changed any transaction or persistence semantics. - No new page, content, economy rule, asset, audio cue, route or package is added by this milestone. ## Ordered Steps ### M32-A — Architecture contract and inventory (`COMPLETE`) - Record the target layering, dependency direction, forbidden imports and migration order. - Inventory the current EntryShell page composition, Controller seams, service boundaries and route/context behavior. - Select one low-risk first-cycle View pilot and define its exact acceptance surface before product code changes. - Commit and push this activation boundary. **Complete at source `9bde014`.** ### M32-B — View contract and pilot extraction (`COMPLETE`) - Add a UI-only `PageView` contract/base and explicit `PageContext`/intent boundary under the current Runtime foundation. - Extract the OUT-004 Recovery View first because its Controller and service boundaries are already complete and its visual composition is accepted. - Keep `EntryShell` as the composition root for this pilot; it may mount the extracted View and translate its intents, but the View cannot call the services directly. - Preserve node names, signals/callback behavior, focus order, localization, touch targets and normal `100%` geometry byte-for-byte where applicable. - Complete at source `23924a2`: `ABGPageView`, `ABGPageContext` and the OUT-004 `ABGRecoveryPageView` are integrated through an explicit intent boundary; the Recovery capture is byte-equivalent to the pre-migration baseline at all four normal fixtures. ### M32-C — Focused verification (`COMPLETE`) - Run the new View contract test and M30 Recovery Controller/Runtime tests. - Run DEV-5 regressions and the full MVP first-cycle audit. - Capture Recovery at the required normal `100%` fixtures and compare the extracted View against the pre-migration evidence. - Require strict diagnostics `0`, exact-once markers, parsed touch and owned process/workspace cleanup. - Complete at source `088f46b`: the source-bound View contract, DEV-5 candidate and full MVP first-cycle audit pass. Recovery normal captures are byte-equivalent to the M30 baseline at all required normal fixtures. Evidence is recorded in `docs/runtime/M32_PAGE_VIEW_ARCHITECTURE_VISUAL_AUDIT.md`. ### M32-D — Handoff and next migration map (`COMPLETE`) - Record the pilot evidence, remaining EntryShell View blocks and the ordered next extraction groups. - Close only the pilot; do not claim that every View has been split. - Update the checkpoint and development ledger, create a focused pushed commit and leave the next page migration as a new bounded step/plan if needed. - Complete at source `088f46b` with the evidence and ordered migration map in `docs/runtime/M32_PAGE_VIEW_ARCHITECTURE_VISUAL_AUDIT.md`. The next page extraction is intentionally not activated by this plan. #### Ordered next extraction map (handoff only) | Order | Group | Why this order | Required boundary | | --- | --- | --- | --- | | 1 | OUT-003 Reward Choice → OUT-002 Knockout → OUT-001 Result | Controllers and continuation facts are already complete; the three pages form one settled-result chain and share the Recovery handoff. | One View per page, explicit intents, no duplicated settlement/reward rules. | | 2 | RSK-002 Risk Commitment → PTY-002 Readiness | Small command surfaces with explicit acknowledgement/eligibility facts; useful for validating shared command-rail primitives. | Preserve frozen Party snapshot, stale-dependency guards and receipt semantics. | | 3 | DEV-4 Party Line → RSK-001 Risk Board | Selection-heavy preparation Views; extract after the command/intent contract has two more proven consumers. | Keep selection local to the View; Controller owns normalized order/selection data. | | 4 | SHP-002 Recruit Review → SHP-001 Market → Contract Choice → Guild | Larger content surfaces and more route/context variants; migrate after the low-risk page grammar is stable. | Keep hire/contract/economy mutations in existing services and preserve stable IDs. | | 5 | BAT-001 Opening → BAT-002 Live | Highest lifecycle/timeline coupling and resize/input surface; defer until passive page Views are proven. | Separate field canvas/timeline rendering from observation/receipt commands. | Do not create a project-wide binding/event-bus layer between these steps. A shared primitive is justified only when two extracted Views have the same semantic contract and a focused test proves that the primitive removes duplication without importing domain state. ## Frozen Scope - No gameplay, economy, Company, Contract, Battle, Reward, Recovery or Save semantic changes. - No visual redesign, asset regeneration, localization rewrite or 130% scope. - No mass rewrite of `entry_shell.gd` in one step. - No deletion of compatibility/history files, `.uid`, `.import` or `prototype_incremental/`. - No iOS export, packaging, signing, deployment or store work. - No global event bus, reflection-based service locator or speculative two-way binding framework. ## Acceptance Checks - `ABG_M32_RECOVERY_VIEW_CONTRACT_OK` exactly once. - Existing `ABG_M30_RECOVERY_CONTROLLER_OK`, `ABG_DEV5_CANDIDATE_SUITE_OK` and `ABG_MVP_FIRST_CYCLE_AUDIT_OK` remain green. - Extracted View has no direct imports of Domain Services, Save, `facade` or `current_screen`; it emits explicit intents and renders supplied ViewData. - Recovery normal `100%` captures at `760x360`, `844x390` and `1280x720` in `zh_CN`/`en` remain visually and interactively equivalent. - Focused source is committed, pushed and clean for tracked files. ## Issue Register | ID | Status | Boundary | Evidence / action | | --- | --- | --- | --- | | `M32-I01` | `OPEN` | `entry_shell.gd` still mixes routing, View construction and command callbacks | Extract only through the bounded pilot; do not mass-rewrite. | | `M32-I02` | `OPEN_SCOPE` | Legacy `main.gd`/compatibility adapters remain | Preserve as an anti-corruption boundary until a later authorized migration. | | `M32-I03` | `OPEN` | No project-wide observable binding layer | Use explicit `ViewData` refresh and intent signals; do not add a global bus. | ## Final Handoff `ABG M32 RECOVERY VIEW PILOT COMPLETE`