> For the complete documentation index, see [llms.txt](https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/.spec_system/archive/sessions/phase32-session02-preference-contract/spec.md).

# Session Specification

**Session ID**: `phase32-session02-preference-contract` **Phase**: 32 - AI Rogue Mobile Input Auto-Detect **Status**: Not Started **Created**: 2026-06-24

***

## 1. Session Overview

This session adds the durable AI Rogue input preference contract needed for the mobile first-run fix. The raw browser-local preference becomes `auto | keyboard | compact`, while runtime-facing input mode stays concrete as `keyboard | compact`.

This is next because Session 01 validated the current failure and the media query heuristic. Sessions 03-05 depend on the raw/effective type split, resolver, mounted browser capability hook, and schema/persistence tests created here.

The session does not wire the effective mode through the Play route, Runtime Canvas compact controls, Settings segmented control, HUD copy, or Playwright gameplay smoke. Those are explicitly split into Sessions 03-05.

***

## 2. Objectives

1. Extend durable AI Rogue preferences so fresh, missing, malformed, and migrated records default to raw `inputMode: "auto"`.
2. Preserve explicit saved `keyboard` and `compact` preferences during parse, write, and migration paths.
3. Add raw and effective input-mode types plus a pure resolver that maps browser capabilities to concrete runtime input modes.
4. Add a mounted browser hook or utility that reads pointer and hover media queries after mount, subscribes to supported changes, and cleans up listeners.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase32-session01-baseline-validation` - Validated the fresh mobile no-preferences failure, explicit override behavior, desktop fallback, and Session 02 media-query heuristic.

### Required Tools Or Knowledge

* Bun 1.3.14, Vitest, TypeScript, React 19, Zod 4, and happy-dom test utilities.
* Existing AI Rogue persistence files and tests: `src/extensions/ai-rogue/save-schema.ts`, `src/extensions/ai-rogue/persistence.ts`, `src/extensions/ai-rogue/use-save-state.ts`, `src/extensions/ai-rogue/__tests__/save-schema.test.ts`, `src/extensions/ai-rogue/__tests__/persistence.test.ts`, and `src/extensions/ai-rogue/__tests__/use-save-state.test.tsx`.
* Existing runtime concrete contract in `src/extensions/ai-rogue/runtime/types.ts` and `src/extensions/ai-rogue/runtime/renderer.ts`.

### Environment Requirements

* Local dependencies installed with Bun.
* No browser hardware is required for this session; media query behavior is covered with injected capabilities and mocked `matchMedia`.

***

## 4. Scope

### In Scope (MVP)

* AI Rogue player gets a durable `auto` preference - extend Zod schema, exported types, default creation, parse, and migration behavior.
* AI Rogue player keeps explicit choices - ensure saved `keyboard` and `compact` values survive reads, writes, and legacy migration.
* AI Rogue runtime keeps a concrete API - add an effective mode type and ensure runtime-facing call sites still provide `keyboard | compact`.
* AI Rogue mounted React code can detect capabilities - add a browser-side hook or utility that reads `(pointer: coarse)` and `(hover: hover)` after mount and unsubscribes on cleanup.
* AI Rogue tests lock the contract - add focused schema, persistence, resolver, and hook tests.

### Out Of Scope (Deferred)

* Runtime Canvas behavioral wiring - Reason: Session 03 owns passing resolved effective mode into mount/update calls, compact controls, and canvas pointer enablement.
* Settings segmented-control UI and copy - Reason: Session 04 owns visible Auto controls and resolved-mode labels.
* Loadout, Play, compact-control, and HUD copy alignment - Reason: Session 04 owns raw/effective copy across product surfaces.
* Playwright mobile gameplay and public-demo smoke - Reason: Session 05 owns first-run gameplay proof and no-bridge route coverage.
* AI Rogue default extension enablement - Reason: P30 default enablement remains deferred and requires a separate product decision.

***

## 5. Technical Approach

### Architecture

Introduce a raw/effective split at the AI Rogue browser-local boundary. Raw preferences live in `save-schema.ts` and can be `auto`, `keyboard`, or `compact`. Effective input mode is a separate concrete type used by runtime mount/update preferences and remains `keyboard | compact`.

Place the new resolver and mounted capability hook outside `src/extensions/ai-rogue/runtime/` so the PixiJS runtime and renderer do not read browser globals or import React. The pure resolver accepts injected capability values, returns concrete mode, and falls back to keyboard when APIs or capability values are unavailable.

Because widening `AiRoguePreferences.inputMode` can make existing runtime-facing calls receive raw `auto`, include only the minimal compile-safety adapter needed to keep runtime calls concrete. Do not enable mobile compact behavior in Runtime Canvas in this session; Session 03 wires the mounted hook into Play and Runtime Canvas behavior.

### Design Patterns

* Raw/effective type split: Keeps durable user preference adaptive without weakening the runtime contract.
* Injected capabilities: Makes the resolver deterministic and testable without browser globals.
* Mounted browser hook: Avoids SSR and hydration instability by reading `matchMedia` after mount.
* Local-first schema defaults: Uses existing Zod defaults and migration helpers for browser-local persistence.

***

## 6. Deliverables

### Files To Create

| File                                                    | Purpose                                                                                                                         | Est. Lines |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `src/extensions/ai-rogue/input-mode.ts`                 | Raw/effective input-mode constants, pure resolver, browser capability snapshot, and mounted media-query hook                    | \~140      |
| `src/extensions/ai-rogue/__tests__/input-mode.test.tsx` | Resolver and hook tests for coarse/no-hover, fine/hover, unavailable APIs, explicit overrides, media-query changes, and cleanup | \~180      |

### Files To Modify

| File                                                        | Changes                                                                                                                                      | Est. Lines |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `src/extensions/ai-rogue/save-schema.ts`                    | Extend preference schema/defaults/migration with raw `auto`, export raw/effective input-mode types, and preserve explicit concrete values    | \~35       |
| `src/extensions/ai-rogue/persistence.ts`                    | Ensure preference reads/writes continue schema-validating raw `auto` and malformed storage resets to the new default                         | \~15       |
| `src/extensions/ai-rogue/use-save-state.ts`                 | Ensure save-state defaults and mutation typing accept raw `auto` without browser global reads                                                | \~10       |
| `src/extensions/ai-rogue/views/runtime-canvas.tsx`          | Add minimal concrete-mode adapter only if needed to keep runtime mount/update preferences from receiving raw `auto` before Session 03 wiring | \~20       |
| `src/extensions/ai-rogue/__tests__/save-schema.test.ts`     | Update default, malformed, legacy migration, and explicit override assertions for raw `auto`                                                 | \~35       |
| `src/extensions/ai-rogue/__tests__/persistence.test.ts`     | Add read/write assertions for default `auto` and explicit concrete preservation                                                              | \~30       |
| `src/extensions/ai-rogue/__tests__/use-save-state.test.tsx` | Assert empty browser-local save state exposes raw `auto` and preference writes preserve explicit overrides                                   | \~25       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] Empty preferences produce raw `inputMode: "auto"`.
* [ ] Missing preference fields default raw `inputMode` to `"auto"`.
* [ ] Malformed preference storage resets to raw `inputMode: "auto"`.
* [ ] Legacy v0 preferences without input mode migrate to raw `"auto"`.
* [ ] Explicit saved `keyboard` and `compact` preferences parse and write unchanged.
* [ ] Resolver maps raw `auto` plus coarse pointer and no hover to effective `compact`.
* [ ] Resolver maps raw `auto` plus fine pointer and hover to effective `keyboard`.
* [ ] Resolver maps raw `auto` plus unavailable media-query APIs to effective `keyboard`.
* [ ] Runtime-facing types and runtime modules do not accept raw `"auto"`.

### Testing Requirements

* [ ] Unit tests written and passing for schema defaults, migration, explicit overrides, persistence reads/writes, resolver cases, and hook lifecycle.
* [ ] Focused AI Rogue Vitest files pass.
* [ ] Typecheck passes for the raw/effective type boundary.

### Non-Functional Requirements

* [ ] New input-mode utilities do not import PixiJS or `src/extensions/ai-rogue/runtime`.
* [ ] Browser global reads are confined to the mounted hook or browser utility, not schema, persistence, or save-state modules.
* [ ] Media-query subscriptions clean up on unmount or capability changes.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.
* [ ] Product-facing UI changes are limited to compile-safety needs; visible Auto control/copy changes remain deferred to Session 04.

***

## 8. Implementation Notes

### Working Assumptions

* A minimal concrete-mode adapter may be necessary in `runtime-canvas.tsx` once `AiRoguePreferences.inputMode` includes `auto`: TypeScript currently passes `preferences.inputMode` directly into runtime mount/update preferences, while `runtime/types.ts` accepts only `keyboard | compact`. It is safe to proceed because the adapter preserves the runtime concrete contract and defers mounted capability behavior to Session 03.
* The new input-mode helper should live at `src/extensions/ai-rogue/input-mode.ts`: existing AI Rogue extension utilities live beside `save-schema.ts`, `persistence.ts`, and `use-save-state.ts`, while the runtime folder is kept Pixi-owned and route-lazy. Planning can proceed because this location keeps resolver code outside the renderer/runtime import boundary.

### Conflict Resolutions

* Session 02 asks for a mounted hook, while Session 03 owns effective mode wiring through Play and Runtime Canvas. The chosen interpretation is to create and test the hook in Session 02, but not wire it to product behavior until Session 03. This follows the phase split and keeps the current session focused on the preference contract.
* Settings and Loadout currently collapse non-keyboard values to Compact in display logic, while Session 04 owns copy alignment. The chosen interpretation is to avoid broad UI copy work here and record that Session 04 must handle visible Auto labels before release validation.

### Key Considerations

* The saved value should remain `auto`; mobile detection should not rewrite storage to `compact`.
* `navigator.maxTouchPoints` is supporting evidence only, not the primary resolver gate.
* The fallback for missing or unsupported browser media query APIs is keyboard.
* Explicit `keyboard` and `compact` choices always override capabilities.

### Potential Challenges

* Type widening can create compile errors in current runtime-facing call sites: add the smallest concrete adapter necessary and test that runtime types remain concrete.
* happy-dom does not provide full `matchMedia`: mock media query lists in `input-mode.test.tsx` and test both `addEventListener` and legacy `addListener` behavior when practical.
* UI copy may look incomplete between Sessions 02 and 04: keep visible product changes scoped and let the staged workflow continue to Session 04.

### Relevant Considerations

* \[P30] **AI Rogue default enablement deferred**: This session changes enabled AI Rogue preferences only; it does not make AI Rogue visible by default.
* \[P30] **Route-lazy runtime ownership scales**: Keep Pixi behind the Play route/local facade and do not move capability detection into the renderer.
* \[P30] **Schema-backed browser-local state works**: Use additive Zod defaults and migrations for browser-local preferences.
* \[P31] **Pages demo stays static-only**: Do not introduce hosted writes, bridge calls, collectors, analytics, or public-demo runtime assumptions.

### Behavioral Quality Focus

Checklist active: Yes

Top behavioral risks for this session:

* Persisted preferences may rewrite explicit user choices during migration.
* Browser media query subscriptions may leak listeners on route unmount.
* Runtime-facing code may accidentally accept raw `auto` after the schema type widens.

***

## 9. Testing Strategy

### Unit Tests

* Update `src/extensions/ai-rogue/__tests__/save-schema.test.ts` for raw `auto` defaults, malformed fallback, legacy migration, and explicit concrete values.
* Update `src/extensions/ai-rogue/__tests__/persistence.test.ts` for default reads, partial writes, and explicit `keyboard`/`compact` preservation.
* Add `src/extensions/ai-rogue/__tests__/input-mode.test.tsx` for resolver and hook behavior.

### Integration Tests

* Update `src/extensions/ai-rogue/__tests__/use-save-state.test.tsx` to prove empty browser-local state exposes raw `auto` and mutation guards still work.
* Run focused AI Rogue Vitest files that cover schema, persistence, save-state, and input mode utilities.

### Runtime Verification

* Run `bun run typecheck` to prove runtime-facing types remain concrete and raw `auto` is not accepted by runtime modules.

### Edge Cases

* Missing localStorage record.
* Malformed localStorage JSON.
* Legacy v0 preference object without `inputMode`.
* Explicit saved `keyboard`.
* Explicit saved `compact`.
* `window.matchMedia` unavailable.
* Media query change after mount.
* Hook cleanup on unmount.

***

## 10. Dependencies

### Other Sessions

* Depends on: `phase32-session01-baseline-validation`
* Depended by: `phase32-session03-effective-mode-wiring`, `phase32-session04-settings-and-copy`, `phase32-session05-gameplay-test-coverage`

***

## Next Steps

Run the `implement` workflow step to begin implementation.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/.spec_system/archive/sessions/phase32-session02-preference-contract/spec.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
