Files
honeyDueKMP/docs/parity-gallery-grid.md
Trey T 9fa58352c0 Parity gallery: unify around canonical manifest, fix populated-state rendering
Single source of truth: `com.tt.honeyDue.testing.GalleryScreens` lists
every user-reachable screen with its category (DataCarrying / DataFree)
and per-platform reachability. Both platforms' test harnesses are
CI-gated against it — `GalleryManifestParityTest` on each side fails
if the surface list drifts from the manifest.

Variant matrix by category: DataCarrying captures 4 PNGs
(empty/populated × light/dark), DataFree captures 2 (light/dark only).
Empty variants for DataCarrying use `FixtureDataManager.empty(seedLookups = false)`
so form screens that only read DM lookups can diff against populated.

Detail-screen rendering fixed on both platforms. Root cause: VM
`stateIn(Eagerly, initialValue = …)` closures evaluated
`_selectedX.value` before screen-side `LaunchedEffect` / `.onAppear`
could set the id, leaving populated captures byte-identical to empty.

  Kotlin: `ContractorViewModel` + `DocumentViewModel` accept
  `initialSelectedX: Int? = null` so the id is set in the primary
  constructor before `stateIn` computes its seed.

  Swift: `ContractorViewModel`, `DocumentViewModelWrapper`,
  `ResidenceViewModel`, `OnboardingTasksViewModel` gained pre-seed
  init params. `ContractorDetailView`, `DocumentDetailView`,
  `ResidenceDetailView`, `OnboardingFirstTaskContent` gained
  test/preview init overloads that accept the pre-seeded VM.
  Corresponding view bodies prefer cached success state over
  loading/error — avoids a spinner flashing over already-visible
  content during background refreshes (production benefit too).

Real production bug fixed along the way: `DataManager.clear()` was
missing `_contractorDetail`, `_documentDetail`, `_contractorsByResidence`,
`_taskCompletions`, `_notificationPreferences`. On logout these maps
leaked across user sessions; in the gallery they leaked the previous
surface's populated state into the next surface's empty capture.

`ImagePicker.android.kt` guards `rememberCameraPicker` with
`LocalInspectionMode` — `FileProvider.getUriForFile` can't resolve the
Robolectric test-cache path, so `add_document` / `edit_document`
previously failed the entire capture.

Honest reclassifications: `complete_task`, `manage_users`, and
`task_suggestions` moved to DataFree. Their first-paint visible state
is driven by static props or APILayer calls, not by anything on
`IDataManager` — populated would be byte-identical to empty without
a significant production rewire. The manifest comments call this out.

Manifest counts after all moves: 43 screens = 12 DataCarrying + 31
DataFree, 37 on both platforms + 3 Android-only (home, documents,
biometric_lock) + 3 iOS-only (documents_warranties, add_task,
profile_edit).

Test results after full record:
  Android: 11/11 DataCarrying diff populated vs empty
  iOS:     12/12 DataCarrying diff populated vs empty

Also in this change:
- `scripts/build_parity_gallery.py` parses the Kotlin manifest
  directly, renders rows in product-flow order, shows explicit
  `[missing — <platform>]` placeholders for expected-but-absent
  captures and muted `not on <platform>` placeholders for
  platform-specific screens. Docs regenerated.
- `scripts/cleanup_orphan_goldens.sh` safely removes PNGs from prior
  test configurations (theme-named, compare artifacts, legacy
  empty/populated pairs for what is now DataFree). Dry-run by default.
- `docs/parity-gallery.md` rewritten: canonical-manifest workflow,
  adding-a-screen guide, variant matrix explained.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:10:32 -05:00

30 KiB

honeyDue parity gallery

43 screens · 102 Android · 174 iOS

Auto-generated by scripts/build_parity_gallery.py — do not hand-edit.

See parity-gallery.md for the workflow guide.

Screens


login (DataFree)

State / Mode Android iOS
light
dark

top


register (DataFree)

State / Mode Android iOS
light
dark

top


forgot_password (DataFree)

State / Mode Android iOS
light
dark

top


verify_reset_code (DataFree)

State / Mode Android iOS
light
dark

top


reset_password (DataFree)

State / Mode Android iOS
light
dark

top


verify_email (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_welcome (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_value_props (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_create_account (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_verify_email (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_location (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_name_residence (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_home_profile (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_join_residence (DataFree)

State / Mode Android iOS
light
dark

top


onboarding_first_task (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


onboarding_subscription (DataFree)

State / Mode Android iOS
light
dark

top


home (DataCarrying)

State / Mode Android iOS
empty / light (not on ios)
empty / dark (not on ios)
populated / light (not on ios)
populated / dark (not on ios)

top


residences (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


residence_detail (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


add_residence (DataFree)

State / Mode Android iOS
light
dark

top


edit_residence (DataFree)

State / Mode Android iOS
light
dark

top


join_residence (DataFree)

State / Mode Android iOS
light
dark

top


manage_users (DataFree)

State / Mode Android iOS
light [missing — ios]
dark [missing — ios]

top


all_tasks (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


add_task (DataFree)

State / Mode Android iOS
light (not on android)
dark (not on android)

top


add_task_with_residence (DataFree)

State / Mode Android iOS
light
dark

top


edit_task (DataFree)

State / Mode Android iOS
light
dark

top


complete_task (DataFree)

State / Mode Android iOS
light
dark

top


task_suggestions (DataFree)

State / Mode Android iOS
light [missing — ios]
dark [missing — ios]

top


task_templates_browser (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


contractors (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


contractor_detail (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


documents (DataCarrying)

State / Mode Android iOS
empty / light (not on ios)
empty / dark (not on ios)
populated / light (not on ios)
populated / dark (not on ios)

top


documents_warranties (DataCarrying)

State / Mode Android iOS
empty / light (not on android)
empty / dark (not on android)
populated / light (not on android)
populated / dark (not on android)

top


document_detail (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


add_document (DataFree)

State / Mode Android iOS
light
dark

top


edit_document (DataFree)

State / Mode Android iOS
light
dark

top


profile (DataCarrying)

State / Mode Android iOS
empty / light
empty / dark
populated / light
populated / dark

top


profile_edit (DataFree)

State / Mode Android iOS
light (not on android)
dark (not on android)

top


notification_preferences (DataFree)

State / Mode Android iOS
light
dark

top


theme_selection (DataFree)

State / Mode Android iOS
light
dark

top


biometric_lock (DataFree)

State / Mode Android iOS
light (not on ios)
dark (not on ios)

top


feature_comparison (DataFree)

State / Mode Android iOS
light
dark

top