Skip to content

feat(ios): let the homeowner answer "I can't check this area" on the spot check - #206

Open
SamGu-NRX wants to merge 7 commits into
fix/spot-fallback-20261003from
fix/spot-unknown-20261003
Open

SamGu-NRX wants to merge 7 commits into
fix/spot-fallback-20261003from
fix/spot-unknown-20261003

Conversation

@SamGu-NRX

@SamGu-NRX SamGu-NRX commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #204 (fix/spot-fallback-20261003). This PR adds one commit on top of it.

What changed

Before the result, the spot check asks whether anything stands in the battery's area. Until now it offered only "It's clear" and "Something's there", so a homeowner who couldn't see or reach the area had to guess. The spot check now offers a third answer, "I can't check this area", with and without a photo. It has the same size and style as the other two, so it doesn't read as a lesser option.

The answer means the area wasn't observed. It is neither an obstruction nor a clear answer:

  • Claims. As with "Something's there", the scan takes back its wall, ground and walked-path claims over the area (CoverageMap.withdrawClaims), so scene.json reports the area unseen, and the scan is sent again. The guidance log closes the check as skipped, the packet's existing generic outcome. The packet and scene schemas are unchanged.
  • Returned spot. When the new answer names the same area, the earlier answer settles it as given (SpotConfirmations.settling). The result then says: "You couldn't check the area around this spot, so your scan leaves it out as not checked. Someone would need to check it in person." The homeowner isn't asked the same question again. A spot that moved, or an area that grew, is asked about as before.
  • A result without that spot. The area's claims stay withdrawn for the rest of the scan, so the result keeps saying so when the answer sent again names no spot, or another spot: "You couldn't check an area along this wall, so your scan leaves it out as not checked. Someone would need to check it in person." Beside a notice about the result's own spot it reads "You also couldn't check another area along this wall". The scan's confirmation records decide this (SpotConfirmations.leftUnchecked(besides:)), and the engine republishes it as ScanViewState.uncheckedAreaElsewhere whenever they change, so Start over and a new wall clear it with the records.
  • Copy. The VoiceOver hint and the acknowledgment say the same: the area is left out as not checked, the wall is checked again, and someone would need to check it in person. None of the three texts says that something was seen there or that anyone has been asked to look.

The answer is typed throughout. ScanActions.answerSpotCheck(clear:) becomes answerSpotCheck(_: SpotCheckAnswer), and both SpotCheckAnswer and HouseScanKit's SpotConfirmationAnswer gain cannotCheck. The kit owns what each answer does. keepsClaims is true only for clear, and guidanceOutcome is met only for clear. The engine reads both instead of comparing a Bool. "It's clear" and "Something's there" behave as before.

Why

A forced guess corrupts the one input that backs the scan's unobserved claims. A wrong "It's clear" lets those claims stand, and a wrong "Something's there" reports an obstruction nobody saw. An explicit answer for "I couldn't look" keeps the record honest.

This changes the answers, not the area the question covers. The outline and the words still describe the same area as on #204.

UI changes

New states, with no before captures (the third button didn't exist):

  • The spot check with three answers, with a photo and without one, at the default and AX5 text sizes: spotConfirm-answers-photo, -noPhoto, -photo-AX5 and -noPhoto-AX5.
  • The acknowledgment after "I can't check this area": spotConfirm-cannotCheck-photo and -noPhoto.
  • The result notice: result-spotNotChecked and -AX5.
  • The wall-wide notice: result-noSpot-scanNotChecked and -AX5 (an answer without a spot), and result-spotAndScanNotChecked (beside the spot's own notice).
  • The same screens from the real engine on the synthetic replay: engine-spotConfirm, engine-result-spotNotChecked, engine-result-spotNotChecked-afterRefine, engine-result-noSpot-scanNotChecked and engine-result-otherSpot-scanNotChecked.

The captures come from the hosted iOS run's ui-test-results artifact. No motion changed. The answer card uses the transition it already had.

Verification

  • HouseScanKit SpotConfirmationTests covers four new cases. Each answer keeps its own meaning. "I can't check this area" is recorded as given and settles the same or a smaller area as itself. It doesn't settle a moved, wider, deeper or shifted area. It and "Something's there" replace each other as the latest answer.
  • SpotConfirmUITests has three new tests on the demo. All three answers can be tapped, are at least 44 pt tall and stay on screen, with and without a photo, at the default size and at AX5, where the test scrolls to them. The acknowledgment's exact words are checked. The result notice is checked word for word at both sizes, together with the accessibility audit. "Something's there" keeps its own notice, and "It's clear" shows none.
  • SpotCannotCheckUITests runs the real engine on the synthetic wall with the sample answer (-autopilotCannotCheck). It audits the engine's spot check with three answers. It then checks that both the uploaded and the exported scene.json claim no wall, ground or facing span over the withdrawn stretch, and that the result shows the not-checked notice and not the obstruction notice. After a ground refine takes the answer down (-injectGroundRise), the result returns with the same notice, and the spot check never reappears.
  • The answer sent again without that spot: HouseScanKit cases for an area held by, or apart from, the spot shown, including a later "It's clear" about a larger area round the same footprint, which must not hide the earlier unchecked one. On the demo, a result without a spot shows the wall-wide notice word for word at both sizes, and beside a spot's notice it reads "You also couldn't check another area". On the real engine, a debug-only launch argument (-sampleResultAfterSpotAnswer) answers the uploads after the first spot answer from a fixture: after "I can't check this area", an answer without a spot (reject-nearest.json) and one naming another spot (spot-left.json, answered clear) both show the wall-wide notice, and after "Something's there" an answer without a spot shows none.
  • iOS run 37117211558 on e2ef03f, checked out as the merge e8e8825 onto fix(ios): describe the spot-check area when no photo is available #204's 803535d, passed. HouseScanKit's tests passed, the build passed, and the UI tests ran 69 with 1 skipped (testFullFlowFromRealReplay, which needs a local recording) and 0 failures. That includes all 9 SpotConfirmUITests, all 4 SpotCannotCheckUITests and FullFlowUITests.testSomethingThereChecksTheWallAgain, the unchanged "Something's there" path. The earlier head 892acdc passed run 37112742785.
  • Nothing ran locally. The Mac's shared heavy-job slot was taken, so the hosted runs are the only builds.

Risk

  • The sample answer names the same spot every time. No test covers a real server that moves the spot after "I can't check this area". That path uses the existing settlement rules, which the kit tests cover.
  • The VoiceOver hint can't be read by XCUITest, so only the source checks its text.
  • At AX5 the result notice is taller than the screen. It is read by scrolling, which the test checks top edge first, then bottom edge.
  • When "I can't check this area" is answered about an area and then about a larger area round the same footprint, the result shows the larger area's own notice and also "You also couldn't check another area", which the larger area already covers (Codex's comment on SpotConfirmation.swift). The claims stay withdrawn and nothing unsafe follows; only the wording repeats. Left as a known limit.
  • No test covers two scans in one launch: the autopilot drives one scan, so the reset of the wall-wide notice and of the test answer file rests on Start over and a new wall replacing the spot records.
  • Not tried on a phone.

Checklist

  • The scope is focused; related changes belong together
  • I explained what changed and why
  • I included before/after images for visible changes (new states only; the captures come from hosted CI)
  • N/A video: no motion changed

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an “I can’t check this area” option for spots that can’t be seen or reached. The area is marked unchecked, its coverage claims are withdrawn, and the scan continues.
    • Results now distinguish an unchecked spot from another unchecked area and explain when an in-person check is needed. A later check of the same spot can settle the notice.
  • Documentation
    • Updated scan guidance and demo instructions to describe the new response and result notices.

…spot check

A homeowner who can't see or reach the spot's area had to guess between
"It's clear" and "Something's there". The spot check now offers a third
answer, the same size as the other two, with and without a photo.

The answer is typed end to end (ScanActions.answerSpotCheck(_:),
SpotCheckAnswer, SpotConfirmationAnswer.cannotCheck). It means not
observed: the area's wall, ground and walked-path claims are withdrawn
and the scan is sent again, as for "Something's there", and the guidance
log closes the check as skipped. When the returned answer names the same
area, the earlier answer settles it as given, so the result says the area
wasn't checked and needs a person, and nobody is asked again. A moved or
larger area is asked about as before.

Tests: HouseScanKit cases for the three meanings, recording, same-area
settlement and changed areas; demo UI tests for reachability at default
and AX5 sizes, the acknowledgment and the result notice; and an engine
test on the synthetic replay (-autopilotCannotCheck) that checks the
withdrawn stretch in both scenes and that the notice survives a ground
refine without a second question.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@codemetrics-ai

codemetrics-ai Bot commented Oct 3, 2026

Copy link
Copy Markdown

Note

Review skipped

Codemetrics is installed on this repository, but it hasn't been added to your team yet.

To enable AI code reviews for this repo, go to your dashboard and add BaseScanning to your team's repositories.

@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d160a95d-08ae-4fbb-b967-6ba7c5ce7028
📥 Commits

Reviewing files that changed from the base of the PR and between 803535d and e2ef03f.

📒 Files selected for processing (17)
  • ios/HouseScan/Contract/ScanContract.swift
  • ios/HouseScan/Runtime/Autopilot.swift
  • ios/HouseScan/Runtime/LaunchOptions.swift
  • ios/HouseScan/Runtime/ResultClient.swift
  • ios/HouseScan/Runtime/ScanEngine+Confirm.swift
  • ios/HouseScan/Runtime/ScanEngine.swift
  • ios/HouseScan/UI/Copy/SpotCheckCopy.swift
  • ios/HouseScan/UI/Preview/DemoEngine.swift
  • ios/HouseScan/UI/Preview/UIDemo.swift
  • ios/HouseScan/UI/Screens/ResultScreen.swift
  • ios/HouseScan/UI/Screens/SpotConfirmScreen.swift
  • ios/HouseScanKit/Sources/HouseScanKit/Confirm/SpotConfirmation.swift
  • ios/HouseScanKit/Sources/HouseScanKit/Coverage/CoverageMap.swift
  • ios/HouseScanKit/Tests/HouseScanKitTests/SpotConfirmationTests.swift
  • ios/HouseScanUITests/SpotCannotCheckUITests.swift
  • ios/HouseScanUITests/SpotConfirmUITests.swift
  • ios/README.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The scan flow adds “I can’t check this area” as a spot-check answer. It withdraws coverage claims for the selected area, records it as unchecked, and can submit another upload. The result screen reports unchecked areas, including areas outside the result’s named spot.

Changes

Unchecked spot-check flow

Layer / File(s) Summary
Define and resolve spot-check answers
ios/HouseScan/Contract/ScanContract.swift, ios/HouseScanKit/Sources/HouseScanKit/Confirm/SpotConfirmation.swift, ios/HouseScanKit/Sources/HouseScanKit/Coverage/CoverageMap.swift, ios/HouseScanKit/Tests/HouseScanKitTests/SpotConfirmationTests.swift
Adds .cannotCheck and defines its claim-retention and guidance outcomes. Confirmation logic determines whether unchecked records are settled by the shown area. Tests cover answer mapping, area matching, and replacement order.
Process answers and update scan state
ios/HouseScan/Runtime/ScanEngine+Confirm.swift, ios/HouseScan/Runtime/ScanEngine.swift, ios/HouseScan/Runtime/Autopilot.swift, ios/HouseScan/Runtime/LaunchOptions.swift, ios/HouseScan/Runtime/ResultClient.swift, ios/README.md
The engine records the answer, withdraws claims for non-clear answers, and starts another upload. Autopilot selects a first answer, while debug launch options can supply a follow-up sample response. The engine derives unchecked-area state from confirmations and the shown area.
Present answers and unchecked-area notices
ios/HouseScan/UI/Copy/SpotCheckCopy.swift, ios/HouseScan/UI/Screens/SpotConfirmScreen.swift, ios/HouseScan/UI/Screens/ResultScreen.swift, ios/HouseScan/UI/Preview/DemoEngine.swift, ios/HouseScan/UI/Preview/UIDemo.swift, ios/HouseScanUITests/*Spot*UITests.swift
The spot-check screen offers three answers and uses a distinct icon for each. The result screen displays a notice for an unchecked spot or another unchecked area. Demo configuration and UI tests cover answer selection, notice behavior, readability, and coverage changes.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  actor Homeowner
  participant ScanEngine
  participant CoverageMap
  participant SampleResultClient
  Homeowner->>ScanEngine: Submit cannotCheck answer
  ScanEngine->>CoverageMap: Withdraw claims over the chosen area
  ScanEngine->>SampleResultClient: Submit follow-up upload
  SampleResultClient-->>ScanEngine: Return configured or bundled sample result
  ScanEngine-->>Homeowner: Show result with unchecked-area state
Loading

Suggested reviewers: aidenjohnston

Merge Risk: ⚪ Minimal · up to e2ef0

The new answer and unchecked-area notices are covered by the reported tests. The real-server moved-spot case remains untested, but no specific failure is established; merge after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to e2ef0

The new answer avoids treating an uninspected area as clear and preserves warnings when the result changes. No new security exposure was identified in the reviewed flow, but recovery after app termination and compatibility with the receiving service were not fully verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected answer path affects the current scan's coverage assertions, submitted scene, and displayed placement result. It does not expose an additional account, tenant, or privileged operation in that path; downstream service behavior was not independently assessed.

Trust Boundaries and Controls

  • observed — The homeowner-controlled answer is accepted only during the active unanswered spot check. Cannot-check cannot select the claim-preserving clear branch. The pending confirmation also records the scene and response hashes and the photo actually represented by the check.

Resilience and Maintainability Implications

  • observed — Ground-change recovery removes the displayed result and pending check, invalidates stale photo-loading work, and retains prior confirmations. Coverage withdrawal also survives map rebuilds. These inspected in-process controls preserve unchecked status; they do not establish restoration after process termination.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.79% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 56 functions across 16 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the main change: adding “I can't check this area” as a spot-check answer.
Description check ✅ Passed The description covers the change, rationale, UI behavior, verification, risks, and checklist. It notes that before-and-after images are not included because the new answer state had no prior version;…
Full details: Docstring Coverage

Explanation

Docstring coverage is 76.79% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 56 functions across 16 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · 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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-03T10:49:30.032992Z e2ef03f Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the size:L 100-499 effective changed lines. label Oct 3, 2026
@greptile-apps

greptile-apps Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds a third answer option to spot check confirmations.

The PR appears safe to merge; no outstanding findings or new actionable issues remain.

Summary

The PR adds “I can't check this area” as a third spot-check answer, withdraws claims for that area, and explains the unchecked area on the result. The latest change preserves that explanation when a later clear answer settles the displayed spot.

Reviews (6) · Last reviewed commit: "fix(ios): let only the spot's own not-ch..."

Comment thread ios/HouseScan/UI/Screens/ResultScreen.swift Outdated
SamGu-NRX and others added 2 commits October 3, 2026 04:18
The reachability and result-notice tests now use #204's whole-frame
scrolling instead of isHittable, so they scroll to the condition they
assert. A notice taller than the window is read top edge first, then
bottom edge, as a homeowner would scroll through it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@SamGu-NRX

Copy link
Copy Markdown
Owner Author

@codex review

After "I can't check this area" the area's claims stay withdrawn for the
scan, but the result only said so when the answer sent back named the
same spot. An answer with no spot, or a spot elsewhere, dropped the
context (Greptile on #206).

The scan's confirmation records are the one source:
SpotConfirmations.leftAreaUnchecked is true once any check was answered
"I can't check this area". ScanEngine republishes it to
ScanViewState.spotAreaUnchecked whenever spotConfirm changes, so
recording sets it and the existing resets (Start over, a new wall)
clear it. The result keeps the spot's own notice where it applies and
otherwise shows a wall-wide one: "You couldn't check an area along this
wall, ...", which names no spot and no obstruction.

Tests: a kit case for leftAreaUnchecked through later answers and a new
record; demo result without a spot at default and AX5; and two engine
runs that answer the re-upload without a spot
(-sampleResultAfterSpotAnswer, debug builds) after "I can't check this
area" and after "Something's there".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 892acdc3f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ios/HouseScan/UI/Screens/ResultScreen.swift Outdated
SamGu-NRX and others added 2 commits October 3, 2026 05:31
The engine set SampleResultClient.answerFile when a spot answer sent the
scan again and never cleared it, so after Start over or a new wall the
next scan's first upload could get the old file (review of 7071b95).
The engine now derives it from the spot records in spotConfirm's didSet,
beside spotAreaUnchecked: set while this scan has an answer other than
"It's clear", nil once the records reset. Debug-only test hook; release
builds never parse the option.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With "I can't check this area" answered about two different spots, the
second result showed only the notice about "this spot" and hid the
wall-wide one, so the first area went unmentioned (Greptile on #206).

The records now answer the question the result asks:
SpotConfirmations.leftUnchecked(besides:) is true when an area other
than the shown spot's was answered "I can't check this area" (any such
area with no spot shown). The engine keeps the shown spot's area in
SpotConfirmState.shownArea and republishes the answer as
ScanViewState.uncheckedAreaElsewhere from spotConfirm's didSet. The
result shows the wall-wide notice whenever that is true, worded "You
also couldn't check another area ..." beside a notice about the spot.

Tests: kit cases for areas held by, and apart from, the shown spot;
demo result with both notices; an engine run where the answer after
"I can't check this area" names another spot, answered clear.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@SamGu-NRX

Copy link
Copy Markdown
Owner Author

@codex review

…lier answer

leftUnchecked(besides:) left out every "I can't check this area" answer
whose area held the shown spot. After that answer, "It's clear" about a
larger area round the same footprint, and the original area shown again,
the clear answer settled it: the result had no notice about the spot,
and the earlier answer was left out by geometry alone, so nothing said
the area was unchecked though its claims stay withdrawn (review of
6cd1248). An answer about the shown spot is now left out only when the
answer that settles the spot is "I can't check this area", which is when
the result's own notice says it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@SamGu-NRX

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cd1248d1d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +238 to +239
records.contains { record in
record.answer == .cannotCheck && !(shown.map { record.area.holds($0) } ?? false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the unchecked notice after a wider clear answer

When area A is answered cannotCheck, a later, larger area B is answered clear, and a subsequent upload returns A, settling(A) selects the newer clear answer from B, but this predicate suppresses the original unchecked record merely because A contains the displayed spot. The result then has a clear spotCheck and uncheckedAreaElsewhere == false, so it shows no warning even though A's coverage claims remain withdrawn; exclude a record only when that specific cannotCheck record is the answer being shown.

AGENTS.md reference: AGENTS.md:L34-L34

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e2ef03f. The record is now left out only when it is what the shown spot's own notice stands for, which means the answer that settles the shown area is "I can't check this area" (leftUnchecked(besides:)). In your sequence (A cannotCheck, then a larger B clear, then A shown again), B's clear answer settles A, so A's earlier record still counts and the result keeps the warning. SpotConfirmationTests.aLaterClearAboutALargerAreaLeavesTheEarlierUncheckedOneToSay pins it.

@SamGu-NRX

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2ef03f797

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

public func leftUnchecked(besides shown: SpotArea?) -> Bool {
let spokenFor = shown.flatMap { area in settling(area)?.answer == .cannotCheck ? area : nil }
return records.contains { record in
record.answer == .cannotCheck && !(spokenFor.map { record.area.holds($0) } ?? false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid reporting a nested check as another area

When area A is answered cannotCheck, the server returns the same spot with a larger clearance area B, and B is also answered cannotCheck, the result's spot notice already describes B (and therefore A). However, A does not hold B, so this predicate also sets uncheckedAreaElsewhere and the UI incorrectly claims that the homeowner could not check “another area along this wall.” Treat earlier unchecked records contained by the currently shown unchecked area as spoken for as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. When A and then a larger area B around the same footprint are both answered "I can't check this area", the result shows B's own notice and also an extra "another area" notice for A, which B already covers. The claims over both stay withdrawn, the spot's own notice is right, and nothing passes that shouldn't. The only problem is a redundant line of wording. We're keeping e2ef03f as it is for this batch and listing this as a known limit in the description. I'm leaving the thread open for a person to resolve.

SamGu-NRX added a commit that referenced this pull request Oct 3, 2026
…branch: let the homeowner answer "I can't check this area" on the spot check

ResultClient.swift conflicted where #203 replaced UploadError.missingSample with MissingSampleResult and #206 added the answerFile early return; both are kept: the answer file first, then the bundled sample with #203's error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SamGu-NRX added a commit that referenced this pull request Oct 3, 2026
… the result card names the review line, draws no unsized zones, and promises a photo only when it offers one

project.pbxproj conflicted only in the generated fixture exception lists (#206's and #209's new result fixtures); it is regenerated with XcodeGen 2.46.0 from the merged tree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

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

Labels

size:L 100-499 effective changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant