Junior goldplate — overnight report, 24 Sept 2026
Junior goldplate — overnight report, 24 Sept 2026
Branch feat/junior-goldplate (from 4b38778, the audit commit), pushed after
every item. Plan: docs/audit/junior-sweep-2026-09-23.md §6, items A1–A6,
B1–B7, C1–C5 (C6 and C7 skipped as instructed). Secondaire was read from a
git archive main export only; its working tree and branches were not
touched.
Result: all 18 items done, none reverted or skipped. Gates at the end:
flutter analyze says No issues found, flutter test 164/164, integration
suite green on the iPhone 17 Pro simulator and the Pixel 9 API 36 emulator
(details in §1). TestFlight: see §7.
1. Items
| # | Item | Status | Commit(s) | Evidence |
|---|---|---|---|---|
| A1 | Write-mode focus theft: port Secondaire’s WrittenIndicativeStudyWidget; VerbTestModel.add() stops notifying |
done | d6df1d9 |
test/regression/write_to_test_focus_test.dart (fails before, passes after — §2); test/widgets/written_indicative_study_widget_test.dart |
| A2 | Focus-safe test fields (FocusEnabledTextField), keyboard next/done |
done | 7dd2ebd |
test/routes/verb_indicative_test_route_focus_test.dart (types all 6 persons char by char) |
| A3 | Child-safe keyboard flags on every answer field | done | 4d869fa |
test/widgets/answer_text_field_test.dart |
| A4 | Answer normalisation (trim, spaces, ’/ʼ → ‘, NFC) + same-length-wrong bug | done | e36d018 |
test/util/answer_normaliser_test.dart, test/util/answer_comparison_test.dart |
| A5 | Fields visible before tapping; 48 dp targets | done | d948c0c |
test/theme/answer_field_visibility_test.dart (≥3:1, 6 palette/brightness combos); screens/before_after/ |
| A6 | Smoke async* loops run; controllers disposed; write→test→evaluate integration test via testTextInput |
done | 3c85749, 4cc5723 |
integration_test/write_flow_test.dart green on iOS sim + Android emu; scripts/run_integration_tests.sh |
| Phase A gate | green | a58386e |
write flow 1/1, smoke 5/5 on both platforms; screens/A/ (3 devices × light/dark/XXL) |
|
| B1 | One MaterialApp, one navigator, clean stack, back buttons |
done | dc4c762 (+ 30e203c) |
test/app/back_navigation_test.dart (6), also on devices via integration_test/back_navigation_test.dart |
| B2 | M3 theme, colours in one token file, 3 palettes (P1 default) | done | a5d23f3, ef9251d, 1ced30b, 9eaa8df |
test/theme/palette_test.dart (no colour literal outside palette.dart; WCAG AA per palette × light/dark); palettes/ |
| B3 | SafeArea inside Scaffold (black bands) |
done | c2bc9fc |
test/app/edge_to_edge_test.dart (fails on pre-B3 code, passes) |
| B4 | Flexible layouts, full “ils/elles”, iPad 600 pt cap, iPhone portrait | done | f42f005 |
test/layout/layout_test.dart (SE, 17 Pro, iPad Air 11”) |
| B5 | TextScaler, cap 2.0× |
done | 32e9538, 7245bc3 |
test/layout/text_scale_test.dart (3 devices × 1.3/2.0/3.1×) |
| B6 | Secondaire-style header, cover-all, result rows, animated check | done | c061580 |
test/widgets/study_screens_test.dart |
| B7 | Dutch only, all strings in app_nl.arb, delegates everywhere |
done | ffd7167 |
test/l10n/dutch_only_test.dart (runs nl-BE on an en-US device; guard against literal UI strings) |
| Phase B gate | green | 2554d9b |
at 7245bc3: write flow 1/1, smoke 5/5, back navigation 6/6 on both platforms; screens/B/ |
|
| C1 | App icon single-size 1024, 15 orphans gone, LaunchImage |
done | 7bca9c0 |
actool compiles with no warning; test/platform/ios_assets_test.dart |
| C2 | Dart 3 SDK, analyzer clean, dependency + dead-code hygiene | done | af5cbc0 |
flutter analyze: No issues found (audit: 65) |
| C3 | .gitignore/tracked files, .gitattributes + CRLF, Android build |
done | 0bd68c0 |
flutter build appbundle --release OK; merged release manifest has no INTERNET; clean git status after builds |
| C4 | savoir = weten, sorted list, search crash, duplicate results |
done | 14dd242 |
test/routes/verb_list_search_test.dart |
| C5 | Settings/app coverage; capture harness committed | done | 9b25cfe |
coverage 96.3% (audit 82.2%); lib/settings 92.6% (was 28.8%), app.dart 100% (was 43%); test/privacy/no_personal_data_test.dart |
| Phase C gate | green | 9eaa8df (tested), 1b3b7e9 (4.1.0+21) |
analyze clean; 164/164; write flow 1/1, smoke 5/5, back navigation 6/6 on iOS sim + Android emu; screens/C/ |
|
| C6, C7 | More tenses; history/streak | skipped (instructed) | — | Decisions #7, #8 |
Fixes found by the gates themselves (each its own commit):
30e203c— B1 Android gate: write mode’s keyboard next used Flutter’s default geometricnextFocus()mid-animation.ad029ca— phase C Android gate: write mode could skip a person (≈1 run in 4 on the emulator). Instrumented: one next, but the page went 1 → 2 because the viewport shrank mid-animation (keyboard inset) and the animation kept its old pixel target. The carousel now snaps to the page it was heading for; focus moves only at rest. 5/5 after, 1/5 with a first (wrong) hypothesis — both runs logged in the commit message.9eaa8df— phase C screenshots: result rows were saturated green/red with a blue pronoun. Pastel feedback containers + a palette test for them.cef6546/4e56ddf— the capture harness rendered “dark” runs light; phase A dark sheets were re-captured at the phase A commit.- Android integration tests had never been able to run: the debug
AndroidManifest.xmlwas a copy of main without INTERNET, so the Flutter tool could not reach the Dart VM service. Standard debug-only manifest restored in A6; main/release still have no INTERNET (verified in C3).
2. The focus test, before and after (A1)
Full output: logs/focus_test_before.txt, logs/focus_test_after.txt.
BEFORE (4b38778 + test) AFTER (d6df1d9)
REPRO iOS focus after 1st char: false ✗ focus after 1st char: true ✓
text after 2nd char: "a" text after 2nd char: "ai"
hidden write field has focus: true ... has focus: false
REPRO Android focus after 1st char: false ✗ focus after 1st char: true ✓
CONTROL (cover) "ai" ✓ "ai" ✓
WRITE MODE "ai" ✓ "ai" ✓
+2 -2: Some tests failed. +4: All tests passed!
3. Screens, before and after
One image per device, audit (top) against phase C (bottom), light, normal text:
Full sweeps per phase gate (each: iPhone 17 Pro, iPhone SE, iPad Air 11” × light, dark, largest accessibility text): screens/A/, screens/B/, screens/C/. Audit “before” sheets, including dark + XXL: docs/audit/img/sheet_junior_*.png.
4. Palettes
palettes/palette_p1.jpg (default) · palette_p2.jpg · palette_p3.jpg — every screen, light (top row) and dark (bottom row), iPhone 17 Pro.
- P1 · Mascotte (shipped): the mascot’s steel blue, the indigo the app already used, the mascot’s orange for the cover blocks.
- P2 · Zonnig: sea green, sunflower yellow, coral.
- P3 · Kermis: grape purple, sky cyan, candy pink.
All three pass the same AA checks (test/theme/palette_test.dart); the answer-feedback colours (green/red/orange) are shared.
To switch: in lib/theme/palette.dart, change the one line const JuniorPalette activePalette = JuniorPalette.p1Mascot; to p2Zonnig or p3Kermis. (To preview without editing: --dart-define=JUNIOR_PALETTE=p2.)
5. Choices I made
Write path
- The write widget was ported as-is (vertical carousel); cards got a 48 pt input row, so the carousel shows two cards per screen instead of 2.4.
- Secondaire’s
FocusEnabledTextFieldre-requests focus inonChanged/onTapOutside; I left that out — tapping the next field firesonTapOutsideon the current one and the two would fight. - Answers are compared case-sensitively (autocapitalisation is off, so a capital is a real typo). One line to relax in
lib/util/answer_normaliser.dart. - NFC is a small built-in composer for the accents French uses, not a package.
- Also set
enableIMEPersonalizedLearning: false(Android keyboards must not learn from a child’s typing). - Deleted
TextCheckerandExpectedRow(dead after the port) and two trivial tests (1+1, an empty file).
Navigation (B1)
- Stack: home → (start replaces) verb list → mode choice → study → test → (replaces) result. Android back on the verb list leaves the app. Back from the test goes to the study screen (to look again); “repeat” on the result goes to the mode choice; “home” to the verb list.
- State restoration dropped (the
restorable*calls restored nothing). - A changed singular/plural choice returns to the verb list (or home if settings was opened from home).
Design (B2, B4, B6)
- Colour schemes are generated from each palette’s seeds (
ColorScheme.fromSeed) instead of Secondaire’s hand-written tables: a palette is six colours and every pair keeps Material’s tone contrast. Accent and feedback containers use pastel variants (the “fidelity” variant made them the raw seed colour — unreadable; found twice, both fixed and tested). - Kept Junior’s larger type scale (25/22/20/16 sp) rather than Material’s defaults.
- Dark mode field borders use
onSurfaceVariant(the outline was 2.9:1 on the tinted write card). - App bars name the step (Afdekken, Kaartjes, Schrijven, Test, Resultaat); the verb sits in the header card. The “indicatif · présent” chip stays French (content, not UI copy).
- Cover blocks are the palette’s accent (orange in P1) with a tap icon; revealed forms in the “correct” green.
- The result screen shows the right form inline and keeps the “?” dialog.
- iPhone is portrait-only; iPad keeps every orientation. Android is not locked (the decision named iPhone only).
Text size (B5)
- One cap, 2.0×, at the root. I did not go higher: the app bars and the write carousel are fixed-height beyond that. Tested at 3.1× (clamped).
Languages (B7)
- The app always runs in nl-BE, whatever the device language;
Info.plistdeclaresCFBundleLocalizations = nlso system UI inside the app is Dutch too (not a usage string, no permission).
Polish (C)
- The icon is the existing hand-made
icon1024.png(no new artwork — still yours to approve). The launch screen now shows the mascot on white. - C3 untracks generated plugin registrants and IDE files; the gradle wrapper scripts stay tracked. The release AAB is 124 MB because the existing
debugSymbolLevel 'FULL'puts native symbols in bundle metadata (not shipped to devices). - After choosing a verb in search there was no way back to the whole list: added “Toon alle werkwoorden”.
- The verb list keeps its scroll position now (it used to be rebuilt).
Signing (end of night)
- The provisioning profile “Verbomatique Junior App Store 2026” contains one certificate (SHA-1
B283EB36…, Apple Distribution: Dries Bellen (Z4HB79CS69), expires 30 June 2027). I signed with that keychain identity. That it is the certificate Apple calls KJ972AVRN3, and that the profile is Apple’s 4BQW8ARL38, is an inference: neither ID is stored in the files on this Mac.
6. New packages
None. Removed: test (duplicated flutter_test), shared_preferences_web, flutter_web_plugins. flutter_launcher_icons moved to dev_dependencies. No network, analytics, crash reporting, ads, IAP or notifications were added; no permissions or usage strings (main AndroidManifest.xml: 0 uses-permission; Info.plist: 0 UsageDescription).
7. TestFlight
Uploaded; processed; not submitted. Build 4.1.0 (21), commit 1b3b7e9.
- Archive:
flutter build ipa --release --no-codesign(unsigned), thenxcodebuild -exportArchivewith manual signing: profile Verbomatique Junior App Store 2026, identity Apple Distribution: Dries Bellen (Z4HB79CS69) (SHA-1B283EB36…, see §5), team Z4HB79CS69, no-allowProvisioningUpdates. - IPA checks (all pass):
CFBundleDisplayName= Verbomatique Junior ·CFBundleShortVersionString= 4.1.0 ·CFBundleVersion= 21 ·ITSAppUsesNonExemptEncryption= false ·MinimumOSVersion= 15.0 in the app and inApp.framework· bundle idcom.teamwabisabi.verbomatique·codesign --verify --deep --strict: valid, satisfies its Designated Requirement · TeamIdentifier Z4HB79CS69 ·get-task-allowfalse · 0 usage strings. - Upload:
fastlane upload_to_testflightwith the Secondaire ASC API key (by path, never printed),apple_id1587030454,skip_submission: true, at 01:57. - 15 minutes later (02:12):
processing_state = VALID, not expired. It is in TestFlight for internal testing. No review submission, no external testers, no metadata edits. - Nothing was done in Play Console.
8. Real-iPhone check for Dries (≈10 minutes)
Install the TestFlight build, set iPhone language to Dutch or English (the app must be Dutch either way).
- QuickType off? Open schrijven for any verb. The suggestion bar above the keyboard must be gone; type
suisand tap space — no word is inserted, no autocorrect. The field must show exactly what you typed. - Accents and j’ai. In the être test, type
êteswith a long-press one— it must count as right. For avoir, the pronoun shows asj'and you type onlyai; check it reads “j’ ai” correctly in the result. Also type a curly apostrophe (’, long-press on') somewhere — it must be treated like'. A trailing space (suis) must still count as right. - Focus never jumps. Write mode → forward → test: tap the first field once and type all six persons using only the keyboard’s volgende key. Every letter must land where you look. Then the same starting from afdekken and kaartjes.
- Hardware keyboard (iPad or a Bluetooth keyboard): Option-i then e gives ê; Tab/Return move on; nothing is capitalised.
- Dictation: dictate “sommes” into a test field; it should land in that field and be judged normally.
- Large text: Settings → Accessibility → Larger Text, max. Walk verb list → schrijven → test → resultaat. Nothing cut off; “ils/elles” in full; pronouns move above the field on narrow rows.
- Dark mode: in the app’s settings pick Donkere kleuren; every screen dark, fields visibly outlined, results readable.
- Back navigation: from the result screen, swipe back (→ study screen), home icon (→ verb list), repeat icon (→ mode choice). The verb list has no back button; it is the start.
- Singular/plural: turn Meervoud off in settings mid-verb → you land on the verb list; the next verb shows only je/tu/il/elle.
- iPad: content centred, never wider than a phone; rotate — still fine; Scribble into an answer field.
Anything odd: the fastest repro is integration_test/write_flow_test.dart and the screenshots in screens/C/.
Build 22
24 Sept 2026, same branch, same method (one commit per item, pushed, each
with a test) and the same hard limits. Items from Dries’s test of build 21.
Screenshots: build22/items.jpg (one per item),
build22/lt_ios.jpg and
build22/lt_android.jpg (the large-text walk).
Result: all 7 items done, none reverted or skipped. Build 4.1.0 (22) uploaded to TestFlight, not submitted.
| # | Item | Commit | Test | Screenshot (build22/items.jpg) |
|---|---|---|---|---|
| 1 | P2 “Zonnig” is the default palette (icon unchanged, approved) | fd358d0 |
test/theme/palette_test.dart ‘the shipped default is P2 “Zonnig”’ (all three palettes still AA) |
every frame; item1_4_cover_zonnig_no_chip |
| 2 | Per-keystroke traffic light on each write-mode field: amber = right so far, red = deviates, green = complete (animated check kept); empty = no colour; test screen gives none | 3e80881, 20104d4, f781552 |
test/util/answer_comparison_test.dart ‘traffic light’ (ê, decomposed ê, curly apostrophe, dead-key accent); test/widgets/write_feedback_test.dart (border colour + 3 pt width + icon + spoken label per state; none on the test screen); palette_test: feedback colours ≥ 4.5:1 on the field |
item2_a_empty, item2_b_red, item2_c_amber, item2_d_green |
| 3 | Search ignores accents and case; ‘e’, ‘et’, ‘etre’ find être; prefix matches first | c4ff1a7 |
test/util/text_fold_test.dart; verb_list_search_test types ‘etre’ and ‘e’ |
item3_search_e (entendre, entrer, être first), item3_search_etre |
| 4 | No “indicatif · présent” chip or any tense/mode label | 7683774 |
test/layout/no_tense_labels_test.dart walks every screen; verb_selection_route_test (one start button even for a verb with an imparfait) |
item1_4_cover_zonnig_no_chip |
| 5 | Enkelvoud/Meervoud mid-verb: study updates in place (answers kept), test restarts after confirmation, result applies next round; never both off; logic in VerbTestModel |
9313d1d |
test/model/cardinality_in_progress_test.dart (every outcome); test/app/cardinality_change_test.dart (write mode keeps answers, test cancel/confirm, result deferred + snackbar, switches) |
item5_a_settings_last_switch_locked, item5_b_cover_updated_in_place, item5_c_test_restart_dialog, item5_d_test_restarted_all_persons |
| 6 | Android: portrait on phones, all orientations on tablets (sw ≥ 600 dp) | 8954b6e |
test/platform/android_orientation_test.dart; on emulators: Pixel 9 requestedOrientation=SCREEN_ORIENTATION_PORTRAIT, stays ROTATION_0 with landscape forced; Pixel Tablet SCREEN_ORIENTATION_UNSPECIFIED |
item6_android_phone_landscape_forced |
| 7 | Automated large-text walk (home → list → search → mode → write → test → result → settings) in scripts/run_integration_tests.sh |
fbd4580 |
integration_test/large_text_test.dart |
build22/lt_ios.jpg, build22/lt_android.jpg |
| — | Gate, screenshots, release | 0f01bed, 14ecd4e |
see below |
Large-text test result (from the final gate, scripts/run_integration_tests.sh):
| Platform | Size set | Result | Screenshots | Restored |
|---|---|---|---|---|
| iOS, iPhone 17 Pro simulator | accessibility-extra-extra-extra-large |
11 steps, no FlutterError, no clipped “ils/elles” — All tests passed | 11 | content_size = large |
| Android, Pixel 9 API 36 emulator | font_scale 2.0 (the maximum) |
11 steps, same checks — All tests passed | 11 | font_scale = 1.0 |
The test proves it can fail: at 1.0× it refuses to run (“the device must be at its largest text size (got 1.0x)”), and with the pronoun column deliberately halved it failed at the test screen: “ils/elles clipped (71.7 < 139.5)”.
Gate before the build (code at f781552): flutter analyze — No issues
found; flutter test — 195/195; integration on both platforms — write flow
1/1, smoke 5/5, back navigation 6/6, large-text walk green.
Choices I made (build 22)
- Item 2 semantics come from Secondaire’s write widget (
_validateInput, ported ascompareAnswerin A1), not the pre-port JuniorTextChecker, which kept the previous colour for a same-length wrong answer. Added: an accent a hardware keyboard is still composing (ˆ before the e) is not red yet. - Item 2: amber is a dots icon, red a cross, green a check — in the field and in the big status icon (which used the check for amber too; fixed in
f781552). - Item 5: the test’s “cancel” keeps you in settings with the change still showing, so you can undo it; confirming restarts the same verb. On the result screen a snackbar says “Je keuze geldt vanaf de volgende ronde.” Write mode also keeps what was typed for a person that is switched off and on again (the spec asked only for persons that remain). The old silent “switch the other one back on” is replaced by a disabled last switch.
- Item 5: the mode-choice screen counts as “study” (updates in place); the verb list and home just remember the choice for the next verb.
- Item 6: done with a resource qualifier (
values-sw600dp) read inMainActivity.onCreate, becauseandroid:screenOrientationcannot depend on screen size. - Item 7: Android’s maximum font scale is 2.0 on current versions, so “2.0, then the maximum” is the same value; the script prints the value it set.
New packages: none. Permissions / usage strings: none added. Play Console: untouched.
TestFlight (build 22)
Uploaded; processed; not submitted. Build 4.1.0 (22), commit 14ecd4e.
- Archive and export exactly as build 21:
flutter build ipa --release --no-codesign, thenxcodebuild -exportArchivewith manual signing (profile Verbomatique Junior App Store 2026, Apple Distribution: Dries Bellen (Z4HB79CS69)), no-allowProvisioningUpdates. - IPA checks, all pass: display name Verbomatique Junior · 4.1.0 (22) ·
ITSAppUsesNonExemptEncryptionfalse ·MinimumOSVersion15.0 in the app and inApp.framework·codesign --verify --deep --strictvalid and satisfies its Designated Requirement · team Z4HB79CS69 ·get-task-allowfalse · 0 usage strings. - Uploaded with
fastlane upload_to_testflight(ASC API key by path,skip_submission) at 08:17. - 15 minutes later (08:32):
processing_state = VALID, not expired. Available for internal testing. No review submission, no external testers, no metadata edits.
Build 23
Dries tested build 22 on his iPhone and marked it “All OK → merge”, with four fixes for build 23. Same method and limits as build 22: one commit per item with a test, pushed after each.
Step 0 — merge. feat/junior-goldplate at e0e0a5c (build 22) merged into
main with --no-ff: merge commit 3e4c653, pushed (a2c6fec..3e4c653).
main’s tree equals e0e0a5c’s tree: both 625fc14442fea36a3b50df6b96b89ff792565c7c.
Build 23 work continued on feat/junior-goldplate.
| # | Item | Commit | Test | Screenshot (build23/items.jpg) |
|---|---|---|---|---|
| 1 | Write mode: only the in-field feedback icon (dots / cross / check, with its spoken label); the big status icon is gone | c37ce24 |
written_indicative_study_widget_test (icon read from the field, no comparisonStatusIcon), write_feedback_test (no icon larger than 48 pt), verb_indicative_study_write_mode_form_state_test |
item1_a…d (empty, amber, red, green) |
| 2 | Result: no “?” button, no dialog; the right form is on the card (→ sommes). Dead code and the three dialog strings deleted |
b3cab35 |
study_screens_test (“evaluate: no ‘?’ button or dialog”), verb_indicative_evaluate_route_test; smoke_test and write_flow_test read the card instead of the dialog |
item2_result_no_question_mark |
| 3 | P3 “Kermis” is the default — still one line in lib/theme/palette.dart (activePalette) |
70dd2a1 |
palette_test (“the shipped default is P3 ‘Kermis’”) + the AA contrast checks for every palette |
item3_kermis_verb_list (and every other shot) |
| 4 | Automated iPad walk: the 11 large-text steps on iPad Air 11” (M3) and iPad mini (A17 Pro), portrait and landscape | 3c5acd0, fix 753f7e8 |
integration_test/ipad_test.dart, in scripts/run_integration_tests.sh (iOS target) |
build23/ipad_*.jpg |
| — | Version 4.1.0+23 | 4267682 |
iPad result (final gate at 753f7e8; per step: no FlutterError, content
≤ 600 pt and centred, write card not clipped, no clipped “ils/elles”):
| Device | Portrait | Landscape | Screenshots |
|---|---|---|---|
| iPad Air 11-inch (M3) | pass, 11 steps | pass, 11 steps | 22 (ipad_air11_portrait.jpg, ipad_air11_landscape.jpg) |
| iPad mini (A17 Pro) | pass, 11 steps | pass, 11 steps | 22 (ipad_mini_portrait.jpg, ipad_mini_landscape.jpg) |
The test found a real bug before it went green: in landscape, on both
iPads, it failed at step 05_write — the write carousel sized its cards from
the viewport height, and with the keyboard up the card was too short for its
own content (“the expected form (208–238) is outside the card (237–255)”).
My first fix (card size from the screen height) broke typing on Android.
753f7e8 removes the height dependency: one person per carousel page, the
card centred at its natural height with its own scroll view. Visible change:
the neighbouring cards no longer peek in from the sides.
Not automated: Scribble (Apple Pencil handwriting into the field). It needs a Pencil on real hardware; the simulator has no Scribble input and Flutter’s test binding can’t inject it. It stays a manual check when an iPad is at hand.
Gate before the build (code at 753f7e8): flutter analyze — No issues
found; flutter test — 194/194 (one test fewer than build 22: the dialog-key
test went with the dialog). Integration, iOS (iPhone 17 Pro): write flow 1/1,
smoke 5/5, back navigation 6/6, large-text walk pass (11 shots, text size
restored), iPad walk as above. Android (Pixel 9 API 36): write flow 1/1, smoke
5/5, back navigation 6/6, large-text walk pass (font_scale 2.0, restored to 1.0).
Choices I made (build 23)
- Item 1: the in-field icon keeps its spoken label, so VoiceOver/TalkBack still say “Goed bezig / Fout / Juist” — nothing was lost by removing the big icon.
- Item 2: the wrong answer stays struck through above
→ <right form>;showAnswerTooltip,correctAnswerTitle,gotItand the now-unusedstatusNotStartedwere removed fromapp_nl.arb. - Item 4: landscape is set through the Flutter binding (
tester.view.physicalSizeswapped), notsimctl—simctlhas no rotate command. In landscape the screenshots are rendered from the layer tree, because the capture driver grabs the device in its physical (portrait) orientation. - Item 4: the iPad walk also caught the verb-list search results running full width; they are now capped at 600 pt like the list (part of
3c5acd0). - The iPad test refuses to run on a screen narrower than 600 pt (“run on an iPad simulator”), so a phone run can’t pass it by accident. The script only runs it on the two iPads.
New packages: none. Permissions / usage strings: none added. Play Console: untouched.
TestFlight (build 23)
Uploaded; processed; not submitted. Build 4.1.0 (23), commit 4267682.
- Archive and export exactly as build 22:
flutter build ipa --release --no-codesign, thenxcodebuild -exportArchivewith manual signing (profile Verbomatique Junior App Store 2026, team Z4HB79CS69), no-allowProvisioningUpdates. - IPA checks, all pass: display name Verbomatique Junior · 4.1.0 (23) ·
ITSAppUsesNonExemptEncryptionfalse ·MinimumOSVersion15.0 in the app and inApp.framework·codesign --verify --deep --strictOK · team Z4HB79CS69 ·get-task-allowfalse · 0 usage strings. - Uploaded with
fastlane upload_to_testflight(ASC API key by path,skip_submission) at 11:37. - 15 minutes later (11:52):
processing_state = VALID, not expired. Available for internal testing. No review submission, no external testers, no metadata edits.