Lab Architect / Ellsworth

Afternoon Brief

Thursday, October 1, 2026 — Twenty-third consecutive night with no nightly-branch build on any app. There is no feature/nightly-20261001 or feature/nightly2-20261001 branch, and git log --all --since="30 hours ago" is empty on all four app repos. Codebase health: 0 analyzer issues on all four apps. Buildr+/Closr+/Inspectr+ still hold 4/6/7 unpushed commits. All three of today's morning-brief ideas are confirmed in code and unbuilt. Two of them go further than the brief said: in Inspectr+, both gallery entry points drop the tapped photo, and in Closr+, deleteLink has no error handling at all. Good news: today's morning brief finally reports the real local main heads for Buildr+ (a7a5a03) and DailyReportr+ (0433f44), so the stale-head correction carried since Sept 24 is closed.

Last Night’s Builds

No nightly-branch build on any app, twenty-third night running missed

Ran git fetch origin --prune on all four repos. No new nightly branches exist; the most recent on any remote are still feature/nightly-20260908 and feature/nightly2-20260909. The held assignments (Dominique → Buildr+ Deletes That Wait for the Server, Katey → DailyReportr+ reports_page.dart rework) were not picked up for the fourth day. The morning report (morning-reports/2026-10-01.md) shows Hermes UP and the nightly-review artifacts MISSING, which is expected since that reviewer was retired. Nothing in either morning source mentions the 00:15 and 01:15 builder jobs. ~/memory/last-nightly-build.md (the builder coordination file) was last updated Aug 5.

Buildr+ 4 commits unpushed · working copy off main

Local main head a7a5a03 (Sept 21) is 4 ahead / 0 behind origin/main (33c825f). The working copy is still on fix/project-card-contract-value-spacing. Unchanged.

Closr+ 6 commits unpushed · tracked node_modules

Local main head 0925b0b (Sept 21) is 6 ahead / 0 behind origin/main (7a3ca21). git status still lists the deleted-but-tracked node_modules/ files. Unchanged.

Inspectr+ 7 commits unpushed

Local main head 8145093 (Sept 21) is 7 ahead / 0 behind origin/main (4140149, Sept 8). origin/main has been silent for 23 days.

DailyReportr+ silent 17 days · working copy off main

main = origin/main = 0433f44 (Sept 14, 3.8.1 TestFlight prep). The working copy is still parked on the Aug 11 fix/owner-report-media-web branch with modified tracked files.

Codebase Health

App Analyzer Issues Breakdown Last 3 Commits (local main)
Buildr+ 0 "No issues found!" Still open: 10 of 10 swipe-to-delete pages with unguarded onDismissed, ProjectCard with no Semantics and 0 tests, and the filtered "No projects yet" empty state. a7a5a03 merge harden-ios-release-gate • a2873c3 merge project-card spacing fix • fac04bd source protected secrets file (unpushed)
Closr+ 0 "No issues found!" New: ConnectedSourcesPage._deleteLink awaits service.deleteLink with no try/catch, so a failed unlink throws an unhandled async error and gives the user no feedback. The page has 0 tests. Still open: the isExpiringSoon boundary gap, _markSynced on partial Procore failure, the silent portfolio catch (_), and tracked node_modules/. 0925b0b merge harden-ios-release-gate • d4c8952 gate TestFlight IPA on client IDs • ca33394 bump TestFlight 2.32.75 (unpushed)
Inspectr+ 0 "No issues found!" New: _showFullScreenImage takes an initialIndex parameter that is never used, the kind of dead parameter the analyzer doesn't flag. PhotoGalleryViewer has no tests. Raw Colors.green: 29 (unchanged, no commits). 8145093 merge Google sign-in client-ID fix • 5e0849b merge harden-ios-release-gate • 744cc12 add iOS artifact gate (unpushed)
DailyReportr+ 0 "No issues found!" Still open: the admin reports retry loop, the duplicated _buildReportCard with its fake "1 activity" badge, the eager Reports list, 40 raw Colors.green, and no sync-receipt tests. 0433f44 prepare 3.8.1 TestFlight • 2495d04 merge keyboard-recovery fix • 7253e65 stop keyboard recovery stranding text field (pushed)

Analyzer counts: Buildr+ 0 · Closr+ 0 · Inspectr+ 0 · DailyReportr+ 0. All four apps were analyzed with dart analyze lib on git archive main snapshots (after flutter pub get --offline) so WIP and off-main working copies don't skew the counts. This matches the morning brief's independent flutter analyze --no-pub result of 0 on all four apps.

Recurring Themes from Morning Briefs

Sources: the Oct 1, Sept 30, and Sept 29 morning briefs (briefs/).

Feature Ideas + UI/Code Health Recommendations

There are 3 new recommendations: 2 UI/code health items and 1 feature. All three came from today's morning brief and were verified against main snapshots with grep and git log --all. No prior commit mentions an initial photo index, unlinking, or a return-to-today control. The held Sept 28 items (Buildr+ server-confirmed deletes, DailyReportr+ admin failure state) still outrank these.

1. Open the Photo That Was Tapped Inspectr+ — UI/Code Health (navigation correctness)

_showFullScreenImage(context, imageUrl, initialIndex, images) (lib/pages/inspection_item_page.dart:2849) receives the tapped index and then builds PhotoGalleryViewer(inspectionItemId:, initialImages:) without it. The viewer has no index parameter, and its _pageController = PageController() and _currentIndex = 0 are both hard-wired to the first photo (lib/widgets/photo_gallery_viewer.dart:31-33). The morning brief missed a second entry point: the photo grid sheet in lib/widgets/common_widgets.dart:904-915 has index in scope in its itemBuilder and also drops it. An inspector who taps the fourth deficiency photo sees the first one under a header reading "Photo 1 of 5", which is the wrong evidence with a confident label.

Why now: it's today's morning-brief idea #1, and it leaves the code cleaner: it removes a dead parameter, fixes two call sites at once, and adds the first PhotoGalleryViewer tests. It's also the natural follow-on to Sept 28's "Photo N of M" markup label, since both concern the user's position in a photo set.

Sketch: Add final int initialIndex (default 0) to PhotoGalleryViewer. In initState, clamp it to 0..images.length-1 and use it to seed both _currentIndex and PageController(initialPage:), which makes _pageController late final. Pass the index from both call sites, and drop the unused imageUrl argument while you're there. After a delete, call _pageController.jumpToPage(_currentIndex) so the page and the label agree. Widget tests: tapping the third of five photos shows "Photo 3 of 5", an out-of-range index clamps, and deleting photo 2 while on photo 5 leaves "Photo 4 of 4".

2. Don’t Unlink While an Import Is Running, and Say So When Unlink Fails Closr+ — UI/Code Health (state safety)

In lib/pages/projects/connected_sources_page.dart, the Sync Now button is disabled while _syncing[link.id] is true (:395), but the Icons.delete_outline "Remove link" button next to it (:345-352) is always enabled. A user can confirm "Remove Link?" in the middle of a Procore import, and service.sync(link) and service.deleteLink(link.id) then race on the same link. The morning brief also missed this: _deleteLink (:232-261) awaits deleteLink with no try/catch. If the delete fails, the dialog closes, nothing is shown, and the error escapes as an unhandled async exception. _sync next to it does catch and report errors, so the two actions on the same card behave differently on failure.

Why now: it's today's morning-brief idea #2, and this is the third Connected Sources issue in a week. It should be bundled with Sept 29's _markSynced-on-failure fix into one hardening pass that adds the first tests for the page and the service. Today's Bluebeam reference (offline check-ins stay "pending" and checked-out files are locked) is the model: an in-flight transfer locks destructive actions.

Sketch: Pass onDelete: isSyncing ? null : () => _deleteLink(link) and add the tooltip "Wait for sync to finish". After the dialog returns, re-check _syncing[link.id] and abort if a sync started. Wrap deleteLink in try/catch and show showSnackBar(..., isError: true) on failure. Invalidate projectLinksProvider only on success, plus a "Link removed" confirmation. Widget tests: delete is disabled while syncing, delete re-enables when the sync completes, a failed delete shows an error and keeps the card, and cancel does nothing.

3. One-Tap Return to Today DailyReportr+ — Feature

DailyReportPage (lib/pages/daily_reports/daily_report_page.dart) navigates by day with chevrons (:199, :241) and by tapping a day in the month grid (:313). Each path separately updates _selectedDay and _focusedMonth and calls _loadWeatherForDate(). The blue "Today" pill (:383-399) only appears when today is already selected, and it isn't tappable. After a superintendent checks a report from three weeks ago, getting back to today means repeated chevron taps or paging months in the grid.

Why now: it's today's morning-brief idea #3. It's small and stays inside the daily-report workflow (no timesheet scope creep, per the brief's own Procore On the Clock guardrail). Building it also lets the three duplicated "change date" blocks collapse into one helper, which is a net code-health gain.

Sketch: Extract _selectDay(DateTime day), which sets _selectedDay and _focusedMonth = DateTime(day.year, day.month) and calls _loadWeatherForDate() in one setState. Route the chevrons and grid taps through it. When !isToday, render a TextButton.icon(Icons.today, 'Today') where the pill would be, with onPressed: () => _selectDay(DateTime.now()). Keep the pill when already on today. Tests: from a date in a prior month, tapping Today selects today and moves the focused month, the button is hidden on today, and crew selection is preserved.

UI Inconsistencies

Competitive Intel

Tomorrow’s Build Suggestion

This is the twenty-third night without builders, and the assignments are held for the fourth day. Nothing has changed in either app since they were set, so rotating them would only add churn. Owner actions before either build: check the 00:15 and 01:15 builder launch jobs, and push the unpushed main commits (Buildr+ 4, Closr+ 6, Inspectr+ 7). For Closr+, stage explicit paths rather than using git add -A.

Dominique → Buildr+

Task: Deletes That Wait for the Server (Sept 28 Recommendation #1), then ProjectCard semantics (Sept 30 Recommendation #2) if time remains, since both edit projects_list_page.dart. Rationale: Buildr+ hasn't had a nightly feature since Aug 5, and a destructive action that can hide a live project is still the highest-stakes honesty bug on the list.

Katey → DailyReportr+

Task: a single reports_page.dart rework: first Admin Reports That Admit Failure, then the shared ReportSummaryCard without the fake "1 activity" badge, then the lazy SliverList. Rationale: it's the stalest app (17 days on main), and all three fixes target the same file. Queued next for whichever builder frees up first: the Inspectr+ tapped-photo fix (Recommendation #1, about an hour, and Inspectr+ has gone longest without a pushed commit), then a Closr+ Connected Sources hardening pass (Recommendation #2 plus _markSynced).