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/).
- Failure must look like failure: this is still the dominant theme. Sept 29 raised the Closr+ partial Procore sync stamped synced, Sept 30 raised the Closr+ expiry banner, and today's Closr+ unlink race adds a delete that fails silently. That's 10+ honesty bugs raised since Sept 27, and 0 built.
- Closr+ Connected Sources keeps coming up: this is the third brief in a week to point at
connected_sources_page.dartandproject_link_service.dart(Sept 27 "Last synced" next to an error, Sept 29_markSyncedon failure, Oct 1 unlink during import). This should be one hardening pass with service tests, not three patches. - Field navigation should respect where the user was: today's Inspectr+ tapped photo and DailyReportr+ return-to-Today join Sept 28's Inspectr+ "Photo N of M" label. In each case the UI loses the user's place or gives them no quick way back to it.
- Stay honest about scope against competitors: today's brief explicitly warns against turning DailyReportr+ crew quick-select into a timesheet or headcount system in response to Procore's On the Clock beta. That's a good guardrail, and it applies to this brief's recommendations too.
- Automation stall: the builders have missed 23 nights. Morning sources treat the missing reviewer as expected but say nothing about the builders.
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
- Inspectr+ gallery header contradicts the tap: both photo entry points open on photo 1 and the header says "Photo 1 of N" no matter which thumbnail was tapped. The thumbnail strip highlight (
photo_gallery_viewer.dart:190) also starts on the first photo, so all three position cues agree on the wrong answer (Recommendation #1). - Closr+ link card: two actions, two failure styles: Sync Now disables itself, shows "Syncing…", and reports errors inline. Remove link never disables and reports nothing on failure. Actions sitting side by side on the same card should share the same busy and error handling (Recommendation #2).
- DailyReportr+ "Today" pill looks like a button but isn't: it's a filled
primaryBluerounded chip, the same shape and color the app uses for actions, but it's static and only appears when you don't need it (Recommendation #3). - Carried over, unchanged (no commits): the Closr+ expiry banner has the boundary gap and no tap action. Closr+ Connected Sources shows "Last synced <now>" next to an insert error. Buildr+ project-card mini-chips are identical on every card. DailyReportr+ has a fake "1 activity" badge in two duplicated cards and an eager Reports list. Buildr+ swipe backgrounds use raw
Colors.redon 10 pages.Icons.arrow_back_iosappears in 8 Closr+ files. RawColors.green: DailyReportr+ 40, Inspectr+ 29, Buildr+ 0, Closr+ 0.
Competitive Intel
- Procore Daily Log: Quantities now accept negative entries for returns, corrections, and reductions, and running totals and Quantities Log reports reflect them. Company-level equipment can now be added from daily log entries, incidents, inspections, T&M tickets, and timesheets. This is relevant to DailyReportr+ quantity and equipment capture: corrections are first-class entries rather than edits. September 2026 release notes, exact week unverified, new to this brief [source] [releasebot]
- Procore "On the Clock" project timesheets were announced Sept 29 as an opt-in web beta with a live on-site roster, available Oct 13. It's announced, not shipped. It's relevant as a scope guardrail: DailyReportr+ crew quick-select should stay report input, not be marketed as live attendance (per today's morning brief). Sept 29 announcement, via morning brief [source]
- Bluebeam mobile: offline check-ins stay "Pending" until reconnection, and checked-out files are locked read-only for others. This is documented current behavior, not a new release. It's the benchmark for Closr+ Recommendation #2: block destructive actions while a transfer is in flight. current capability, not news [source]
- Last ~7 days, otherwise: the Procore iOS Inspections sync header came back again and was already reported Sept 24, 26, and 29. Fieldwire results were the undated Sheet Compare and private markups post (already reported Sept 30) and "recurring tasks" for routine inspections (date unverified). Autodesk Construction Cloud / PlanGrid returned Autodesk Assistant and Handover, already reported Sept 2 onward and treated as current capability. No dated, in-window ACC or PlanGrid ship was found. [Fieldwire] [ACC]
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).