| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 4 | 14 | 0% |
| Commands | 0 | 3 | 0 | 0% |
| Section tags | 3 | 4 | 4 | 27% |
What each file covers
Sections
0 shared · 4 only in A · 14 only in B- − Wand Enhancer Agent Notes
- − Remote Web Panel
- − ASAR Patch Pipeline
- − Validation
- + 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
Commands
0 shared · 3 only in A · 0 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- − build
- − security
- − deployment
- − agent-behaviour
- + test
- + architecture
- + git-pr
- + ui
- code-style
- testing-strategy
- do-not
Line diff
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
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
@@ −1 +1 @@
1−INFO ./docs/*
1+Use these rules as defaults, not as a reason to add ceremonial folders or wrapper layers.
22
3−# Wand Enhancer Agent Notes
3+## Core Principles
44
5−This repository patches the Wand Electron app from a .NET Framework WPF desktop tool. Keep changes narrow and preserve the patch pipeline invariants.
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.
613
7−## Remote Web Panel
14+## Screaming Architecture
815
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).
16+The repository structure and public APIs should reveal what the product does.
2717
28−## ASAR Patch Pipeline
18+Prefer:
2919
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.
20+```text
21+features/
22+ checkout/
23+ search/
24+ account-security/
25+```
4226
43−## Validation
27+Avoid making the application read primarily as:
4428
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`.
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?
48255
