Lab Architect / Ellsworth
Afternoon Brief
Wednesday, October 7, 2026 — Twenty-ninth consecutive night with no nightly-branch build on any app. No commits on any ref in any app repo in the last 30 hours. Codebase health: 0 analyzer issues on all four apps. No morning brief was published today (briefs/ still stops at Oct 5). New today: the Inspectr+ Reports tab deletes a report after the swipe animation finishes. If the server call fails, the card is already gone and the report is still in the list, so the next rebuild trips Flutter's "dismissed Dismissible is still part of the tree" assertion. The Projects tab in the same app already does this correctly. That makes the held Buildr+ delete fix a cross-app pattern: 13 swipe-deletes across 3 apps run in onDismissed.
Last Night’s Builds
No nightly-branch build on any app, twenty-ninth night running missed
Ran git fetch origin on all four repos. No feature/nightly-20261007 or feature/nightly2-20261007 branch exists. The newest nightly branches are still from early September (Buildr+ nightly2-20260909, DailyReportr+ nightly-20260909, Closr+ nightly2-20260907, Inspectr+ nightly-20260823). git log --all --since="30 hours ago" is empty on all four. The held assignments (Dominique → Buildr+ Deletes That Wait for the Server; Katey → DailyReportr+ reports_page.dart rework) went unclaimed for a tenth day. Today's Fountainhead morning report (morning-reports/2026-10-07.md) again shows Hermes UP and the nightly review artifacts MISSING. Its log tail still ends Sept 6. The builder coordination file ~/memory/last-nightly-build.md was last updated Aug 5.
Buildr+ 4 commits unpushed · working copy off main
Local main is at a7a5a03 (Sept 21), 4 ahead / 0 behind origin/main (33c825f). The working copy is on fix/project-card-contract-value-spacing. Unchanged.
Closr+ 6 commits unpushed · tracked node_modules
Local main is at 0925b0b (Sept 21), 6 ahead / 0 behind origin/main (7a3ca21). git status still lists deleted-but-tracked node_modules/ paths. Unchanged.
Inspectr+ 7 commits unpushed
Local main is at 8145093 (Sept 21), 7 ahead / 0 behind origin/main (4140149). Unchanged.
DailyReportr+ silent 23 days · working copy off main
main = origin/main = 0433f44 (Sept 14, 3.8.1 TestFlight prep). The working copy is still on the Aug 11 fix/owner-report-media-web branch, with modified tracked files.
Codebase Health
| App | Analyzer Issues | Unimported files | Raw 'Error: $…' text |
Deletes in onDismissed |
Breakdown | Last 3 Commits (local main) |
|---|---|---|---|---|---|---|
| Buildr+ | 0 | 16 | 60 | 10 | "No issues found!" No change. All 10 Dismissible deletes call the server after the card is gone (the held Sept 28 fix). |
a7a5a03 merge harden-ios-release-gate • a2873c3 merge project-card spacing fix • fac04bd source protected secrets file (unpushed) |
| Closr+ | 0 | 12 (+14 transitive) | 35 | 0 | "No issues found!" No change. The 9,645-line dashboard subtree still needs an owner retire-or-re-home decision (Oct 6 Rec #2). | 0925b0b merge harden-ios-release-gate • d4c8952 gate TestFlight IPA on client IDs • ca33394 bump TestFlight 2.32.75 (unpushed) |
| Inspectr+ | 0 | 18 | 5 | 1 | "No issues found!" New today: reports_page.dart:303 (Reports tab, main nav via responsive_navigation.dart:76) deletes in onDismissed. Also, the client reads cleanupPending as a number (report_storage_service.dart:184), but the Worker returns a boolean, so the "cleanup still queued" log never fires. |
8145093 merge Google sign-in client-ID fix • 5e0849b merge harden-ios-release-gate • 744cc12 add iOS artifact gate (unpushed) |
| DailyReportr+ | 0 | 11 | 0 | 2 | "No issues found!" Both swipe-deletes are in reports_page.dart (:224 and admin :408), the same file as Katey's held rework, so they fold into it. |
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. They come from dart analyze lib on fresh git archive main snapshots, after flutter pub get --offline, in /private/tmp/afternoon-brief-20261007/. With no commits since Sept 21, the unimported-file and raw-error counts carry over from Oct 6. The new "Deletes in onDismissed" column counts Dismissible callbacks that start a server delete after the card is already gone. Closr+ has none.
Recurring Themes from Morning Briefs
Sources: the Oct 5, Oct 4, and Oct 3 morning briefs in briefs/. There was no Oct 6 or Oct 7 morning brief, only the Fountainhead morning reports, which repeat the "artifacts MISSING" status.
- Don't show success before it happens: sync receipts,
_markSynced, "Rev. 0", zero impact counts from unloaded data, and now the Reports-tab delete. The UI keeps reporting an outcome before the server has confirmed it. Today's Rec #1 applies the fix Inspectr+ already uses on its Projects tab. - Reachability: Connected Sources, the offline queue, and the Closr+ Dashboard. Today's targets were all checked for a real entry point: Reports is a main-nav tab.
- Drawings and plan context: revision impact, snapshot revision, and sheet reference are still queued. Not repeated here.
- Automation stall: 29 nights. The morning sources still watch only the retired review producer.
Feature Ideas + UI/Code Health Recommendations
There are 2 recommendations: 1 UI/code health item and 1 feature, both in Inspectr+ (no Inspectr+ item has been added since Oct 4). Dedup checks: git log --all -i --grep for "dismiss", "undo", and "delete report" found only paywall and MFA dismissal fixes. grep -rn "Recently Deleted\|restoreReport" lib found 0 hits, and the only "Undo" in lib/ is the photo-markup stroke undo (photo_markup_page.dart:160). Both targets were checked for an entry point: ReportsPage() is constructed in responsive_navigation.dart:76. The held items from Sept 28 to Oct 6 still rank ahead of these.
1. Report Deletes That Wait for the Server Inspectr+ — UI/Code Health (consistency + test)
On the Reports tab, swiping a report asks for confirmation, plays the dismiss animation, and only then calls ReportStorageService().deleteReport (reports_page.dart:303–327). If that call fails (offline, an expired token, or the Worker's 404 report_not_found), the card has already left the screen, but recentReportsProvider is never invalidated. The report is still in the list, so the next rebuild hits Flutter's "A dismissed Dismissible widget is still part of the tree" assertion. That's a red error screen in debug and a stuck list in release. The user also sees 'Error deleting report: $error' with the raw exception text. The Projects tab in the same app does this correctly: it deletes inside confirmDismiss, returns success, and shows a plain "Failed to delete project" (projects_page.dart:288–340).
Why now: this is the same bug as the Buildr+ fix held for 10 days, but in Inspectr+ it's a single call site with a working model in the same repo. It's the cheapest way to establish the pattern before the larger Buildr+ (10 sites) and DailyReportr+ (2 sites) passes. Inspectr+ has no test that covers report deletion.
Sketch: move the try { await deleteReport } catch block into confirmDismiss after the dialog returns true. Return true only on success, and keep onDismissed to ref.invalidate(recentReportsProvider). Replace the raw-error SnackBar with "Couldn't delete this report. Check your connection and try again." (log $error with debugPrint only). Match the Projects-tab SnackBar style (AppColors.error). Also fix report_storage_service.dart:184 to accept cleanupPending == true. Add a widget test with a fake storage service that throws: swipe, confirm, then assert the card is still present and the friendly message is shown. Analyzer stays at 0, onDismissed deletes go from 1 to 0, and raw-error lines go from 5 to 4.
2. Recently Deleted Reports (7-day restore) Inspectr+ — Feature
The Inspectr+ Worker already soft-deletes reports: deleteReport sets reports.deleted_at rather than removing the row (functions/api/[[path]].js, deleteReport). However, it immediately queues and runs R2 cleanup for the report's exported PDFs (processStorageCleanupJobs, called inline), so the files are gone within the same request. A mis-swipe after a long inspection walk means rebuilding the report by hand. Instead, keep exports for 7 days and add a "Recently Deleted" view under Reports with a Restore action.
Why now: Rec #1 makes deletes honest, and this makes them recoverable. Most of the backend is already there (deleted_at on both reports and storage_objects). Procore expanded its Recycle Bin to deleted uploads and placeholders, with metadata preserved (Sept 17, below). Field users expect an undo for destructive actions on inspection records.
Sketch: (1) In the Worker, stop calling processStorageCleanupJobs inline from deleteReport. Give storage_cleanup_jobs a run_after of now + 7 days, and have the processor skip jobs that aren't due. (2) Add GET /reports?deleted=1 (owner-scoped, deleted_at > now-7d) and POST /reports/:id/restore. Restore clears deleted_at on the report and its storage objects and cancels pending jobs with reason='report_deleted'. (3) In Flutter, add listDeletedReports/restoreReport to ReportStorageService, plus a "Recently Deleted" row at the bottom of the Reports list that opens a simple list showing "Deleted 2 days ago · Restore". Give the delete SnackBar a SnackBarAction('Undo') that calls restore. (4) Tests: a Worker contract test (delete → list deleted → restore → artifacts still present) and a service test for the restore round-trip. Ship it after Rec #1, because Undo on a delete that silently failed would be worse than no Undo.
UI Inconsistencies
- Inspectr+ deletes behave differently on its two main tabs: Projects waits for the server and keeps the card if the delete fails. Reports removes the card first and reports failure afterward. The failure SnackBars also differ: Projects shows the friendly "Failed to delete project" with
AppColors.error, non-floating. Reports shows the raw"Error deleting report: $error", floating, withTheme.colorScheme.error. The confirm dialogs differ as well: Reports uses "Confirm Deletion" with a redFilledButton, while Projects uses_showDeleteConfirmationDialog. Rec #1 aligns the first two. - Swipe-delete semantics differ across apps: Closr+ has no swipe-deletes. Buildr+ (10), DailyReportr+ (2), and the Inspectr+ Reports tab (1) delete after dismissal. The Inspectr+ Projects tab deletes before dismissal. No app offers Undo. A shared
ConfirmedDismissible(onConfirmDelete: Future<bool> Function())in each app's widgets would fix all three at once. - Carried over, unchanged (no commits): Closr+ uses three different error treatments on the project page (Oct 6). The Documents tab hides 4 of its 5 failure branches. The Buildr+ custom
Icons.arrow_back_iosback button appears on 9 reachable pages. The Inspectr+ gallery ignores the tapped index. The DailyReportr+ "Today" pill looks tappable but isn't. RawColors.green: DailyReportr+ 40, Inspectr+ 29, Buildr+ 0, Closr+ 0.
Competitive Intel
- Procore Connected BIM Object Linking (September 2026, exact day not shown). It links 3D model objects to Assets, Inspections, RFIs, and Submittals with spatial context. This is the model-side version of the drawing-reference theme queued for Inspectr+ and Buildr+. Our apps link to sheets, not models, and that's the right scope for now. new to these briefs [source]
- Procore Recycle Bin expansion (Sept 17). Deleted uploads and placeholders can now be recovered, with metadata preserved and strict permissions. It's directly relevant to Rec #2. outside the 7-day window, new to these briefs [source]
- Procore Groundbreak 2026, Oct 21–22 (Orlando). Expect a burst of feature announcements in two weeks. Plan a dedicated intel sweep for the Oct 22–23 briefs. upcoming [source]
- Not used: Procore Project Map status pins (already reported Oct 3), iOS 2026.0923 (already reported Oct 3 and Oct 4), the EU zone (infrastructure), timesheet grid (out of scope, already reported Oct 1 and Oct 2). The Autodesk "Assistant / Handover / Drawing Extraction" announcement is undated (forconstructionpros returns 403), and Autodesk Assistant was already reported Oct 1. Bluebeam Max (early 2026) is not news this week. No new Fieldwire or PlanGrid ship was found. It's another quiet week for in-scope features.
Tomorrow’s Build Suggestion
This is the twenty-ninth night without builders. The assignments stay held (tenth day). Queue change: Inspectr+ Report Deletes That Wait for the Server (today's #1) goes to the front of the shared queue, because it's one call site with an in-repo model. Owner actions before any build: check the 00:15 and 01:15 builder launch jobs. Push the unpushed main commits (Buildr+ 4, Closr+ 6, Inspectr+ 7). Decide on the Closr+ Dashboard and Connected Sources.
Dominique → Buildr+
Task: Deletes That Wait for the Server (Sept 28 Rec #1), using the Inspectr+ projects_page.dart:288 confirmDismiss pattern for all 10 sites. Rationale: Buildr+ hasn't had a nightly feature since Aug 5 and has the most onDismissed deletes (10) and raw error text (60).
Katey → DailyReportr+
Task: the single reports_page.dart rework (Admin Reports That Admit Failure → shared ReportSummaryCard → lazy SliverList), now including moving the two swipe-deletes (:224, :408) into confirmDismiss. Rationale: it's the stalest app (23 days on main), and every open DailyReportr+ issue is in this one file. Queued next for whichever builder frees up first: Inspectr+ Report Deletes That Wait for the Server (today's #1), Closr+ Errors a Closeout Manager Can Act On (Oct 6), Inspectr+ Drawing References That Admit Failure (Oct 4), then Inspectr+ Recently Deleted Reports (today's #2).