{ "intent": "Bring kcd2_dice_solver/src/App.tsx (306 lines) under the repo-wide 250-line cap with no change to what the app renders or how the inventory behaves, completing the four-file kcd2 series.", "scope": [ "kcd2_dice_solver/src/App.tsx — reduced to the view: props, the search query, the solve request, and the JSX", "kcd2_dice_solver/src/hooks/useInventoryState.ts — new: InventoryState, useInventoryState", "Non-goal: any change to rendered markup, class names, ARIA roles or copy, which the four App test files query on", "Non-goal: a dedicated test file for the hook, which would change the 288-test count this split is verified against" ], "changes": [ "Extracted the inventory state into a useInventoryState hook under src/hooks/, following the existing useSolver pattern: the link-versus-storage resolution at mount, the deferred-persistence boolean and its storage effect, the fragment-clearing effect, and the setDieCount/toggleBadge mutators, all with their explanatory comments.", "Promoted the two inline handlers that only set state — Keep, and the Discard/Clear/import replacement — to named `keep` and `replace` callbacks on the hook, so the view no longer touches setPersist or setSaved directly. importInventory collapsed into `replace`, which Clear, Discard and InventoryIo's onImport now all use.", "The hook exposes `stored` as a plain SavedInventory rather than the loadInventory result, since Discard is the only consumer and it wants the inventory, not the parse report.", "Deliberately avoided a re-export: an initial draft had the hook re-export EMPTY, which is the barrel pattern this series is not using. App.tsx imports EMPTY from lib/inventoryIo.ts, its real home." ], "verification": [ { "command": "npx vitest run", "result": "pass", "evidence": "Test Files 24 passed (24); Tests 288 passed (288). The four App test files — AppStorage, AppShell, AppLinks, AppRecovery — exercise the extracted state through the component and all still pass unmodified." }, { "command": "npx vitest run --coverage", "result": "pass", "evidence": "Statements 100% (864/864), Branches 100% (375/375), Functions 100% (182/182), Lines 100% (811/811). Totals rose by exactly one statement and one function against the 863/375/181/810 baseline, which is the Keep handler becoming a named callback; branches are unchanged, so no conditional left test reach. This was the metric the brief warned would drop if the extraction were too large — it did not." }, { "command": "npm run lint (tsc --noEmit && eslint src)", "result": "pass", "evidence": "Exit 0, no output." }, { "command": "wc -l src/App.tsx src/hooks/useInventoryState.ts", "result": "pass", "evidence": "App.tsx 217, useInventoryState.ts 163 — both under the 250-line cap, down from a single 306-line file." }, { "command": "bash ~/utils/scripts/check_file_length.sh --all", "result": "pass", "evidence": "No kcd2_dice_solver file appears in the over-cap listing at all. This is the fourth and last of the four files the series targeted." }, { "command": "grep -rn 'eslint-disable|ts-ignore|ts-expect-error' kcd2_dice_solver/src/ ; git diff --stat vite.config.ts", "result": "pass", "evidence": "No hits and an empty diff: zero suppressions across the whole series, and the coverage thresholds and exclude list were never touched." }, { "command": "pre-commit run --files ", "result": "pass", "evidence": "All configured hooks pass on the touched files." } ], "risks": [ "The hook has no dedicated test file; its coverage comes entirely from the four App test files exercising it through the component. Coverage is 100% including branches, but a future change to the hook could be under-tested in a way a direct hook test would catch. Adding one was deliberately left out of scope because it would change the 288-test baseline this split is verified against.", "App.tsx and the hook now share a wide interface — eleven fields. That is the cost of extracting state rather than markup from a component with no prop-drilled subtree; a presentational split would have needed comparable prop plumbing.", "kcd2_dice_solver has no CI workflow and is not covered by pre-commit, so the vitest and lint runs above are the only gate on this change." ], "rollback": [ "git revert the commit — one edited file and one added file, with no data, config or schema migration.", "After rollback, re-run npx vitest run --coverage and npm run lint; expect 288 tests passing at 100% coverage (totals returning to 863/375/181/810) and lint exit 0." ] }