Skip to content

refactor(quality): code analysis cleanup - #322

Merged
deepiq2022 merged 1 commit into
mainfrom
working
Jul 25, 2026
Merged

refactor(quality): code analysis cleanup#322
deepiq2022 merged 1 commit into
mainfrom
working

Conversation

@deepiq2022

Copy link
Copy Markdown
Contributor

Summary

Addresses every finding in docs/code-analysis/2026-07-24-analysis.md.

  • Deduplication — one sheetStepper replaces 10 private copies; a _PersistedFlag base replaces 10 one-key prefs notifiers; one _pickBytes replaces 5 copies of the FilePicker preamble (whose error handling had already drifted). All 46 storage keys diffed against HEAD to prove none moved.
  • Sheet rolls are testable — 11 sheets called bare Random() in the widget layer with banding inline, so none of it could be asserted. Banding moved to a pure engine/sheet_rolls.dart, dice come from an injectable diceProvider. +34 tests where there were zero.
  • Found a live bugencounter_screen carried its own drifted copy of the HP ladder missing Embark and DCC, so those characters showed no HP in an encounter while the roster showed it fine. Both now use the shared characterHpPool; each sheet owns its clamp via a HasHpPool contract.
  • models.dart 5,663 → 33 LOC + 10 part files (one scope, so none of the ~90 import sites changed). Verified lossless: identical 144-symbol set.
  • Backups no longer fail silently — a real fault (full disk, revoked permission) is distinguished from platform unavailability and surfaced in Settings.
  • Coverage 85.5% → 86.4%; engine docs 72% → 100%; 7 new lint rules; .metadata restored (it had lost its android/ios/web entries to a tool run).

Two deliberate limits

  • Character keeps its 16 nullable sheet fields. Removing them touches hundreds of c.dnd-style call sites for nothing the player can see. Instead the failure mode the finding named is now guarded: character_sheet_slots_test.dart enumerates all 15 slots for JSON round-trip. Mutation-tested — deleting a toJson branch fails it with the intended "silently dropped on campaign export" message.
  • Only the 7 lint rules already at zero violations were enabled. avoid_redundant_argument_values (169) and directives_ordering (142) are churn, not quality; analysis_options.yaml records the reasons with measured counts.

Test plan

  • flutter analyze clean (at the tightened lint level)
  • flutter test — 2292 tests pass (was 2192)
  • Journal composer-focus + layout guard tests confirm element identity survived the build() extraction
  • Design-system suite green, including the ColorScheme migration ratchet
  • Manual smoke: open a Cairn/OSE sheet and tap a save button (snackbar wording unchanged)
  • Manual smoke: Settings sheet renders normally (no backup warning when healthy)

🤖 Generated with Claude Code

Addresses every finding in docs/code-analysis/2026-07-24-analysis.md. The
duplication went first: one `sheetStepper` replaced ten private copies across
the sheets, a `_PersistedFlag` base replaced ten hand-rolled one-key
SharedPreferences notifiers, and one `_pickBytes` replaced five copies of the
FilePicker preamble whose error handling had already drifted apart. All 46
storage keys were diffed against HEAD to prove none moved.

The substantive work was the sheet rolls. Eleven sheets called a bare
`Random()` in the widget layer with the pass/fail banding written inline, so
none of it could be asserted — an Argosa roll-under or a DCC spell check could
have been wrong in either direction and no test would have noticed. The
banding now lives in a pure `sheet_rolls.dart` and the dice come from an
injectable `diceProvider`, which buys 34 tests where there were none.

Collapsing the HP ladder found a live bug the report had not: `encounter_screen`
carried its own copy of it, missing Embark and DCC, so a character on either
sheet showed no HP at all in an encounter while the roster showed it fine. Both
now read the one `characterHpPool`, and each sheet owns its own clamp through a
`HasHpPool` contract.

Two deliberate limits. `Character` keeps its sixteen nullable sheet fields —
removing them touches hundreds of `c.dnd`-style call sites for nothing the
player can see — so instead the failure mode the finding actually named is now
guarded: `character_sheet_slots_test.dart` enumerates all fifteen slots for
JSON round-trip, and was mutation-tested by deleting a `toJson` branch to
confirm it fails with the intended "silently dropped on campaign export"
message. And of the stricter lints, only the seven already at zero violations
were enabled; `avoid_redundant_argument_values` (169) and `directives_ordering`
(142) are churn, not quality, and analysis_options.yaml records why with counts
so nobody re-litigates them.

`models.dart` is split into ten `part` files behind a 33-line header — verified
lossless by an identical 144-symbol set — which left the design-system test
reading it as a single file; that now scans the parts. 2292 tests pass, 86.4%
coverage, analyze clean at the tightened level.

Co-authored-by: John Taylor <jttaylor63@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
@deepiq2022
deepiq2022 merged commit 979765c into main Jul 25, 2026
1 check passed
@deepiq2022
deepiq2022 deleted the working branch July 25, 2026 02:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants