Repository navigation
Conversation
…ckage Add a UI-free Progress read model that maps today's nutrition, day/ 7/30/90-day trends, recent journal entries, and goal targets from the existing persistence layer, preserving each number's provenance: local journal entry vs Apple Health total, confirmed personal target vs suggested default. Unknown days stay distinct from zero intake and averages cover days with data only. ProgressRevisionToken tracks portion multipliers, cooked-at epochs, and a health-profile digest — dimensions the shared dashboard observer omits — so Progress invalidates on portion edits, date shifts, and profile/goal changes. ProgressRangeState owns presentation state for range switches: stale results are dropped, failures retain the last good reading, and every reading is labelled with its single source. New offline package Tools/progress-flow-check mirrors the historical nutrition check pattern: refresh.sh stages the real production sources into a Linux-runnable package, tests seed the actual migrations and services with synthetic data, and the suite asserts portion, date, and profile revisions through the real aggregation APIs. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ProgressRangeCoordinator owns the trend-range presentation state end to end: selecting a range or refreshing cancels the in-flight read, stop() (view close) cancels it and tears down the revision observation, and every revision-token delivery triggers an explicit refresh — so accepted portion/date revisions and profile/goal edits update Progress without user action. Apple Health imports bypass the tracked tables, so the view model refreshes on Health-import events and on appear. Failures keep the last good reading and surface a readable message: ProgressReadFailure.from unwraps our own kind instead of re-describing the enum. The coordinator is lock-guarded rather than MainActor-isolated so the off-host suite can drive it deterministically; the SwiftUI view model hops to the main actor when mirroring state into @published. Tests assert real cancellation (a parked load observes CancellationError when the selection changes and when the view closes), that no load starts after close, that journal and profile revisions trigger refresh through the real loader over the real database, and that a failing refresh retains the last good reading. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
The Progress tab now renders from the provenance-preserving read model: the trend chart consumes coordinator-owned range state (selection, in-flight load, last good reading), meal cards route taps to the existing journal detail screen, and source/aggregate-policy notes sit behind on-demand disclosures. A suggested default target is pill-labelled so it never presents as user-set, and the meal-logging entry follows the "Log a Meal" / fork.knife convention with an explicit accessibility label. Provenance note wording is staged into the offline package and covered by tests (34 passing on Linux). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…e script Contrast ratios are now computed on-host from the actual DesignSystem token values (WCAG 2.x luminance math) rather than asserted from memory. The math caught a real violation — sage on its own 8% tint computes 2.87:1, below the 3:1 graphics threshold — so the insight row dropped the tint, and row/note text moved to textPrimary for AA small-text compliance (13.94:1 on the page background). Light-mode textSecondary computes 3.66:1, the app-wide subtitle convention: flagged in the evidence ledger as a design-system finding, not fixed here. The macOS fixture-capture script (390x844 and 1440x900 PNGs plus the XCUITest accessibility-audit path) is committed marked UNEXECUTED — this host has no Xcode, and nothing was substituted for the missing PNGs. 39 tests passing on Linux; raw log committed. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Workspace retirement checkpoint: preserves the human-review record and acceptance-criteria status beyond the retiring workspace. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Progress flow: provenance read model, owned range state, wired UI (M1–M4)
Draft PR — work continues in milestones. Base
obv/fl-next-historical-nutrition-r1, headobv/fl-l4-progress-flow.What changed and why
The Progress tab aggregates day/7/30/90-day intake from the local cooking journal and Apple Health, but the surfaces that own those numbers do not tell Progress when their details change: the shared cooking-history observer tracks row count, max id, a rating checksum, and a servings checksum, so a portion edit or a date shift changes aggregates while leaving that token untouched, and profile/goal changes are invisible to it entirely. Progress cards would go silently stale. Separately, the repository's daily-total windows zero-fill missing days and averages divide by every window day, so "no data" days present as 0 intake; and nothing distinguishes a confirmed personal target from the app default.
M1 — provenance read model (commit
d605b8e)apps/ios/Feature/Progress/ProgressReadModel.swift— typed readings (ProgressTodayReading,ProgressRangeReading,ProgressRecentJournalEntry,ProgressStats,ProgressGoalTarget) that carry each number's provenance:localJournalvsappleHealthsource per reading, optional per-day values (value == nilmeans no data, never 0),suggestedTargetvsconfirmedPersonalTargetgoal provenance, and a visiblefallbackReasonwhen the requested source failed. Averages cover days with data only.ProgressRevisionTokentracks portion multipliers (milli-units), cooked-at epoch checksum + max, and a health-profile digest over the same shared database, exposed as aValueObservationso Progress invalidates on portion edits, date shifts, and profile/goal changes.apps/ios/Feature/Progress/ProgressRangeState.swift— pure presentation-state transitions for day/7/30/90-day selection: stale results for a superseded range are dropped, failures keep the last good reading and surface an error.apps/ios/Feature/Progress/ProgressFlowPolicy.swift— routing decisions for end-to-end wiring: tapped meals route to the existing journal detail, correction requests hand off when a correction flow is already active, goal editing is offered through the app's existing goal-editor callback only.apps/ios/Tools/progress-flow-check/— new offline package mirroring the historical-nutrition-check pattern (Scripts/refresh.shstages real production sources, including the real migrations and repositories, intoSources/ProgressFlowCheck/Real/). Tests seed the app's actual persistence layer with synthetic data through real migrations andPersonalizationService.recordCooking, then drive the read model through its public API — no re-implemented arithmetic.M2 — state ownership (commit
6daa89c)apps/ios/Feature/Progress/ProgressRangeCoordinator.swift— owns the trend-range presentation state end to end: selecting a range or refreshing cancels the in-flight read;stop()(view close) cancels all work and tears down observation; journal/profile revisions trigger an explicit refresh through the real loader; failures retain the last good reading and surface the error. The coordinator is injectable:.live(...)drives the real read model on a database queue,.mock(...)serves synthetic readings for tests and previews.DatabaseQueue) rather than piggybacking on the Home dashboard observer, because the revision dimensions (portion/date/profile) are exactly the ones the shared observer omits.M3 — wired UI + on-demand provenance notes (commit
f7ea3b4)ProgressViewModelnow loads through the read model (todayReading,weeklyReading,goalTargetare provenance-tagged), owns its revision-token observation (portion edits, date shifts, profile/goal changes → reload + trend refresh) and an Apple Health import observer (.appleHealthDidUpdate→ reload + explicit trend refresh). The duplicateChartRangedeclaration that lived in this file was removed (single definition inProgressRangeState.swift).ProgressWeeklyTrendSectionrenders coordinator-owned state — selection, in-flight load, last good reading on failure, source-labelled subtitle with coverage ("5 of 7 days with data · from your meal log"), a distinct fallback row when the Health read failed, and an error row. Unknown days are never plotted as zero: only days with data enter the chart; the empty chart says "Cook a meal to start tracking!" rather than drawing a zero line. Averages in the insight text cover days with data only.ProgressRecentMealsSectionmeal cards are tappable and route throughProgressFlowPolicy.mealRoute(for:)to the existingRecipeJournalDetailView— no new journal editor; a portion/date revision lands in the journal and the revision-token observer refreshes the cards instead of leaving them stale.ProgressSourceNotesbuilds the wording (pure logic, staged into the offline package and tested) — the tab exposes a "Where do these numbers come from?" disclosure naming the source, the unknown-day averaging policy, coverage, and whether the target is saved or suggested. The calorie hero pill-labels suggested targets ("Suggested target") so a default never presents as user-set.onOpenProfileSettings); the meal-logging entry keeps the "Log a Meal" /fork.knifeconvention with an explicit accessibility label (PR 35's intent), and the correction-handoff rule is encoded inProgressFlowPolicy(see Assumptions for the UI surface note).M4 — accessibility evidence (commit
33ec552)apps/ios/Tools/progress-flow-check/Tests/ProgressFlowCheckTests/ProgressContrastTests.swift— WCAG 2.x contrast ratios computed on-host from the actualAppTheme.swifttoken values, not asserted from memory. The math caught a real violation: the insight row's sage icon on its own 8% tint computed 2.87:1, below the 3:1 graphics threshold — the tint was removed (sage on the page background computes 3.10:1 light / 7.83:1 dark), and row/note body text moved totextPrimary(13.94:1 on the page background, light mode) so small text passes AA.textSecondarycomputes 3.66:1 on the page background — below AA for small text. This is the app-wide subtitle convention (everyFLSectionHeader), out of this task's file scope; documented in the ledger.apps/ios/Tools/progress-flow-check/Results/EVIDENCE.md— the evidence ledger: every required check (contrast, keyboard, large type, reduced motion, labels, screenshots, compilation) mapped to verified-on-host / source-asserted / unexecuted-on-host with reasons and how-to-run instructions.apps/ios/Tools/progress-flow-check/Scripts/capture-fixtures.sh— macOS/Xcode script that renders the Progress screen at 390x844 and 1440x900 from synthetic fixtures, exports PNGs, and drives the native XCUITest accessibility audit (performAccessibilityAudit()). Committed marked UNEXECUTED — this host has no Xcode; no PNGs exist and nothing was substituted (no webmocks; Playwright/axe are web tools and were not used).Commands and results
Host: Debian 13 Linux, Swift 6.4 (x86_64-unknown-linux-gnu), no Xcode/macOS.
Computed contrast values printed by the suite (mirrored token values from
AppTheme.swift): textPrimary/bg 13.94:1 light / 16.05:1 dark; textPrimary/surface 15.46:1 / 13.76:1; accent/bg 3.65:1 / 6.37:1; sage/bg 3.10:1 / 7.83:1; textSecondary/bg 3.66:1 / 8.78:1.Raw logs are committed per milestone under
apps/ios/Tools/progress-flow-check/Results/(M3:progress-flow-check-m3.log, M4:progress-flow-check-m4.logplusEVIDENCE.md). The staged "Real" sources are the app's actual read-model/policy/coordinator files plus the real migrations and repositories; the seeded tests drive them through public API against synthetic data.Actual vs missing native execution
Verified on-host (Linux):
ProgressContrastTests) computed from the actual DesignSystem token values — including one real violation found and fixed (sage on its own tint, 2.87:1) and one design-system-wide gap flagged (light-mode textSecondary 3.66:1).UNEXECUTED on this host, with reasons:
ios-ci.ymlon pull_request) is the hosted compilation evidence. The app-target files changed here (view model + 5 views + 1 new view) were compiled only as far as Linux allows (the pure logic they call is package-tested);project.ymlglobs the Feature directory, so the newProgressSourceNotes.swiftis picked up automatically.Scripts/capture-fixtures.sh, committed, marked UNEXECUTED): renders the Progress screen at 390x844 and 1440x900 from synthetic fixtures and exports PNGs; requires Xcode previews/simulators. Not run — no macOS. Never substituted with webmocks; Playwright/axe were not used (web-only tools).Results/EVIDENCE.mdmaps every check to its status with how-to-run instructions.Assumptions
ProgressFlowPolicy.correctionHandoff(isCorrectionFlowActive:)encodes the handoff rule and is tested, but there is no Progress UI that currently needs to call it — adding a correction entry point to Progress would expand the surface this task was scoped away from. The rule is wired for whoever adds that entry point.UserDataRepository's existing daily-total windows as its per-day source and applies the unknown-day/average policy at the read-model layer, rather than changing repository arithmetic (out of scope). This means zero-filled days from the repository are mapped back to "no data" where the journal has no rows for that day — the read model decides emptiness from the journal's day keys, not from the zero value.AppleHealthService.authorizationStatus()failing or returning data-less results is treated as "unknown day" rather than zero, with the fallback reason surfaced.Expected conflicts
apps/ios/Tools/*like this one. No file overlap observed at push time; conflicts would only arise if a sibling edits shared files (apps/ios/Package.swift,project.yml,ios-ci.yml) — none of which this PR touches.ProgressTabView.swift. This PR preserves its "Log a Meal" /fork.knife/ accessibility-label convention, but if it lands first there may be a textual conflict in the actions section.Handoff (final checkpoint, 2026-10-11 — workspace retirement)
The Obvious workspace/account that produced this PR is retired at ~2026-10-11 07:20Z. Its dispatched human-review task and workspace review-summary artifact do NOT survive retirement; the durable review record is
apps/ios/Tools/progress-flow-check/REVIEW-SUMMARY.md(committed on this branch by the final checkpoint commit). Review must be re-requested in the successor workspace.Done
d605b8e— typed provenance read model (local-journal vs Health source; unknown day != zero; suggested vs confirmed target; revision token covering portion/date/profile) +apps/ios/Tools/progress-flow-checkoffline package with synthetic seeded-DB tests.6daa89c— ProgressRangeCoordinator: cancels superseded loads, stops on view close, refreshes on revision-token change, retains last good reading on failure.f7ea3b4— UI wiring: view model through read model + coordinator, trend source/coverage/fallback states, meal-card tap-through to RecipeJournalDetailView, on-demand provenance notes, suggested-target pill, "Log a Meal"/fork.knifeconvention preserved (PR 35 intent).33ec552— accessibility evidence: WCAG contrast computed from real DesignSystem tokens (fixed real 2.87:1 sage-on-tint violation in Progress; flagged app-wide light-modetextSecondary3.66:1 as a separate out-of-scope decision),Results/EVIDENCE.mdledger,Scripts/capture-fixtures.sh(macOS capture script, committed marked UNEXECUTED).REVIEW-SUMMARY.mdmirrored into the repo as the durable review record (see Retirement note inside).build-and-test(macos-26, Xcode 26.6) anddead-code-advisory— that is the app-compilation evidence; the producing host was Linux (no Xcode).NOT done
Exact next steps
apps/ios/Tools/progress-flow-check/REVIEW-SUMMARY.mdas the review record (it contains lead verification of authorship, CI, logs, and the acceptance-criteria status).bash apps/ios/Tools/progress-flow-check/Scripts/capture-fixtures.sh— produce the two PNG sizes plus the native a11y audit, commit outputs underResults/, and flip the affected EVIDENCE.md rows from UNEXECUTED to executed.textSecondary3.66:1 contrast finding (out of this PR's scope by design).Verify commands
git rev-parse HEAD— must equal the final checkpoint commit (33ec552 + one docs commit).git log --format='%an <%ae>' obv/fl-next-historical-nutrition-r1..HEAD— every line must beSam Gu <127461594+SamGu-NRX@users.noreply.github.com>.git log --format='%B' obv/fl-next-historical-nutrition-r1..HEAD | grep -c 'Co-authored-by: obvious-autobuild'— expect 5 (one per commit).swift test --package-path apps/ios/Tools/progress-flow-check— expect 39/39 passing (verified on Debian 13, Swift 6.4.0; raw logs underResults/).build-and-testanddead-code-advisoryshould be green.Decisions recorded