Lab Architect / Ellsworth
Afternoon Brief
Wednesday, September 30, 2026 — Twenty-second consecutive night with no nightly-branch build on any app. There is no feature/nightly-20260930 or feature/nightly2-20260930 branch, and git log --all --since="36 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. Checking the Closr+ idea turned up a real date bug: a document that expires later today counts as neither "expired" nor "expiring soon", so it drops out of the warning banner on its last valid day (Recommendation #1). Correction to the morning brief (sixth time): it again lists b983e74 and af49e33 (Aug 11) as the latest Buildr+ and DailyReportr+ commits. The real main heads are a7a5a03 and 0433f44.
Last Night’s Builds
No nightly-branch build on any app, twenty-second night running missed
Ran git fetch origin 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+ Admin Reports That Admit Failure) were not picked up. Today's morning brief adds a new detail: the Ellsworth nightly-review producer "was retired at Leonard’s request", so the missing review report is expected. That covers only the 04:20 reviewer. It doesn't explain why the 00:15 and 01:15 builders are silent, and those launch jobs are still the thing to inspect. ~/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 shows 1,648 entries, almost all deleted-but-tracked node_modules/. 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 22 days.
DailyReportr+ silent 16 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!" New: ProjectCard is a bare GestureDetector with no Semantics wrapper, and there are 0 tests referencing ProjectCard. Still open: 10 of 10 swipe-to-delete pages with unguarded onDismissed, 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: CloseoutDocument.isExpiringSoon requires days > 0, while isExpired uses isAfter(now), which leaves a gap of up to 24 hours. There are 0 tests covering either predicate. Still open: _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!" Raw Colors.green: 29 (unchanged, no commits). Still open: the revision field on drawing references and the "Photo N of M" markup label. |
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!" New: ReportsPage builds every report card eagerly (SingleChildScrollView → Column → ResponsiveDesktopGrid, which is a plain Column on narrow screens). Twenty other page sites in the app already use ListView.builder or SliverList. Still open: the admin retry loop, the duplicated _buildReportCard, 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. Each run took 3–4 seconds.
Recurring Themes from Morning Briefs
Sources: the Sept 30, 29, and 28 morning briefs (briefs/).
- Failure must look like failure: this theme runs through all three briefs. Sept 28 raised Buildr+ delete-before-confirm and the DailyReportr+ admin "zero reports". Sept 29 raised the Closr+ partial Procore sync stamped synced and the DailyReportr+ fake "1 activity". That's now 9 honesty bugs raised since Sept 27, and 0 built. Today's Closr+ expiry gap falls in the same family: the warning goes quiet at the exact moment it matters most.
- Field accessibility and ergonomics: this theme is new today (Buildr+ VoiceOver project card, backed by WCAG 2.5.8 target size). It pairs with Sept 28's Inspectr+ "Photo 2 of 5" position label: both are about making field UI legible without sight or memory.
- Performance at real-world data sizes: this theme is new today (DailyReportr+ eager report list). It touches the same
reports_page.dartas the held Katey task and Sept 29's report-card extraction, so all three should land as one rework of that page. - Warnings should link to the things they warn about: today's Closr+ expiry banner is static. Sept 28's Procore status-colored pins and today's Procore/Fieldwire drawing items make the same point, that an alert should take you to its items.
- Automation stall: the builders have missed 22 nights. The morning pipeline says the nightly-review producer was deliberately retired, but no one has said the builders were.
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 with git grep and git log --all. On history: Closr+ d62df44 added expiry tracking and 1c6c000/068f0bc added a separate warranty expiry page, but nothing filters closeout documents by expiry or fixes the boundary. Buildr+ has one earlier accessibility commit (9d7228b, paywall pill) and none on project cards. DailyReportr+ has no lazy-list or sliver commit for Reports. The held Sept 28 items (Buildr+ server-confirmed deletes, DailyReportr+ admin failure state) still outrank these.
1. Tap the Expiry Banner — and Stop Losing Docs on Their Last Day Closr+ — Feature (with bug fix)
_buildExpiryBanner (lib/pages/documents/documents_page.dart:288) counts expired and soon-expiring documents and shows "2 documents expired · 3 expiring within 30 days". It's a plain Container with no tap target, and the All tab only offers text search, so the user has to hunt for the documents it names. The counting also has a gap. In lib/models/document.dart, isExpired is now.isAfter(expirationDate), but isExpiringSoon requires daysUntilExpiration > 0, and Duration.inDays truncates. A certificate that expires at 5 PM today is at 0 days at noon, so it's neither expired nor expiring soon. It vanishes from the banner, the red/orange coloring, and CloseoutDocumentNudge on its last valid day. There are no tests for either predicate.
Why now: it's today's morning-brief idea #3, and verifying it turned up a correctness bug the brief didn't mention. Procore's Sept 15 Project Archive work and Autodesk's handover exports treat expiring closeout paperwork as the item to chase. A banner that goes quiet on the deadline day is the opposite. The fix is small, local, and needs no D1 change.
Sketch: In the model, change isExpiringSoon to !isExpired && days != null && days <= 30 (days >= 0), or compare on calendar dates. Wrap the banner in InkWell + Semantics(button: true, label: '… tap to show') and toggle a local _expiryFilter that pre-filters in _applySearch, then compose it with the search query. Show a "Showing expiring only — Clear" chip. Tests: expires in 10 h (soon, not expired), exactly 30 d, 31 d, 1 min ago (expired), null date, and the widget filter/clear round-trip.
2. One Clear VoiceOver Action per Project Card Buildr+ — UI/Code Health (accessibility)
ProjectCard (lib/widgets/project_card.dart:31) is a bare GestureDetector around a thumbnail, status pill, number, name, address, contract value, six _miniChip icons (12 pt RFI, CO, submittal, meeting, transmittal, and lien icons), and a chevron. It has no Semantics node, so VoiceOver or TalkBack walks each fragment separately and never announces a button or an "open" action. The long-press menu (delete and edit) isn't exposed as a custom action at all. The six chips are the same on every card whatever the project contains, so they're pure decoration that screen readers still visit.
Why now: it's today's morning-brief idea #1, and ProjectCard has 0 tests. This is the most-visited widget in the app, so a semantics test plus a golden-free widget test leaves it better covered. It also fits the 50/50 standard's accessibility bucket, and it's a small, contained change for whoever next touches projects_list_page.dart (Dominique's held task edits the same page).
Sketch: Wrap the card in MergeSemantics/Semantics(button: true, label: '${project.name}, ${status}, ${contract value}', hint: 'Open project', customSemanticsActions: {'Project options': onLongPress}). Wrap the mini-chip row and chevron in ExcludeSemantics. Swap GestureDetector for InkWell so it gets a ripple and focus highlight on web and desktop. Tests: the tester.getSemantics label contains the name and status, there is exactly one tap action, the custom action fires onLongPress, and the layout doesn't overflow at textScaler 2.0.
3. Build the Reports List Lazily DailyReportr+ — UI/Code Health (performance)
ReportsPage (lib/pages/reports/reports_page.dart:101) puts the overview card, admin filter, and report list inside a SingleChildScrollView → Column. The list is a ResponsiveDesktopGrid (:187, :369) whose narrow-screen path is another plain Column of every card, and each one is wrapped in a Dismissible. A superintendent with 18 months of dailies pays the build and layout cost for hundreds of cards before seeing the first one. Admins pay it for every user's reports. Twenty other page call sites in the app already use ListView.builder or SliverList, so this page is the odd one out.
Why now: it's today's morning-brief idea #2. It touches the same file as Katey's held admin-failure task and Sept 29's shared ReportSummaryCard extraction. Doing all three as one reports_page.dart rework means one review and one set of tests instead of three conflicting diffs.
Sketch: Replace the scroll view with CustomScrollView: a SliverToBoxAdapter for the overview and filter, then SliverList.builder on narrow screens. Keep ResponsiveDesktopGrid (or a SliverGrid with a fixed cross-axis count of 2) for wide web so the desktop layout is unchanged. Keep RefreshIndicator and Dismissible keys stable. Tests: with a 500-report fixture, fewer than 30 card builds on first frame (count via a builder hook), scrolling to the last report works, and pull-to-refresh and delete still work.
UI Inconsistencies
- Closr+ expiry states disagree at the boundary: a document expiring later today shows no red/orange banner, while the per-category badge logic (
closeout_document_nudge.dart:107-109) uses the same brokenisExpiringSoon. The page reads all-clear on the deadline day (Recommendation #1). The banner is also the only warning on the page with no action. Even the search field next to it has a clear button. - Buildr+ project card chips look like data but aren't: the six colored RFI/CO/submittal/meeting/transmittal/lien chips are identical on every card, whether a project has 0 or 40 RFIs. They use the same color tokens as real status badges elsewhere, which invites users to read them as counts (Recommendation #2). Either drop them or bind them to real counts.
- DailyReportr+ list rendering is inconsistent: Reports is the one high-volume page built eagerly, while most other list pages build lazily (Recommendation #3). Its overview also shows
0on both loading and error, the same "reassuring zero" flagged Sept 28. - Carried over, unchanged: Closr+ Connected Sources shows "Last synced <now>" next to an insert error. DailyReportr+ has a fake "1 activity" badge in two duplicated cards. 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 Document Management: the recycle bin now covers uploads and placeholders (Sept 17), so deleted drawings and document placeholders can be restored. It's relevant to Buildr+ deletes: the benchmark makes deletes recoverable, while ours can drop a card whose server delete failed (held Dominique task). Sept 17, just outside 7 days, new to this brief [source]
- Procore Project Archive enhancements: archives are easier to create, navigate, and audit (Sept 15). It's relevant to Closr+ closeout packages and turnover, and it supports treating expiring and missing closeout docs as first-class, filterable items (Recommendation #1). Sept 15, new to this brief [source]
- Procore AI & Datagrid "Private Space" (Sept 24): agents, datasets, and pages can be kept private and shared with specific teammates. It's in-window but outside our surface area, noted only for completeness. [source]
- Fieldwire mobile Sheet Compare and private (purple) markups on iOS and Android. It's relevant to Inspectr+ drawing revision identity (Sept 29 Recommendation #3). date unverified, likely an older release [source]
- Last ~7 days, otherwise: the only in-window Bluebeam item was the Sept 24 academic availability of Bluebeam Max, which is a press release, not a feature. Revu 21.11 (Sept 1) was already reported. Autodesk Construction Cloud / PlanGrid results were the undated "Download All Attachments", As Built Export, and PlanGrid Data Transition items; the first two were already reported Sept 19–23 and are treated as current capability, not news. [Bluebeam] [ACC]
Tomorrow’s Build Suggestion
This is the twenty-second night without builders, and the assignments are held for the third day. Rotating them doesn't bring the builders back, and both still top the queue. Owner actions before either build: check the 00:15 and 01:15 builder launch jobs (the morning pipeline confirms only the reviewer was retired), 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), plus today's ProjectCard semantics (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 the highest-stakes honesty bug on the list.
Katey → DailyReportr+
Task: a single reports_page.dart rework: first Admin Reports That Admit Failure (the retry loop), then the shared ReportSummaryCard without the fake "1 activity" badge, then the lazy SliverList (Recommendation #3). Rationale: it's the stalest app (16 days on main), and all three fixes target the same file. Closr+ expiry banner + boundary fix (Recommendation #1) and Closr+ partial-sync honesty are queued next for whichever builder frees up first.