Skip to content

[obvious-fleet] fix(ios): make Not-this-item on ingredient review never a silent no-op - #36

Draft
obvious-autobuild[bot] wants to merge 3 commits into
alphafrom
obv/fridgeluck-046
Draft

obvious-autobuild[bot] wants to merge 3 commits into
alphafrom
obv/fridgeluck-046

Conversation

@obvious-autobuild

@obvious-autobuild obvious-autobuild Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

fleet-packet: burst6-cont-fridgeluck-36

What changed and why

Continuation of this PR's original change: "Not this item" on a needs-confirmation ingredient-review row was a silent no-op when no candidate was selected (first run, empty learning cache). It now always produces a visible state change:

  • clearSelection routes through a new pure state transition, IngredientReviewView.notThisItemOutcome(detection:selectedIngredientId:suggestedIngredientId:), which (a) removes a previously selected suggestion from the confirmed set, (b) clears the selection entry, (c) rejects the row (rejectedDetectionIds), and (d) records the learning outcome for a shown suggestion (unchanged telemetry semantics).
  • Rejected rows leave every review-screen aggregate: the needs-confirmation list/count, unresolvedCount, confirmationCompletion, the divider between sections, and the confidence-health pill ("All clear" once nothing is left).
  • The Yes / Not-this-item buttons get 44pt minimum tap targets, contentShape(Rectangle()), and accessibilityIdentifier("ingredient_review_not_this_item").
  • New apps/ios/Tests/IngredientReviewNotThisItemTests.swift covers the pure transition: first-run no-selection (previously the dead-tap case), selected-suggestion rejection, and suggestion-matches-vision-label counting as accepted.

Port onto alpha. The original base feat/minimal-product-20261007 is stale against the current integration branch alpha (69 alpha commits since merge-base 8c9c88f; alpha tip 0309dd1). The change is ported by merging origin/alpha into obv/fridgeluck-046 as merge commit 5cfb966 — chosen over rebase so the evidence commits 31a4819 and ebb6047 keep their hashes and the earlier CI runs on them stay attributable. The merge had zero conflicts. The PR base was retargeted from feat/minimal-product-20261007 to alpha.

How I verified it (commands and results)

  • git merge origin/alpha --no-ff → zero conflicts; merge commit 5cfb966 with parents ebb6047 + 0309dd1.
  • git diff --stat origin/alpha..HEAD → exactly 3 files, +130/−10 (IngredientReviewSections.swift +5, IngredientReviewView.swift +67/−10, Tests/IngredientReviewNotThisItemTests.swift +68): the merged head is alpha plus only this change.
  • git diff --stat ebb6047..HEAD → 160 files, +17147/−677, all alpha-side; none of this PR's files are touched.
  • grep on the merged IngredientReviewView.swift: alpha's location: .inferredFromIngredient call-site change (line 71) coexists with rejectedDetectionIds (line 96), reviewableNeedsConfirmation (line 493), and notThisItemOutcome (lines 695/733).
  • swift-frontend -parse (Swift 6.1.2, Linux) on the three touched files → no parse errors.
  • swift test --package-path apps/ios (Linux) → compilation of FLFeatureLogic fails at LiveAssistantPanelLayout.swift:1:8: error: no such module 'CoreGraphics'. This is a pre-existing limitation of the package on Linux (CoreGraphics/SwiftUI are Apple-only), not something this branch introduced; the repo's own iOS tests are xcodebuild test against the generated Xcode project and cannot run in this sandbox.
  • Read-level consistency check: the three XCTest cases' expected values match the pure function's contract exactly ((nil, nil) → reject only; (5, 5) vs ingredient 7 → reject + clear + recorded false; (9, 9) → recorded true).
  • Hosted macOS CI (the repository's own check) on merged head 5cfb966, run https://github.com/SamGu-NRX/FridgeLuck/actions/runs/38755647018: build-and-test pass (6m52s), dead-code-advisory pass (2m44s), CodeRabbit pass.

What is not verified

  • No Xcode / iOS 26 simulator in this sandbox, so the new XCTests (IngredientReviewNotThisItemTests) were not executed locally; they run in the hosted build-and-test job. Everything else about the SwiftUI behavior (44pt targets, contentShape, VoiceOver identifier) also rests on that job — no device run was possible.
  • Nothing was verified in a real app run: no manual exercise of the review screen, and the visual/interaction change has no device evidence.

Assumptions

…ure function

clearSelection(for:) was instance-bound SwiftUI state, untestable. Extract
IngredientReviewView.notThisItemOutcome as a pure decision and apply its
outcome in clearSelection. Behavior-preserving: didRejectDetection is false,
so the new rejection wiring is inert; the added tests encode the contract we
want (a tap must never be a silent no-op) and fail on this commit by design.
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d057f484-bba1-4986-bab1-5ded5e7228ce

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…rget

On a first run the learning cache is empty, so no candidate chip is
pre-selected and clearSelection mutated nothing at all — the tap reported
dead in the first-run walkthrough. It did not miss: in that state the action
was a guaranteed silent no-op.

- notThisItemOutcome now always rejects the row, so every tap produces a
  visible state change (the row leaves the review list; counts, pill and
  progress bar follow via reviewableNeedsConfirmation). A never-selected
  row was never ingested (ingestConfirmedScan skips unconfirmed detections),
  so this is review-screen state only.
- 'Not this item' and 'Choose another' had ~17pt tall hit targets (caption
  label, no frame expansion); both now get a 44pt minimum frame with a
  contentShape, per the HIG minimum.
- Adds accessibilityIdentifier ingredient_review_not_this_item.
SamGu-NRX added a commit that referenced this pull request Oct 11, 2026
…no-op

Folds obv/fridgeluck-046 @ ebb6047 into the app integration trunk, fold 6 of 12.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
PR #36's obv/fridgeluck-046 was cut from feat/minimal-product-20261007, which is
stale against the current integration branch alpha (69 commits ahead at 0309dd1).
Merge origin/alpha into the branch so the change integrates with alpha; the
evidence commits 31a4819 and ebb6047 are preserved verbatim.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
@obvious-autobuild
obvious-autobuild Bot changed the base branch from feat/minimal-product-20261007 to alpha October 15, 2026 14:55
@obvious-autobuild

Copy link
Copy Markdown
Author

Independent review (Obvious, Autobuild + Max)

Verdict: ready. Two low-severity findings below, neither a merge blocker. Disclosure for context: this review comes from the same automation fleet that authored the PR; every claim below was re-derived from the repo and CI logs, not taken from the PR body.

What I verified (commands and results)

  • Read the full diff, all comments/reviews, and AGENTS.md (no CLAUDE.md/CONTRIBUTING exists). Diff is exactly 3 files, +130/−10, no drive-by changes; clearSelection has exactly one caller (onClear from the needs-confirmation section), so the new rejection semantics can't leak into other sections.
  • Port structure: head 5cfb966 = merge of ebb6047 + 0309dd1 (origin/alpha); git diff --stat origin/alpha..HEAD = the PR's 3 files only. Matches the body.
  • CI: run 38755647018 ran on headSha 5cfb966de… — build-and-test pass (6m52s), dead-code-advisory pass (2m44s), CodeRabbit pass. gh run view --job=116493221581 --log shows all three new tests actually executed and passed on macOS (Test Suite 'IngredientReviewNotThisItemTests' passed; Test Suite 'All tests' passed).
  • Core behavior on Linux Swift 6.4: extracted the pure transition verbatim from the PR head into a standalone harness (with a mirror of Detection), replayed the three test cases' assertions → 11/11 checks pass.
  • Fail-without-change: the new tests target a new API and can't compile against the base, so I ran the equivalent check — extracted the pre-change clearSelection from origin/alpha (git show 0309dd1:…) and modeled its observable state change. On the first-run case (no selection, no suggestion) the old code mutated nothing (nothing removed, removeValue on a missing key, no rejection state, nothing recorded) — the reported dead tap is real, and the new first-run outcome differs from it, so testNotThisItemRejectsARowEvenWhenNothingIsSelected discriminates old from new.
  • Premise check: selectedIngredient(for:) is a bare selectedIngredientForDetection[detection.id] lookup with no fallback, so the transition's inputs are identical to what the old code read — no behavior change for the already-working paths.
  • Save-path safety: findRecipes ingests detections + confirmedIds + selectedIngredientForDetection; a rejected row is in none of them, so "rejection is purely review-screen state" holds. Learning telemetry semantics are unchanged (suggestion == detection.ingredientId, only when a suggestion was shown).
  • Linux swift test --package-path apps/ios fails at FeatureLogic/Assistant/LiveAssistantPanelLayout.swift:1:8: no such module 'CoreGraphics' — reproduced, pre-existing, matches the body.

Findings (ranked)

  1. Low — one divider still keys on the unfiltered list — apps/ios/Feature/Ingredients/IngredientReviewView.swift:206: the divider between AUTO-DETECTED and NEEDS-CONFIRMATION uses !categorized.needsConfirmation.isEmpty, while the NEEDS-CONFIRMATION↔MAYBE divider (line 234) correctly uses reviewableNeedsConfirmation. After rejecting every row the section renders nothing (its body guards if !detections.isEmpty) but this divider survives — visible when there are auto-detected items and no MAYBE items (stray trailing divider). This also makes the body's "Rejected rows leave every review-screen aggregate: … the divider between sections" claim half-true: only one of the two dividers was switched. Smallest fix: use reviewableNeedsConfirmation in the line-206 condition too.
  2. Low — rejection has no in-session undo, and "Clear" doesn't restore rows — clearUncertainSelections (IngredientReviewView.swift:644, toolbar button at line 590) resets selections but not rejectedDetectionIds, so a rejected row can never come back without leaving the screen. Defensible as designed (rejection ≈ dismissal), but if that toolbar action is meant to be a full reset, add rejectedDetectionIds.removeAll() there. Not a regression — base had no rejection at all.

Informational, not a finding: didClearSelection/didRejectDetection are constants (true) in NotThisItemOutcome, so the two call-site guards and the corresponding test assertions can't fail. Harmless; the varying fields (removedConfirmedIngredientId, recordedSuggestedOutcome) are well covered by the three tests.

What I couldn't verify

  • No Xcode/iOS simulator here: no local run of the app or the new XCTests (they ran on hosted macOS CI, above), and no device/VoiceOver check of the 44pt targets, contentShape, or the accessibility identifier — all UI-touching behavior rests on CI plus code reading.
  • No visual evidence either way: I did not capture before/after screenshots of the review screen; CI is the only behavioral check the change has beyond the unit tests.

Nothing blocking: the fix does what it claims, the tests exercise the real path and fail against the old behavior, and the scope is right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants