Phase 11: Verification & Coverage Gate (v1.2 milestone) - #11
Merged
Conversation
Phase 9 executed fully on 2026-08-20 (all 6 plans, atoms + demos + barrel exports shipped, 09-06 self-check passed) but only 09-03 carried 'status: complete' frontmatter, so the ROADMAP progress count derived 4/6 'In Progress'. This is a bookkeeping correction only — no code change. - Add status: complete + completed: 2026-08-20 to 09-01/02/04/05/06 SUMMARY - Flip ROADMAP Phase 9 row to '6/6 | Complete | 2026-08-20' Unblocks Phase 11 (Verification & Coverage Gate), which depends on Phases 8, 9, 10 and owns the formal verification sweep.
- Remove 42-line comment+test block at scaffold_selection_actions_test.dart:334-375 - Delete trailing blank line so Test 18 follows prior test with single blank separator - File goes from 929 to 886 lines (plan expected 887 — plan arithmetic off-by-one) - Suite now runs zero-skip; 18 non-skipped tests already cover onSelectionChanged via debugSimulateSelection
- Add 11-01-SUMMARY.md with verbatim gate evidence for 11-VERIFICATION.md - Records the 887-vs-886 plan arithmetic discrepancy (semantic intent satisfied)
- New example/test/capture_images_test.dart pumps all 26 demo widgets and writes one PNG per demo to ../images/ (D-02 WRITER harness) - Uses scaffoldThemeExtensions via canonical _pump wrapper to match widget-test rendering - RenderRepaintBoundary.toImage(pixelRatio: 2.0) produces 1600x1200 PNGs - Infinite-animation demos (LoadingDemo flickr, AnimationsDemo, KitchenSinkDemo pulse, ScaffoldStreamingRichTextDemo cursor) use pump(kAnimationSettleTime) instead of pumpAndSettle per 11-RESEARCH.md Pitfall 1 - All numeric literals named as kCapture* constants at top of file - No matchesGoldenFile — D-02 hard rule
- Rework _captureWidget to wrap toImage/toByteData/File.writeAsBytes in tester.runAsync so the real-async rasterization resolves even when demo widgets contain infinitely-repeating animations (ActionButton rotate, Loading flickr, ScaffoldAnimatedDisplayPulse, streaming cursor blink). Without runAsync the test binding's fake-async zone waited forever and the run timed out at the 10-minute limit (Rule 3 - blocking issue). - Drop the useAnimationSettle flag — every capture now uses a fixed pump(kAnimationSettleTime) so infinite animations simply show whatever frame is current at the 1s mark. - Drain pending rendering exceptions via tester.takeException() after each capture: ScaffoldChart's X-axis legend Row overflows by 16px at the 800px capture width (a pre-existing layout quirk in lib/components/scaffold_chart.dart:302, not a harness bug). Pixels are still written; the harness is a WRITER, so the warning is discarded. - 26 PNGs at 1600x1200 (800x600 x pixelRatio 2.0) under package-root images/, one per demo in main.dart registry order. - flutter test test/capture_images_test.dart reports 'All tests passed!' - dart analyze --fatal-infos (whole example package) clean - images/ is NOT gitignored (git check-ignore exits 1)
- 26 PNGs at 1600x1200 in images/, one per demo, named per 11-RESEARCH.md - Harness is WRITER per D-02 (no matchesGoldenFile) - Two Rule 3 deviations documented: runAsync fix for infinite animations, takeException drain for pre-existing chart legend overflow
- Replace '214 tests' with '454 tests' (lines 86, 300) - Replace 'one demo screen per widget family' with '26 demo screens covering every widget family and the v1.2 atoms' - Add images/ row to repository layout tree
- Embed one images/<name>.png per demo in the same order as the _DemoTile registry in example/lib/main.dart - Filenames match Plan 11-02 captures exactly; package-root-relative paths render on GitHub
- Preamble states 7 ready / 8 thin / 4 primitive-enabled split - Table copied verbatim from 11-RESEARCH.md §WIDG-45 19-Component Coverage Proof (bold stripped from tier cells to keep grep checks unambiguous) - Inserted between Component gallery and Develop
…llery, WIDG-45 coverage
Root cause of broken images: the capture harness used ThemeData(extensions: scaffoldThemeExtensions) — a default LIGHT ThemeData with the DARK ScaffoldPalette.defaultPalette bolted on. Dark-palette text (white, grey) on a light Material scaffold background was unreadable. Additionally, flutter_test forces the Ahem test font (solid rectangle glyphs) for all text rendering, making the images useless for human viewing. Fixes: - Build proper brightness-matched ThemeData (mirrors main.dart:72-85): dark uses ScaffoldPalette.defaultPalette, light uses ScaffoldPalette.lightPalette, both with ColorScheme.fromSeed - Load Roboto fonts (Regular/Medium/Bold) from Flutter SDK's material_fonts directory via FontLoader, registered under both 'Roboto' and 'monospace' family names so all widget text renders real glyphs - Capture both dark and light themes per demo (52 PNGs total) - Update README gallery to reference _dark.png and _light.png stacked (dark first, matching demo app default)
…fixes Widget fixes (root cause — atoms consume bare consumer Text but never set DefaultTextStyle, so text resolved M3 ColorScheme.onSurface grey instead of the scaffold palette): - ScaffoldSelectionActions: wrap SelectionArea child in DefaultTextStyle with palette.textPrimary - ScaffoldTraceList: wrap item.body in DefaultTextStyle before passing to ScaffoldDisclosure - ScaffoldDisclosure: wrap body in DefaultTextStyle with palette.textSecondary inside AnimatedSize reveal Harness fixes (example/test/capture_images_test.dart): - Load MaterialIcons font (icons rendered as Ahem squares) - Add PrepareCapture hook to drive interactive state before rasterization - _expandAllDisclosures: tap only COLLAPSED disclosures (check AnimatedSize.child is Padding vs SizedBox.shrink) — tapping an already-expanded disclosure collapses it - _captureWalletSheet: build sheet content directly (connected + disconnected states) — demo is button-triggered so static captures only showed the button page - _tapChartCenter / _dragChartRange for chart interaction captures Regenerate all 52 PNGs with readable text in both themes.
Both _captureWidget and _captureWalletSheet dropped the inner Future<File> returned by writeAsBytes. Awaiting create().then(...) resolves as soon as create() finishes, leaving the write un-awaited and silently swallowing errors. Await both futures sequentially and flush the bytes.
Replaces the magic i<6 iteration bound with an unbounded walk that stops at the filesystem root. The constant 6 was tied to a specific Flutter SDK layout and would silently regress to Ahem placeholder squares if the layout ever shifted. Now throws StateError on failure so the writer harness fails loudly instead of producing Ahem-renderer PNGs.
Previously the AnimatedSize child swapped between Padding (expanded) and SizedBox.shrink (collapsed). Because those are different widget types with no shared key, the element tree replaces the child and swaps the render object mid-animation, causing a snap rather than a smooth collapse. Always render the Padding wrapper (with the top padding gated on the expanded state) and only swap the inner child so the RenderPadding render object survives the transition.
…sh (WR-01) Previously the probe call to widget.toolbarBuilder was discarded, and the overlay builder invoked toolbarBuilder a second time. Side-effectful consumer builders (analytics, controller allocation, stateful widgets relying on initState/dispose symmetry) ran twice per toolbar appearance. Cache the probe widget as _toolbarChild and reuse it in the overlay builder, so toolbarBuilder is invoked exactly once per refresh. Also update the toolbarBuilder dartdoc to document that the builder must be pure and that the same widget is reused for the overlay.
Previously _escapeFocusNode.requestFocus() ran unconditionally on every toolbar insert, yanking focus out of consumer-controlled fields when the toolbar was triggered by a keyboard selection or debugSimulateSelection while focus was elsewhere. Gate the request: only grab focus when no pointer is mid-selection AND we don't already own focus. Pointer-driven selections keep SelectionArea's existing focus.
…matedSize WR-03 changed ScaffoldDisclosure's AnimatedSize child to always be Padding (with gated top-padding) for smooth collapse animation. The harness's expansion check (child is Padding = expanded) now always returns true, so collapsed disclosures were never tapped. Fix: check the Padding's INNER child — SizedBox.shrink = collapsed, anything else = expanded. Regenerate disclosure/trace_list images.
…mponents Bare Text widgets in action_button/animations/bottom_drawer/responsive_grid/ toast/tracer demos, DesktopBodyContainer, Loading, and StringButton now use ScaffoldPalette.textPrimary/textSecondary instead of theme-default onSurface grey. The example app's _buildTheme aligns ColorScheme onX slots with the palette so M3 composites resolve the same colors.
…res painted light primary Interleaved tester.runAsync (toImage/encode/write) freezes in-flight AnimatedTheme tickers at their start value, so each light->dark swap on the reused element tree left the widget-level theme stuck on the light scheme while already-painted dark layers persisted — every _dark capture with a plain ElevatedButton rendered the light-scheme primary (#1C6B50) over dark surfaces instead of the app's dark primary (#8BD5B5). themeAnimationDuration: Duration.zero swaps themes instantly on the pump frame, immune to the frozen ticker. Regenerated all 52 demo images; pixel-verified dark primaries.
Super-Genius
marked this pull request as ready for review
August 24, 2026 19:04
Contributor
Author
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9772dd745b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- StringButton: only default label to palette.textPrimary when no fill color is supplied; with a consumer fill, inherit MaterialButton's contrast-derived foreground (prevents white-on-white for light fills). Regenerated string_button_light.png capture. - capture_images_test: narrowed the post-capture exception drain — only known RenderFlex overflow warnings (ScaffoldChart X-axis legend at 800px) are tolerated; any other rendering exception now fails the test instead of being silently discarded. Gates: dart analyze --fatal-infos clean, 454/454 tests pass.
Super-Genius
added a commit
that referenced
this pull request
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 11: Verification & Coverage Gate
Goal: Every v1.2 atom meets the v1.1 shipping bar (tests, demo, barrel export) and the 19-component Beautiful UI set is demonstrably composable from shipped scaffold atoms.
Status: Verified ✓ (3/3 must-haves passed, 2026-08-23)
Phase 11 closed out the v1.2 milestone: the test suite went zero-skip, a reproducible dual-theme image-capture harness now generates the 26-demo README gallery from
example/, and the README was restructured with corrected counts, a 26-entry component gallery, and the 19-row WIDG-45 coverage proof. Post-verification fixes include a capture-harness color-fidelity fix (dark captures were painting the light-scheme primary on dark surfaces) and palette text-color alignment in remaining demo/component text.Changes
Plan 11-01: Skip-Test Deletion + Zero-Skip Sweep Gate
Deleted the permanently-skipped long-press SelectionArea smoke test; sweep gate evidence (analyzer clean, zero skips, barrel exports) recorded.
Key files:
test/components/scaffold_selection_actions_test.dartPlan 11-02: Demo Image Capture Harness
Reproducible WRITER harness (
capture_images_test.dart) rasterizes all 26 demos under dark+light themes viaRenderRepaintBoundary.toImage+tester.runAsync, with real Roboto fonts, intoimages/(52 PNGs).Key files:
example/test/capture_images_test.dart,images/*_dark.png,images/*_light.pngPlan 11-03: README Restructure
Corrected counts (454 tests, 26 demos), 26-entry Component gallery, 19-row WIDG-45 Coverage table,
images/added to the layout tree.Key files:
README.mdPost-verification fixes
fix(capture-images):themeAnimationDuration: Duration.zero— interleavedtester.runAsyncfroze in-flightAnimatedThemetickers, leaving every_darkcapture with a plainElevatedButtonpainting the light-scheme primary (#1C6B50) over dark surfaces instead of the app's dark primary (#8BD5B5). All 52 images regenerated and pixel-verified.fix(theme): demo/example text andDesktopBodyContainer/Loading/StringButtonnow resolveScaffoldPalettetext colors; example_buildThemealigns ColorScheme onX slots with the palette.Requirements Addressed
example/, and barrel export (same bar as v1.1)Verification
dart analyze --fatal-infoscleanflutter test— 454 tests, all passingKey Decisions
themeAnimationDuration: Duration.zeroin the harness — determinism over animation fidelity for static capturesRoboto Medium); known minor defect — w500 labels render Regular weight in capturesReview
6791278); WR-01..WR-05 findings fixed in4b7a277,f35be8f,4fd539f,d55dc2e,118d78d,6e207bd