| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 14 | 4 | 0% |
| Commands | 0 | 0 | 3 | 0% |
| Section tags | 3 | 4 | 4 | 27% |
What each file covers
Sections
0 shared · 14 only in A · 4 only in B- − Core Principles
- − Screaming Architecture
- − Suggested Structure
- − Dependency Direction
- − Make Composition Read Like The Product
- − UI Boundary
- − State Ownership
- − Effects And Async Work
- − Data And Infrastructure
- − Component And Hook APIs
- − Public Boundaries
- − Growing The Architecture
- − Testing
- − Review Checklist
- + Wand Enhancer Agent Notes
- + Remote Web Panel
- + ASAR Patch Pipeline
- + Validation
Commands
0 shared · 0 only in A · 3 only in B- + pnpm run build:bridge
- + node --check web-panel/dist/bridge.cjs
- + node --check web-panel/dist/renderer-scripts/remote-popup-cleanup.js
Section tags
3 shared · 4 only in A · 4 only in B- − test
- − architecture
- − git-pr
- − ui
- + build
- + security
- + deployment
- + agent-behaviour
- code-style
- testing-strategy
- do-not
Line diff
k1tbyte/Wand-Enhancer · web-panel/CLAUDE.md
@@ −1 @@
1Use these rules as defaults, not as a reason to add ceremonial folders or wrapper layers.
2
3## Core Principles
4
5- Organize code around product capabilities, not framework vocabulary.
6- Keep related UI, state, rules, and data access close until a real boundary justifies moving
7 them apart.
8- Dependencies point from composition and UI toward stable rules and narrow capabilities.
9- Protect rendering code from business, state-management, and infrastructure complexity.
10- Keep one source of truth and derive everything else.
11- Apply KISS, YAGNI, and DRY together. Remove duplicated knowledge, not merely similar syntax.
12- Prefer explicit, readable flow over clever abstractions and hidden behavior.
13
14## Screaming Architecture
15
16The repository structure and public APIs should reveal what the product does.
17
18Prefer:
19
20```text
21features/
22 checkout/
23 search/
24 account-security/
25```
26
27Avoid making the application read primarily as:
28
29```text
30components/
31hooks/
32services/
33stores/
34utils/
35```
36
37Technical folders are useful inside a capability, where their owner is clear. Generic top-level
38folders easily become dependency magnets with unclear ownership.
39
40Names should use product language. Prefer `useCheckoutSummary`, `reserveStock`, and
41`AccountSecurityPanel` over `useData`, `processItems`, and `GenericPanel`.
42
43## Suggested Structure
44
45Start with the smallest structure that makes ownership obvious:
46
47```text
48src/
49 app/ startup, providers, router, global composition
50 pages/ route-level composition
51 features/
52 <capability>/
53 index.ts optional public API
54 ui/ optional rendering components
55 model/ optional state, view models, decisions
56 api/ optional external data access
57 lib/ optional feature-local pure helpers
58 domains/ optional shared product rules and types
59 shared/
60 ui/ domain-free visual primitives
61 api/ generic transport/query infrastructure
62 lib/ genuinely generic pure helpers
63```
64
65Folders are created when they contain a real responsibility. A small feature may be one cohesive
66file. Do not create empty layers in anticipation of future complexity.
67
68## Dependency Direction
69
70- `app` installs providers, constructs dependencies, and composes the application.
71- `pages` compose capabilities for a route. They do not own business rules or data protocols.
72- A feature owns one user-recognizable capability end to end.
73- Feature UI consumes its own model/view-model API, not raw infrastructure.
74- Shared domain code contains reusable product rules and stays independent of React and I/O.
75- `shared` contains only domain-free code. Product-specific code is not shared merely because
76 two files use it.
77- Avoid feature-to-feature imports. Compose features in a page, promote truly shared rules to a
78 domain module, or introduce a named workflow when coordination is the actual responsibility.
79- Cyclic imports are an architecture problem, not something to solve with a tooling workaround.
80
81For a simple feature, direct `ui -> model -> api` dependencies are sufficient. Introduce ports,
82facades, dependency injection, or workflows only when they hide real complexity, enable
83important tests, or separate unstable infrastructure.
84
85## Make Composition Read Like The Product
86
87Pages and other composition boundaries should use capability-level APIs.
88
89Prefer:
90
91```tsx
92<CheckoutSummary />
93<PlaceOrderButton />
94```
95
96Over:
97
98```tsx
99<Card>
100 <Select options={paymentOptions} onChange={handlePaymentChange} />
101 <Button onClick={handleSubmit}>Submit</Button>
102</Card>
103```
104
105The second version makes the page understand checkout behavior and low-level UI configuration.
106That knowledge belongs to the checkout capability.
107
108This does not mean wrapping every native element or design-system primitive. Semantic HTML and
109visual primitives are correct inside feature UI. Create a capability component when it hides
110product behavior or gives composition code a clearer product-level API.
111
112Avoid "raw components" whose consumers must know internal options, state transitions, query
113shapes, or protocol details. Avoid generic configuration-driven components that combine
114unrelated product modes behind dozens of props.
115
116## UI Boundary
117
118- Components render data and translate DOM events into named user intents.
119- Keep business decisions, data mapping, persistence, protocol handling, and multi-step async
120 flows outside rendering components.
121- UI receives render-ready values. It should not reconstruct domain meaning from raw DTOs.
122- Prefer intent props and commands such as `onApprove`, `renameProject`, or `submitOrder` over
123 generic `onChange`, `setState`, or `patch` APIs at capability boundaries.
124- Keep ephemeral visual state local: focus, hover, open/closed, and uncommitted input usually
125 belong in the component.
126- Split components by responsibility and API clarity, not by arbitrary line limits.
127- Prefer slots and composition over components with many layout modes and boolean props.
128- Use semantic HTML and preserve accessibility behavior.
129
130A view-model hook is useful when it protects UI from state shape, async coordination, or business
131decisions. Do not create a pass-through hook that only renames one value to satisfy a diagram.
132
133## State Ownership
134
135Choose the smallest correct owner:
136
137| State | Preferred owner |
138| --- | --- |
139| Ephemeral visual state | local component state |
140| Uncommitted form state | the form or feature |
141| URL/shareable navigation state | the router/URL |
142| Remote server resource and cache | a query/cache layer |
143| Shared capability state | that feature's model/store |
144| Cross-capability process | a named workflow or app-level model |
145
146- A store is not a bucket for every value used by several components.
147- Split state by capability and lifecycle, not by data type.
148- Expose narrow selectors, hooks, or commands. Do not expose a complete mutable store to all UI.
149- Store transitions should express user or domain intent, not generic object mutation.
150- Derive values instead of storing synchronized copies.
151- Do not use effects to keep two pieces of application state synchronized.
152- React Context is suitable for dependency injection or stable scoped state. Avoid one broad
153 app context whose every update rerenders unrelated consumers.
154
155State-library choice is an implementation detail. Architecture should survive replacing it
156without rewriting pages and rendering components.
157
158## Effects And Async Work
159
160- Use effects to synchronize with external systems, not to calculate render data or handle user
161 events.
162- Start event-driven work from the event or model command that owns it.
163- Every subscription, timer, listener, or in-flight operation must have a clear owner and
164 cleanup path.
165- The owning feature/model defines pending, success, empty, error, retry, and cancellation
166 semantics.
167- Prevent stale async results and race conditions where users can trigger overlapping work.
168- Do not hide failures with broad `catch` blocks or silently convert errors into empty data.
169
170## Data And Infrastructure
171
172- Treat network responses, storage, URL input, files, and third-party SDK output as untrusted.
173- Validate and normalize data at the boundary where it enters the application.
174- Map transport DTOs and external errors into product-oriented values before they reach UI.
175- Keep raw `fetch`, storage APIs, SDK calls, and protocol details out of rendering components.
176- Keep a feature-specific API adapter inside the feature until it has a real shared consumer.
177- Introduce a client, repository, gateway, service, or facade only when its responsibility is
178 distinct and useful.
179- Avoid wrapper chains that only forward calls. One clear adapter is better than
180 `Client -> Service -> Facade` without separate responsibilities.
181- Inject infrastructure when tests, multiple implementations, lifecycle, or unstable external
182 APIs justify it. Do not introduce dependency injection for every pure helper.
183
184## Component And Hook APIs
185
186- Component and hook APIs describe product intent, not internal implementation.
187- Avoid boolean prop combinations that create unclear or invalid modes. Prefer explicit variants
188 or separate components.
189- Avoid passing raw query results, stores, SDK clients, or large configuration objects through
190 component trees.
191- Keep public props small and cohesive. A component that needs unrelated groups of props likely
192 owns too many responsibilities.
193- Custom hooks encapsulate React state, lifecycle, or reusable reactive behavior. Pure
194 calculations remain plain functions.
195- Do not use `useEffect`, `useMemo`, `useCallback`, or `memo` by habit. Use them for correctness
196 or measured performance needs.
197- Do not duplicate server or domain state into component state merely to make it editable.
198 Create an explicit draft only when the UX requires commit/cancel semantics.
199
200## Public Boundaries
201
202- Export the smallest useful public surface of a feature.
203- Consumers should use a feature's public components, hooks, commands, and types, not deep
204 internal paths.
205- Keep implementation-only state, DTOs, adapters, and helpers private.
206- Do not create barrel files everywhere. Use a public entry point only where a real boundary
207 exists.
208- A reusable abstraction should have a clear owner and at least one current reason to exist.
209- Avoid generic `core`, `common`, `helpers`, `services`, or `utils` modules that collect
210 unrelated responsibilities.
211
212## Growing The Architecture
213
214Start local and promote code only after pressure appears:
215
216- A second consumer may justify shared domain code, but similar code is not automatically the
217 same knowledge.
218- Repeated external integration logic may justify a shared adapter.
219- A process coordinating several capabilities may justify a named workflow.
220- A large feature may split into smaller capabilities when they have distinct responsibilities
221 and lifecycles.
222- Separate packages are useful when an enforceable boundary, independent reuse, or independent
223 lifecycle outweighs their maintenance cost.
224
225Do not begin a small application with every possible layer, package, provider, repository,
226facade, and design pattern. Strong architecture makes growth cheaper; it does not predict every
227future requirement.
228
229## Testing
230
231- Test product behavior and public contracts, not implementation trivia.
232- Test pure rules with unit tests.
233- Test feature models and async transitions without rendering where practical.
234- Test components through accessible user behavior.
235- Test infrastructure mapping and validation at external boundaries.
236- Keep end-to-end tests for critical user journeys.
237- Mock external systems and unstable boundaries, not every internal function.
238- Add tests proportional to risk, especially for validation, permissions, races, retries,
239 cancellation, and regressions.
240
241## Review Checklist
242
243Before finishing a change, ask:
244
245- Does the file location make its owner obvious?
246- Does composition code read in product language?
247- Is UI protected from raw state, DTOs, infrastructure, and business decisions?
248- Is there one source of truth?
249- Are effects only synchronizing external systems?
250- Is new shared code genuinely domain-free or genuinely shared?
251- Does every abstraction remove current complexity?
252- Can important behavior be tested without rendering the whole app?
253- Did the change preserve accessibility, error handling, and cleanup?
254- Is this the least code that clearly solves the current problem?
255
k1tbyte/Wand-Enhancer · AGENTS.md
@@ +1 @@
1INFO ./docs/*
2
3# Wand Enhancer Agent Notes
4
5This repository patches the Wand Electron app from a .NET Framework WPF desktop tool. Keep changes narrow and preserve the patch pipeline invariants.
6
7## Remote Web Panel
8
9- The default local remote port is `3223`. Keep bridge and frontend constants aligned; C# must not duplicate the presentation URL or port.
10- The embedded panel must stay small because the desktop patcher embeds it and then injects it into Wand's `app.asar`.
11- Remote tooltip links and every rendered `remote-qr-code` are redirected by `web-panel/bridge/scripts/default/remote-popup-cleanup.js`. It reuses Wand's loaded QR renderer through the webpack runtime, keeps the local URL visible as a fallback, and hides the Pro onboarding remote mobile app card. Do not reintroduce C# ASAR patches for the tooltip URL or QR component; a changed UI bundle must not make the whole remote-panel patch fail.
12- Production builds must not include mock data, debug routes, sourcemaps, local fonts, heavy icon libraries, or runtime class helper packages.
13- The Electron bridge is authored as TypeScript under `web-panel/bridge/src/`, but production runtime must be bundled/minified into `web-panel/dist/bridge.cjs` by `pnpm run build:bridge`. Do not copy bridge source into Wand or embed it as ASAR resources.
14- Mock/demo data is dev-only and must be reached through `import.meta.env.DEV` dynamic imports.
15- Source can use React-compatible imports, but production runtime resolves them to Preact aliases in `web-panel/vite.config.ts`.
16- UI uses Tailwind CSS and lightweight local primitives under `web-panel/src/shared/ui/`.
17- Default renderer script sources live in `web-panel/bridge/scripts/default/` and are bundled/minified into `web-panel/dist/renderer-scripts/` by `pnpm run build:bridge`. Custom user scripts are selected in the WPF patch modal and copied from `PatchConfig.CustomScriptPaths`; only existing `.js` files are accepted. A local `renderer-scripts/` folder next to the patcher exe is still copied as an advanced fallback.
18- `web-panel/bridge/scripts/default/installed-apps-sync.js` resolves Wand's renderer services/store and publishes `My Games` snapshots through the `wand-remote-installed-apps` IPC channel. The synced list must mirror Wand's `my_games` source criteria: catalog games come from `installedGameVersions`, and extra installed unsupported titles come from `correlatedUnavailableTitles` whose `games[].correlationIds` match `installedApps`.
19- If the injected renderer cannot read a populated `correlatedUnavailableTitles` slice from the live store, `installed-apps-sync.js` must fall back to Wand's `/v3/unavailable_titles` correlation lookup through the renderer API client instead of degrading to raw install entries or an empty `My Games` list.
20- Installed app snapshots should include game artwork in `imageUrl` when possible. `installed-apps-sync.js` must prefer Wand's own client icon CDN shape `https://api-cdn.wemod.com/steam_community/<steamAppId>/client_icon/96.webp` whenever the matched title/game/version metadata contains a Steam AppID, regardless of install platform. Do not assume `steamAppId` is a flat property; search nested `steam*` metadata before falling back to installed Steam `sku`. If metadata still does not expose the icon, fall back to the rendered Wand sidebar DOM (`.sidebar-game-row-image` background-image) keyed by `titleId` parsed from `data-tooltip-trigger-for`. The web panel `GameCover` must tolerate broken artwork URLs and fall back to its text cover.
21- The same renderer sync script also forwards lifecycle state through `wand-remote-game-status`: `game-launched` / `game-ended` come from Wand's launch monitor service, and trainer runtime comes from the running-trainer visibility service. The web panel consumes this as the `game_status` websocket message.
22- When Wand does not emit a `game-launched` event but a trainer is already active, `wand-remote-game-status` must synthesize a running session from the running-trainer visibility payload so the remote panel does not show an idle game session next to a running trainer.
23- The websocket `hello` snapshot must still send cached `installed_apps` and `game_status` even when no trainer snapshot is active yet; do not reintroduce a handshake path that returns early after `trainer_changed`.
24- Remote Play/Stop uses the websocket `remote_command` message. The bridge forwards it over `wand-remote-command` / `wand-remote-command-response`, and `installed-apps-sync.js` resolves Wand's trainer API + trainer service to launch a trainer for a `gameId` or end the current trainer.
25- Remote Play must construct Wand's real trainer launch request class (`69482.vO`) before calling `trainerService.launch(...)`. Passing a plain object launches the game process but breaks Wand's `getMetadata(vO)`-based trainer state, causing missing status, disappearing play/close buttons, and stuck loading behavior.
26- Pro activation is a C# asar patch (`EPatchType.ActivatePro`, independent of the remote panel / bridge). It rewrites three account-returning service methods to inject `subscription:{period:"yearly",state:"active"}` into the response before it reaches the store: `getUserAccount` and `setAccountWandBrandExperience` (Resolver-style, service field via `<service_name>` placeholder) and `setAccountLanguage` (`BuildSetAccountLanguagePatch` PatchFactory — captures the real param names + the original `post("/v3/account/language",{...})` expr and wraps `.then`). A fourth patch (`setAccountReducer`) rewrites the `ACTION_SET_ACCOUNT` store reducer so any account write (periodic `refreshAccount`, push/profile updates, etc.) keeps Pro even when it bypasses those API methods. Pro is `am(account) = !!account.subscription` (flags/512 are irrelevant). `setAccountLanguage` is the one the original two patches missed, which is why Pro dropped on language change. If a future Wand build changes these method bodies, re-derive the regexes against the live `app-*.bundle.js` (do NOT trust `.source/new` — it is a different version).
27
28## ASAR Patch Pipeline
29
30- Preserve and restore both `resources/app.asar` and `resources/app.asar.unpacked` backups.
31- Inject `web-panel/dist` as `remote-panel/`; it must already contain `bridge.cjs` and generated default renderer scripts under `renderer-scripts/`. Selected/local custom renderer scripts are then copied under `remote-panel/renderer-scripts`.
32- Do not commit extracted `.source/` or `.sources/` output. Recreate it only for reverse-engineering sessions.
33- `AsarSharp.AsarExtractor.ExtractAll` must skip unpacked entries when their source path equals the destination (in-place extraction is a self-copy that fails on locked files like `TrainerLib_x64.dll`) and silently skip unpacked entries whose source is missing on disk (e.g. `auxiliary/GameLauncher.exe` removed by an installer). Do not reintroduce hard failure on either case.
34- The `DevToolsOnF12` patch anchors on the Electron main-process `<app>.whenReady().then(` site and attaches a `before-input-event` hook to every `BrowserWindow.webContents`. Do not patch the renderer keydown listener — the minified `ACTION_OPEN_DEV_TOOLS` dispatch site is not stable across Wand releases.
35- Cheats can be pinned per game in the web panel via `pinned-storage.ts` (`localStorage` key `wand-remote.pinned-cheats.v1:<gameId>`). Pinned cheats render as a virtual `pinned` category at the top of the list; their normal category placement is preserved.
36- Custom quick presets are per trainer/game and stored by `preset-storage.ts` under `localStorage` key `wand-remote.presets.v1:<gameId-or-trainerId>`. Presets capture persistent cheat values only; do not include `button` one-shot cheats in saved presets.
37- All `localStorage` access in `web-panel/src/` MUST go through `web-panel/src/shared/storage.ts` (`loadJson` / `saveJson` / `loadStringSet` / `saveStringSet`). Do not reintroduce per-capability `try/catch` + `JSON.parse` duplication. Trainer/game storage IDs are derived through the shared `getTrainerStorageId(trainer)` helper; do not re-implement the `gameId → titleId → trainerId → 'global'` precedence inline.
38- Shared web protocol version, port, and HTTP/WS paths live in `web-panel/protocol/web-contract.json`. Bridge-only IPC channels, WS opcodes, and renderer injection delays live in `web-panel/bridge/src/constants.ts`. Do not redeclare these values inline.
39- UI string-union types follow the `E*` enum convention from `.claude/rules/frontend-conventions.md` (currently `ECheatType` in `protocol/messages.ts`, `EConnectionStatus` in `remote-session/remote-session.reducer.ts`); wire string values must remain on the right-hand side of enum members. Reducer action tags stay as discriminated-union string literals.
40- Cheat input controls live one-per-file under `web-panel/src/trainer/controls/`; shared `SliderTrack` / `StepButton` / `ControlInternalProps` are in `controls/shared.tsx` and number formatting helpers in `controls/format-number.ts`. `controls/CheatControl.tsx` remains a thin dispatcher keyed by `ECheatType`.
41- Mobile drawer performance is sensitive to `backdrop-filter`. Keep drawer panels and nested glass controls blur-free under coarse pointers, and do not add per-row `backdrop-blur-*` inside drawer lists.
42
43## Validation
44
45- Web panel build: `cd web-panel && pnpm run build` (runs type-check, Vite build, then `build:bridge` into `dist`).
46- Bridge/script syntax checks after build: `node --check web-panel/dist/bridge.cjs` and `node --check web-panel/dist/renderer-scripts/remote-popup-cleanup.js`.
47- Production dist should contain only static assets and should not contain `mock-instance`, `Mock Adventure`, `Simulation`, `Debug session`, `mock=1`, `demo-session`, `vite.svg`, `tailwind-merge`, `class-variance-authority`, or `clsx`.
48
@@ −1 +1 @@
1−Use these rules as defaults, not as a reason to add ceremonial folders or wrapper layers.
1+INFO ./docs/*
22
3−## Core Principles
3+# Wand Enhancer Agent Notes
44
5−- Organize code around product capabilities, not framework vocabulary.
6−- Keep related UI, state, rules, and data access close until a real boundary justifies moving
7− them apart.
8−- Dependencies point from composition and UI toward stable rules and narrow capabilities.
9−- Protect rendering code from business, state-management, and infrastructure complexity.
10−- Keep one source of truth and derive everything else.
11−- Apply KISS, YAGNI, and DRY together. Remove duplicated knowledge, not merely similar syntax.
12−- Prefer explicit, readable flow over clever abstractions and hidden behavior.
5+This repository patches the Wand Electron app from a .NET Framework WPF desktop tool. Keep changes narrow and preserve the patch pipeline invariants.
136
14−## Screaming Architecture
7+## Remote Web Panel
158
16−The repository structure and public APIs should reveal what the product does.
9+- The default local remote port is `3223`. Keep bridge and frontend constants aligned; C# must not duplicate the presentation URL or port.
10+- The embedded panel must stay small because the desktop patcher embeds it and then injects it into Wand's `app.asar`.
11+- Remote tooltip links and every rendered `remote-qr-code` are redirected by `web-panel/bridge/scripts/default/remote-popup-cleanup.js`. It reuses Wand's loaded QR renderer through the webpack runtime, keeps the local URL visible as a fallback, and hides the Pro onboarding remote mobile app card. Do not reintroduce C# ASAR patches for the tooltip URL or QR component; a changed UI bundle must not make the whole remote-panel patch fail.
12+- Production builds must not include mock data, debug routes, sourcemaps, local fonts, heavy icon libraries, or runtime class helper packages.
13+- The Electron bridge is authored as TypeScript under `web-panel/bridge/src/`, but production runtime must be bundled/minified into `web-panel/dist/bridge.cjs` by `pnpm run build:bridge`. Do not copy bridge source into Wand or embed it as ASAR resources.
14+- Mock/demo data is dev-only and must be reached through `import.meta.env.DEV` dynamic imports.
15+- Source can use React-compatible imports, but production runtime resolves them to Preact aliases in `web-panel/vite.config.ts`.
16+- UI uses Tailwind CSS and lightweight local primitives under `web-panel/src/shared/ui/`.
17+- Default renderer script sources live in `web-panel/bridge/scripts/default/` and are bundled/minified into `web-panel/dist/renderer-scripts/` by `pnpm run build:bridge`. Custom user scripts are selected in the WPF patch modal and copied from `PatchConfig.CustomScriptPaths`; only existing `.js` files are accepted. A local `renderer-scripts/` folder next to the patcher exe is still copied as an advanced fallback.
18+- `web-panel/bridge/scripts/default/installed-apps-sync.js` resolves Wand's renderer services/store and publishes `My Games` snapshots through the `wand-remote-installed-apps` IPC channel. The synced list must mirror Wand's `my_games` source criteria: catalog games come from `installedGameVersions`, and extra installed unsupported titles come from `correlatedUnavailableTitles` whose `games[].correlationIds` match `installedApps`.
19+- If the injected renderer cannot read a populated `correlatedUnavailableTitles` slice from the live store, `installed-apps-sync.js` must fall back to Wand's `/v3/unavailable_titles` correlation lookup through the renderer API client instead of degrading to raw install entries or an empty `My Games` list.
20+- Installed app snapshots should include game artwork in `imageUrl` when possible. `installed-apps-sync.js` must prefer Wand's own client icon CDN shape `https://api-cdn.wemod.com/steam_community/<steamAppId>/client_icon/96.webp` whenever the matched title/game/version metadata contains a Steam AppID, regardless of install platform. Do not assume `steamAppId` is a flat property; search nested `steam*` metadata before falling back to installed Steam `sku`. If metadata still does not expose the icon, fall back to the rendered Wand sidebar DOM (`.sidebar-game-row-image` background-image) keyed by `titleId` parsed from `data-tooltip-trigger-for`. The web panel `GameCover` must tolerate broken artwork URLs and fall back to its text cover.
21+- The same renderer sync script also forwards lifecycle state through `wand-remote-game-status`: `game-launched` / `game-ended` come from Wand's launch monitor service, and trainer runtime comes from the running-trainer visibility service. The web panel consumes this as the `game_status` websocket message.
22+- When Wand does not emit a `game-launched` event but a trainer is already active, `wand-remote-game-status` must synthesize a running session from the running-trainer visibility payload so the remote panel does not show an idle game session next to a running trainer.
23+- The websocket `hello` snapshot must still send cached `installed_apps` and `game_status` even when no trainer snapshot is active yet; do not reintroduce a handshake path that returns early after `trainer_changed`.
24+- Remote Play/Stop uses the websocket `remote_command` message. The bridge forwards it over `wand-remote-command` / `wand-remote-command-response`, and `installed-apps-sync.js` resolves Wand's trainer API + trainer service to launch a trainer for a `gameId` or end the current trainer.
25+- Remote Play must construct Wand's real trainer launch request class (`69482.vO`) before calling `trainerService.launch(...)`. Passing a plain object launches the game process but breaks Wand's `getMetadata(vO)`-based trainer state, causing missing status, disappearing play/close buttons, and stuck loading behavior.
26+- Pro activation is a C# asar patch (`EPatchType.ActivatePro`, independent of the remote panel / bridge). It rewrites three account-returning service methods to inject `subscription:{period:"yearly",state:"active"}` into the response before it reaches the store: `getUserAccount` and `setAccountWandBrandExperience` (Resolver-style, service field via `<service_name>` placeholder) and `setAccountLanguage` (`BuildSetAccountLanguagePatch` PatchFactory — captures the real param names + the original `post("/v3/account/language",{...})` expr and wraps `.then`). A fourth patch (`setAccountReducer`) rewrites the `ACTION_SET_ACCOUNT` store reducer so any account write (periodic `refreshAccount`, push/profile updates, etc.) keeps Pro even when it bypasses those API methods. Pro is `am(account) = !!account.subscription` (flags/512 are irrelevant). `setAccountLanguage` is the one the original two patches missed, which is why Pro dropped on language change. If a future Wand build changes these method bodies, re-derive the regexes against the live `app-*.bundle.js` (do NOT trust `.source/new` — it is a different version).
1727
18−Prefer:
28+## ASAR Patch Pipeline
1929
20−```text
21−features/
22− checkout/
23− search/
24− account-security/
25−```
30+- Preserve and restore both `resources/app.asar` and `resources/app.asar.unpacked` backups.
31+- Inject `web-panel/dist` as `remote-panel/`; it must already contain `bridge.cjs` and generated default renderer scripts under `renderer-scripts/`. Selected/local custom renderer scripts are then copied under `remote-panel/renderer-scripts`.
32+- Do not commit extracted `.source/` or `.sources/` output. Recreate it only for reverse-engineering sessions.
33+- `AsarSharp.AsarExtractor.ExtractAll` must skip unpacked entries when their source path equals the destination (in-place extraction is a self-copy that fails on locked files like `TrainerLib_x64.dll`) and silently skip unpacked entries whose source is missing on disk (e.g. `auxiliary/GameLauncher.exe` removed by an installer). Do not reintroduce hard failure on either case.
34+- The `DevToolsOnF12` patch anchors on the Electron main-process `<app>.whenReady().then(` site and attaches a `before-input-event` hook to every `BrowserWindow.webContents`. Do not patch the renderer keydown listener — the minified `ACTION_OPEN_DEV_TOOLS` dispatch site is not stable across Wand releases.
35+- Cheats can be pinned per game in the web panel via `pinned-storage.ts` (`localStorage` key `wand-remote.pinned-cheats.v1:<gameId>`). Pinned cheats render as a virtual `pinned` category at the top of the list; their normal category placement is preserved.
36+- Custom quick presets are per trainer/game and stored by `preset-storage.ts` under `localStorage` key `wand-remote.presets.v1:<gameId-or-trainerId>`. Presets capture persistent cheat values only; do not include `button` one-shot cheats in saved presets.
37+- All `localStorage` access in `web-panel/src/` MUST go through `web-panel/src/shared/storage.ts` (`loadJson` / `saveJson` / `loadStringSet` / `saveStringSet`). Do not reintroduce per-capability `try/catch` + `JSON.parse` duplication. Trainer/game storage IDs are derived through the shared `getTrainerStorageId(trainer)` helper; do not re-implement the `gameId → titleId → trainerId → 'global'` precedence inline.
38+- Shared web protocol version, port, and HTTP/WS paths live in `web-panel/protocol/web-contract.json`. Bridge-only IPC channels, WS opcodes, and renderer injection delays live in `web-panel/bridge/src/constants.ts`. Do not redeclare these values inline.
39+- UI string-union types follow the `E*` enum convention from `.claude/rules/frontend-conventions.md` (currently `ECheatType` in `protocol/messages.ts`, `EConnectionStatus` in `remote-session/remote-session.reducer.ts`); wire string values must remain on the right-hand side of enum members. Reducer action tags stay as discriminated-union string literals.
40+- Cheat input controls live one-per-file under `web-panel/src/trainer/controls/`; shared `SliderTrack` / `StepButton` / `ControlInternalProps` are in `controls/shared.tsx` and number formatting helpers in `controls/format-number.ts`. `controls/CheatControl.tsx` remains a thin dispatcher keyed by `ECheatType`.
41+- Mobile drawer performance is sensitive to `backdrop-filter`. Keep drawer panels and nested glass controls blur-free under coarse pointers, and do not add per-row `backdrop-blur-*` inside drawer lists.
2642
27−Avoid making the application read primarily as:
43+## Validation
2844
29−```text
30−components/
31−hooks/
32−services/
33−stores/
34−utils/
35−```
36−
37−Technical folders are useful inside a capability, where their owner is clear. Generic top-level
38−folders easily become dependency magnets with unclear ownership.
39−
40−Names should use product language. Prefer `useCheckoutSummary`, `reserveStock`, and
41−`AccountSecurityPanel` over `useData`, `processItems`, and `GenericPanel`.
42−
43−## Suggested Structure
44−
45−Start with the smallest structure that makes ownership obvious:
46−
47−```text
48−src/
49− app/ startup, providers, router, global composition
50− pages/ route-level composition
51− features/
52− <capability>/
53− index.ts optional public API
54− ui/ optional rendering components
55− model/ optional state, view models, decisions
56− api/ optional external data access
57− lib/ optional feature-local pure helpers
58− domains/ optional shared product rules and types
59− shared/
60− ui/ domain-free visual primitives
61− api/ generic transport/query infrastructure
62− lib/ genuinely generic pure helpers
63−```
64−
65−Folders are created when they contain a real responsibility. A small feature may be one cohesive
66−file. Do not create empty layers in anticipation of future complexity.
67−
68−## Dependency Direction
69−
70−- `app` installs providers, constructs dependencies, and composes the application.
71−- `pages` compose capabilities for a route. They do not own business rules or data protocols.
72−- A feature owns one user-recognizable capability end to end.
73−- Feature UI consumes its own model/view-model API, not raw infrastructure.
74−- Shared domain code contains reusable product rules and stays independent of React and I/O.
75−- `shared` contains only domain-free code. Product-specific code is not shared merely because
76− two files use it.
77−- Avoid feature-to-feature imports. Compose features in a page, promote truly shared rules to a
78− domain module, or introduce a named workflow when coordination is the actual responsibility.
79−- Cyclic imports are an architecture problem, not something to solve with a tooling workaround.
80−
81−For a simple feature, direct `ui -> model -> api` dependencies are sufficient. Introduce ports,
82−facades, dependency injection, or workflows only when they hide real complexity, enable
83−important tests, or separate unstable infrastructure.
84−
85−## Make Composition Read Like The Product
86−
87−Pages and other composition boundaries should use capability-level APIs.
88−
89−Prefer:
90−
91−```tsx
92−<CheckoutSummary />
93−<PlaceOrderButton />
94−```
95−
96−Over:
97−
98−```tsx
99−<Card>
100− <Select options={paymentOptions} onChange={handlePaymentChange} />
101− <Button onClick={handleSubmit}>Submit</Button>
102−</Card>
103−```
104−
105−The second version makes the page understand checkout behavior and low-level UI configuration.
106−That knowledge belongs to the checkout capability.
107−
108−This does not mean wrapping every native element or design-system primitive. Semantic HTML and
109−visual primitives are correct inside feature UI. Create a capability component when it hides
110−product behavior or gives composition code a clearer product-level API.
111−
112−Avoid "raw components" whose consumers must know internal options, state transitions, query
113−shapes, or protocol details. Avoid generic configuration-driven components that combine
114−unrelated product modes behind dozens of props.
115−
116−## UI Boundary
117−
118−- Components render data and translate DOM events into named user intents.
119−- Keep business decisions, data mapping, persistence, protocol handling, and multi-step async
120− flows outside rendering components.
121−- UI receives render-ready values. It should not reconstruct domain meaning from raw DTOs.
122−- Prefer intent props and commands such as `onApprove`, `renameProject`, or `submitOrder` over
123− generic `onChange`, `setState`, or `patch` APIs at capability boundaries.
124−- Keep ephemeral visual state local: focus, hover, open/closed, and uncommitted input usually
125− belong in the component.
126−- Split components by responsibility and API clarity, not by arbitrary line limits.
127−- Prefer slots and composition over components with many layout modes and boolean props.
128−- Use semantic HTML and preserve accessibility behavior.
129−
130−A view-model hook is useful when it protects UI from state shape, async coordination, or business
131−decisions. Do not create a pass-through hook that only renames one value to satisfy a diagram.
132−
133−## State Ownership
134−
135−Choose the smallest correct owner:
136−
137−| State | Preferred owner |
138−| --- | --- |
139−| Ephemeral visual state | local component state |
140−| Uncommitted form state | the form or feature |
141−| URL/shareable navigation state | the router/URL |
142−| Remote server resource and cache | a query/cache layer |
143−| Shared capability state | that feature's model/store |
144−| Cross-capability process | a named workflow or app-level model |
145−
146−- A store is not a bucket for every value used by several components.
147−- Split state by capability and lifecycle, not by data type.
148−- Expose narrow selectors, hooks, or commands. Do not expose a complete mutable store to all UI.
149−- Store transitions should express user or domain intent, not generic object mutation.
150−- Derive values instead of storing synchronized copies.
151−- Do not use effects to keep two pieces of application state synchronized.
152−- React Context is suitable for dependency injection or stable scoped state. Avoid one broad
153− app context whose every update rerenders unrelated consumers.
154−
155−State-library choice is an implementation detail. Architecture should survive replacing it
156−without rewriting pages and rendering components.
157−
158−## Effects And Async Work
159−
160−- Use effects to synchronize with external systems, not to calculate render data or handle user
161− events.
162−- Start event-driven work from the event or model command that owns it.
163−- Every subscription, timer, listener, or in-flight operation must have a clear owner and
164− cleanup path.
165−- The owning feature/model defines pending, success, empty, error, retry, and cancellation
166− semantics.
167−- Prevent stale async results and race conditions where users can trigger overlapping work.
168−- Do not hide failures with broad `catch` blocks or silently convert errors into empty data.
169−
170−## Data And Infrastructure
171−
172−- Treat network responses, storage, URL input, files, and third-party SDK output as untrusted.
173−- Validate and normalize data at the boundary where it enters the application.
174−- Map transport DTOs and external errors into product-oriented values before they reach UI.
175−- Keep raw `fetch`, storage APIs, SDK calls, and protocol details out of rendering components.
176−- Keep a feature-specific API adapter inside the feature until it has a real shared consumer.
177−- Introduce a client, repository, gateway, service, or facade only when its responsibility is
178− distinct and useful.
179−- Avoid wrapper chains that only forward calls. One clear adapter is better than
180− `Client -> Service -> Facade` without separate responsibilities.
181−- Inject infrastructure when tests, multiple implementations, lifecycle, or unstable external
182− APIs justify it. Do not introduce dependency injection for every pure helper.
183−
184−## Component And Hook APIs
185−
186−- Component and hook APIs describe product intent, not internal implementation.
187−- Avoid boolean prop combinations that create unclear or invalid modes. Prefer explicit variants
188− or separate components.
189−- Avoid passing raw query results, stores, SDK clients, or large configuration objects through
190− component trees.
191−- Keep public props small and cohesive. A component that needs unrelated groups of props likely
192− owns too many responsibilities.
193−- Custom hooks encapsulate React state, lifecycle, or reusable reactive behavior. Pure
194− calculations remain plain functions.
195−- Do not use `useEffect`, `useMemo`, `useCallback`, or `memo` by habit. Use them for correctness
196− or measured performance needs.
197−- Do not duplicate server or domain state into component state merely to make it editable.
198− Create an explicit draft only when the UX requires commit/cancel semantics.
199−
200−## Public Boundaries
201−
202−- Export the smallest useful public surface of a feature.
203−- Consumers should use a feature's public components, hooks, commands, and types, not deep
204− internal paths.
205−- Keep implementation-only state, DTOs, adapters, and helpers private.
206−- Do not create barrel files everywhere. Use a public entry point only where a real boundary
207− exists.
208−- A reusable abstraction should have a clear owner and at least one current reason to exist.
209−- Avoid generic `core`, `common`, `helpers`, `services`, or `utils` modules that collect
210− unrelated responsibilities.
211−
212−## Growing The Architecture
213−
214−Start local and promote code only after pressure appears:
215−
216−- A second consumer may justify shared domain code, but similar code is not automatically the
217− same knowledge.
218−- Repeated external integration logic may justify a shared adapter.
219−- A process coordinating several capabilities may justify a named workflow.
220−- A large feature may split into smaller capabilities when they have distinct responsibilities
221− and lifecycles.
222−- Separate packages are useful when an enforceable boundary, independent reuse, or independent
223− lifecycle outweighs their maintenance cost.
224−
225−Do not begin a small application with every possible layer, package, provider, repository,
226−facade, and design pattern. Strong architecture makes growth cheaper; it does not predict every
227−future requirement.
228−
229−## Testing
230−
231−- Test product behavior and public contracts, not implementation trivia.
232−- Test pure rules with unit tests.
233−- Test feature models and async transitions without rendering where practical.
234−- Test components through accessible user behavior.
235−- Test infrastructure mapping and validation at external boundaries.
236−- Keep end-to-end tests for critical user journeys.
237−- Mock external systems and unstable boundaries, not every internal function.
238−- Add tests proportional to risk, especially for validation, permissions, races, retries,
239− cancellation, and regressions.
240−
241−## Review Checklist
242−
243−Before finishing a change, ask:
244−
245−- Does the file location make its owner obvious?
246−- Does composition code read in product language?
247−- Is UI protected from raw state, DTOs, infrastructure, and business decisions?
248−- Is there one source of truth?
249−- Are effects only synchronizing external systems?
250−- Is new shared code genuinely domain-free or genuinely shared?
251−- Does every abstraction remove current complexity?
252−- Can important behavior be tested without rendering the whole app?
253−- Did the change preserve accessibility, error handling, and cleanup?
254−- Is this the least code that clearly solves the current problem?
45+- Web panel build: `cd web-panel && pnpm run build` (runs type-check, Vite build, then `build:bridge` into `dist`).
46+- Bridge/script syntax checks after build: `node --check web-panel/dist/bridge.cjs` and `node --check web-panel/dist/renderer-scripts/remote-popup-cleanup.js`.
47+- Production dist should contain only static assets and should not contain `mock-instance`, `Mock Adventure`, `Simulation`, `Debug session`, `mock=1`, `demo-session`, `vite.svg`, `tailwind-merge`, `class-variance-authority`, or `clsx`.
25548
