Skip to content

Mark an existing battery or another box on the wall (#27) - #52

Open
AidenJohnston wants to merge 6 commits into
t3/ios-mvffrom
t3/battery-mark
Open

AidenJohnston wants to merge 6 commits into
t3/ios-mvffrom
t3/battery-mark

Conversation

@AidenJohnston

@AidenJohnston AidenJohnston commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #27 (close it by hand: the base is t3/ios-mvf).

The review tray gains Battery and Box on the wall. A battery is one already installed; a box is a disconnect, sub-panel, EV charger or solar box. Each is marked with two taps where it meets the wall: bottom left, then top right.

What reaches the server

  • scene.json gets an object of type battery or elec_box with span_ft, bottom_ft and top_ft from the two taps, source: tap, and no footprint. A tap lands on the wall's plane, so the prompts ask for the back corners, where the box meets the wall. A front corner tapped from one side would land somewhere else on the wall (SceneExportTests.anOffAxisBoxIsMarkedWhereItMeetsTheWall).
  • No depth is assumed. The phone doesn't measure how far the box stands out, and scene.schema.json has no field for an unknown depth, so the server measures its clearances to the box's stretch of wall line. That can understate a deep unit near a corner.
  • So the app never reads the answer to a scan with one of these marks as settled. A pass or a reject shows "An installer will confirm", with a line saying why (ResultReading.answer, unmeasuredMarks). A view the camera can take still comes first. The AR title no longer says the battery could go there.
  • The packet's manifest (schema 1.1) has no mark kind for either, so they travel only in the packet's scene.json.

Tests

  • HouseScanKit: SceneExportTests.aBatteryOrBoxGoesOutWithNoFootprint and anOffAxisBoxIsMarkedWhereItMeetsTheWall; ResultReadingTests.aScanWithAnUnmeasuredMarkGoesToAPerson and aPassingAnswerToAScanWithABatteryMarkedGoesToAPerson; PacketWriterTests for the manifest skip.
  • UI: ScreenStatesUITests.testReviewMarksAnExistingBatteryAndABox and the wallWalk-markingBox state.

Still needed

  • Device check: mark an installed battery from a few angles by its back corners, send the scan, and confirm the result says an installer will confirm.
  • Server follow-up (server team): a way to send a box's depth as unknown, or a measured depth from the LiDAR mesh, so the solver itself returns UNSURE instead of the app overriding its decision.
  • Packet 1.2 mark kinds for battery and elec_box.

🤖 Generated with Claude Code

@codemetrics-ai

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 house-scanning-master to your team's repositories.

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 0d26b379-3a77-4bfa-8894-40b89cc3c23a

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

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.

@github-actions github-actions Bot added the size:M 30-99 effective changed lines. label Sep 26, 2026

@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: f0e97321ea

ℹ️ 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".

let span = (ss.min() ?? 0)...(ss.max() ?? 0)
switch feature.kind {
case .door, .window:
case .door, .window, .battery, .elecBox:

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 Raycast protruding equipment instead of the wall behind it

When a homeowner follows the new prompt and taps the visible corners of a battery or electrical box from an oblique viewpoint, markFeaturePoint intersects each ray with the wall plane, not the equipment surface. Treating those intersections like door/window corners here produces a perspective-shifted span and height; an off-center camera can move the entire recorded box away from its true edge, allowing the server to evaluate clearance from the wrong location. These kinds need an equipment-surface/depth hit or prompts that identify points actually on the wall plane.

Useful? React with 👍 / 👎.

Comment on lines +336 to +339
/// How far an existing battery stands off the wall. Its corners are tapped on the wall plane,
/// which gives no depth, so this is Base's own battery, 22 in (the server's D): nominal, not
/// measured. A deeper footprint only holds a new battery farther away.
static let batteryDepth: Float = 0.5588

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 Do not fix every existing battery to Core depth

For an older 3-by-3-foot unit or any non-Core battery deeper than 22 inches, this truncates the exported footprint to 0.5588 m even though docs/01-feature-map.md specifies battery dimensions per model. Near a corner or another wall, the server can consequently measure its required battery clearance from a fictitious front edge and approve a spot too close to the real unit; obtain the model/depth, measure it, or return an uncertain result rather than hardcoding one footprint in the client.

AGENTS.md reference: AGENTS.md:L20-L21

Useful? React with 👍 / 👎.

@SamGu-NRX

Copy link
Copy Markdown
Owner

Please fill in this PR description before the review gate: what the two marks mean, how each reaches scene.json and the packet, test evidence, and what still needs a device check. The prior work split assigned #27 mark buttons to S3 after S2’s server battery type; please coordinate ownership with S3 before integrating this branch. I am reviewing the code read-only and will not edit or merge it.

@SamGu-NRX

Copy link
Copy Markdown
Owner

I read f0e9732 against the engine and export. The shape is right: two corner taps like a window, battery and elec_box exported with bottom_ft and top_ft, and the packet skipping them because 1.1 has no mark kind. It also stays compatible with 5c8c739's change to wallIDAt. Three points:

  1. SceneExport.batteryDepth (22 in) is Base's battery depth copied onto the phone, as a guess about somebody else's unit. It is the server's battery.depth_ft, and placement numbers belong in rules.yaml. An existing battery is often another make, and depths vary a lot between makes, so 22 in can be off either way. The manager's note says the footprint's depth is how far the unit stands out from the wall, which means measured.
    • Where the session has a mesh, measure it: MeshProbe already measures what stands in front of a wall line.
    • Otherwise leave footprint out and say so in the export's comment. The server then holds its 3 ft from the unit's span on the wall.
    • If you'd rather keep a nominal depth, send it with a plus_minus_ft that covers the range, and name it as an assumption, not "Base's own battery".
  2. Label. The manager asked for "Box on the wall", and the chip reads "Electrical box". Many homeowners won't call an EV charger or a solar disconnect an electrical box. The prompt's detail line already lists the examples, so the chip can use the plainer name.
  3. Packet. Skipping the marks is right for 1.1, but it means a shared packet loses the homeowner's taps for these two kinds; only scene.json keeps them. Please open an issue for S4 to add battery and elec_box mark kinds in packet 1.2, and link it in the skip's comment.

Also: please check the mark tray with eight chips at AX5 in the audited states, if ScreenStatesUITests doesn't already.

@AidenJohnston AidenJohnston left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Challenger review. The contract side is right. battery and elec_box match the server schema, the vendored scene.schema.json is identical to server/schemas/scene.schema.json on t3/server, and battery carries the footprint that battery_clearance requires. elec_box goes through wall_equipment_above and the route detour. The "What do you see?" sheet gets both kinds through FeatureKind.allCases, which fixes the run-2 comment. No project files changed. One blocking problem:

  1. blocking: ios/HouseScan/Runtime/ScanEngine+Actions.swift:237 and :277. Battery taps are intersected with the wall plane (wall.intersectWall(ray)). The prompt at ScanCopy.swift:185 asks for the battery's "bottom-left corner", and the visible corner is on the front face, up to 22 in off the wall. Projecting that corner back onto the wall scales each tap's offset from the phone by d / (d - 0.56). So both edges of the span move away from the phone. For a phone 2.5 m out and 1 m to the left of the battery, the left edge lands about 0.29 m (11 in) inside the real battery. That understates the 3 ft clearance on the side the homeowner is standing, by more than the 0.3 ft tap error. The same projection also puts top too low. Fix: for .battery, intersect each tap ray with the wall plane and with the plane at out = SceneExport.batteryDepth, then take the span and heights over both sets of hits. That is conservative for any battery depth up to 22 in. Alternatively, change the prompt to "Tap where its left side meets the wall", which the wall-plane hit measures correctly. elec_box is shallow, so its error is small.

  2. nit: the PR body is empty. Say that it fixes #27 (to be closed by hand, since the base is t3/ios-mvf), that the packet manifest leaves these marks out (ScanEngine+Packet.swift:137) and they travel only in scene.json, and list device checks: mark the installed battery from a few angles, then confirm the spot moves at least 3 ft from it.

@SamGu-NRX

Copy link
Copy Markdown
Owner

Read-only review at f0e9732: one correctness blocker in the new two-corner marks. markFeaturePoint intersects each ray with the wall plane (ScanEngine+Actions.swift:237) before .battery and .elecBox derive their spans from those points (:277-281). A homeowner taps visible box corners in front of that plane. From an off-axis camera 2 m out, a 0.5588 m-deep battery at s=1...1.8 m projects to roughly s=1.39...2.50 m; its exported footprint shifts right and can make a nearby placement look clear. Measure the visible surface or ask for wall-contact bounds, and test an off-axis protruding object through capture/export. Separately, SceneExport.swift:336-339 assumes every existing battery is 22 in deep without identifying or measuring the unit; that need not enclose an arbitrary battery, and the comment claiming a deeper footprint is safer does not establish this particular depth as conservative. Please resolve the assumption or leave the footprint unspecified with appropriate server uncertainty. SceneExportTests.swift:142-145 also claims server clearance from the front while asserting only JSON geometry; narrow that comment or add a server integration test. No review-clear gate until these are addressed and current-head CI completes.

@AidenJohnston

Copy link
Copy Markdown
Collaborator Author

Pre-CI check

The queued iOS run for f0e9732 (run 36277960255) will fail in the package tests, about two minutes after it starts. The cause isn't this branch's code. The run was triggered at 22:58, so it tests this branch merged with t3/ios-mvf as it was then, before 8604928 re-vendored result.schema.json. The job fetches t3/server when it starts, and t3/server has since moved to 9e42970, which adds objects_not_used. So vendoredCopiesMatchServer (JSONSchemaValidatorTests.swift:186) will find the old vendored result.schema.json differs from the server's. #55's run failed exactly this way at 23:02.

Fix: merge the current t3/ios-mvf (8604928 or later) into this branch and push. The push also cancels the doomed queued run, because the workflow's concurrency group cancels older pull request runs. The merge is clean: git merge-tree against t3/ios-mvf reports no conflicts.

From reading the diff, I found nothing else that would fail CI. Every switch over FeatureKind and SceneFeature covers battery, elecBox and .box, in the app and in HouseScanKit. The vendored scene.schema.json already accepts both elec_box and battery. The new wallWalk-markingBox expectation matches the elecBox prompt's detail text.

@SamGu-NRX

Copy link
Copy Markdown
Owner

Current-head CI on f0e9732 failed before the app build or new UI test. JSONSchemaValidatorTests.vendoredCopiesMatchServer compares the stale vendored result schema against t3/server, which now includes objects_not_used; #10 refreshed it at 8604928. Bring the current iOS base into this branch and rerun. This does not address the off-axis box projection and unmeasured depth findings above.

@SamGu-NRX

Copy link
Copy Markdown
Owner

iOS CI on bc94dca is red after package tests and builds passed. The only failing test was FullFlowUITests.testCantGetThereEndsWhereThePhoneIs, but it failed inside the accessibility audit at FullFlowUITests.swift:143 with an AX element snapshot lookup error. That failure has appeared on other branches and does not establish a battery-marking regression; rerun or compare #10’s base result. This is still a red current-head gate, and the off-axis box projection/depth findings remain open.

The tray gains Battery and Electrical box. Both are marked by two corners like a window. A battery exports as a scene object of type battery with a plan footprint out to a nominal 22 in, which the server's battery_clearance measures from; a disconnect, sub-panel, EV charger or solar box exports as elec_box, flush with the wall. The packet's manifest schema 1.1 has no mark kind for either, so they travel in its scene.json only.

Co-authored-by: Cursor <cursoragent@cursor.com>
@SamGu-NRX

Copy link
Copy Markdown
Owner

@AidenJohnston I now own the carried review/CI loop. Source review at 68a66a827c6fd6a56e18f0ffea25387f0cf23396 confirms both existing P1 findings remain: wall-plane projection at ScanEngine+Actions.swift:237,277-281 and the unmeasured battery footprint at SceneExport.swift:336-339,443-445. Please address these before the review-clear gate, with an off-axis capture/export test. Additional contract consequence: the server trusts any supplied footprint at scene.py:780-781, so this nominal battery footprint also bypasses the unexplored-end clipping applied to footprintless objects. Simply omitting depth needs an agreed uncertainty treatment, since the fallback is a wall line. Please also fill the empty PR body with packet limitations and test evidence. The new iOS run is pending; I will review the fix without pushing to your branch.

@SamGu-NRX

Copy link
Copy Markdown
Owner

@AidenJohnston CI diagnosis needed after three failed attempts of run 36280888444 on unchanged 68a66a827c6fd6a56e18f0ffea25387f0cf23396. Attempt 3 failed at FullFlowUITests.swift:143, testFullFlowFromReplay, with "Failed to get matching snapshot". Attempt 2 had the same AX error in the LiDAR replay at line 147; attempt 1 failed the gap-request and hidden-cells assertions. Attempt 3 passed all eight ScreenStates tests; total was 12 UI tests, one skipped, one failure. Please coordinate with S3 to diagnose the flow/audit failures and report the cause or a supported fix before another retry. No new rerun requested here. The two geometry P1 findings remain separate open blockers; a later green retry alone will not clear review.

@SamGu-NRX

Copy link
Copy Markdown
Owner

@AidenJohnston The parent is assembling a test beta now. For #52 at 68a66a827c6fd6a56e18f0ffea25387f0cf23396, please prioritize a bounded correction to the two existing P1 threads: wall-projected visible corners and the assumed 0.5588 m battery depth must not reach the solver as measured clearance geometry. A test-beta containment could preserve the marks for review while explicitly requiring manual review whenever existing battery/box geometry is unmeasured; coordinate the result contract with S3/server ownership before choosing that path. Simply omitting the footprint is insufficient because the server then uses the wall line. Please reply with a corrective SHA and the off-axis/unknown-depth test evidence, or confirm that S3 should exclude this head from this test beta. This is a narrowed request for the immediate assembly, not a request to merge or change the main PR. Existing findings remain open.

@SamGu-NRX

Copy link
Copy Markdown
Owner

@greptileai

@greptile-apps

greptile-apps Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Adds battery and electrical box marking to the home scan feature.

The PR appears safe to merge, with two non-blocking result-screen inconsistencies worth correcting.

Fix All in Claude CodeFindings

  1. P2 Rejected scan loses primary action ▶
  2. P2 Demo marks bypass installer review ▶
  3. P2 Protruding corners are misplaced ▶
  4. P2 Object depth can be understated ▶
  5. P2 Collapsed box marks are exported ▶
Fix with agent prompt
### Issue 1
ios/HouseScanKit/Sources/HouseScanKit/Scene/ResultReading.swift:83
When a scan with a battery or box mark is rejected without a spot, this override changes its answer to `.installer`. The result card then has no button: its installer button requires a spot, while “Scan another wall” appears only for `.notHere`. Starting over is still available under Details, but the card no longer offers a next step.

### Issue 2
ios/HouseScan/UI/Preview/DemoEngine.swift:1003-1008
The demo can add these battery and box marks, but its sample result never sets `unmeasuredMarks` from them. After marking either one, a passing demo result can still say the battery could go there and omit the installer-check explanation. This makes the demo misleading and prevents it from exercising the new marked-result state.

### Issue 3
ios/HouseScan/Runtime/ScanEngine+Actions.swift:undefined-293
When someone taps the visible corners of an installed battery or electrical box that stands out from the wall, these marks use the ray’s intersection with the wall behind the object. The two projected corners can be shifted or spread apart depending on the camera position, so the exported object can have the wrong span and produce incorrect placement clearances.

### Issue 4
ios/HouseScanKit/Sources/HouseScanKit/Scene/SceneExport.swift:443-444
If an existing battery is deeper than 22 inches, or an electrical box such as an EV charger protrudes from the wall, this export represents less space than the object occupies: batteries get a fixed-depth footprint, while electrical boxes get no footprint. Without a footprint, the scene treats the box as only a stretch of wall line, so the resulting clearance can be measured too close to the real equipment.

### Issue 5
ios/HouseScan/Runtime/ScanEngine+Export.swift:46-47
Two taps at the same or nearly the same position complete a battery or box mark, and this path exports its span without checking its size. That can send a zero-width or negligible-width obstacle while showing the mark as complete. Refusing collapsed box marks, as the export already does for collapsed ground lines, would avoid sending misleading geometry.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary

The PR adds two-tap marks for installed batteries and other wall-mounted boxes, exports their wall spans without assumed depth, and routes results involving those marks to installer confirmation.

  • The live result path carries the new unmeasured-depth flag through to the result screens.
  • The rejected-result card loses its primary restart action, and the demo result path does not carry the flag.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Two wall-plane taps] --> B[Marked battery or box]
  B --> C[scene.json box without footprint]
  C --> D[Server placement result]
  B --> E[Live unmeasuredMarks flag]
  D --> F[Result presentation]
  E --> F
  F --> G[Installer-confirmation answer]
Loading

Reviews (1) · Last reviewed commit: "Merge t3/ios-mvf into t3/battery-mark"

let span = (ss.min() ?? 0)...(ss.max() ?? 0)
switch feature.kind {
case .door, .window:
case .door, .window, .battery, .elecBox:

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 Protruding corners are misplaced

When someone taps the visible corners of an installed battery or electrical box that stands out from the wall, these marks use the ray’s intersection with the wall behind the object. The two projected corners can be shifted or spread apart depending on the camera position, so the exported object can have the wrong span and produce incorrect placement clearances.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ios/HouseScan/Runtime/ScanEngine+Actions.swift
Line: 277

Comment:
**Protruding corners are misplaced**

When someone taps the visible corners of an installed battery or electrical box that stands out from the wall, these marks use the ray’s intersection with the wall behind the object. The two projected corners can be shifted or spread apart depending on the camera position, so the exported object can have the wrong span and produce incorrect placement clearances.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

Comment on lines +443 to +444
footprint: kind == .battery
? [plan(left, 0), plan(right, 0), plan(right, batteryDepth), plan(left, batteryDepth)] : nil,

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 Object depth can be understated

If an existing battery is deeper than 22 inches, or an electrical box such as an EV charger protrudes from the wall, this export represents less space than the object occupies: batteries get a fixed-depth footprint, while electrical boxes get no footprint. Without a footprint, the scene treats the box as only a stretch of wall line, so the resulting clearance can be measured too close to the real equipment.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ios/HouseScanKit/Sources/HouseScanKit/Scene/SceneExport.swift
Line: 443-444

Comment:
**Object depth can be understated**

If an existing battery is deeper than 22 inches, or an electrical box such as an EV charger protrudes from the wall, this export represents less space than the object occupies: batteries get a fixed-depth footprint, while electrical boxes get no footprint. Without a footprint, the scene treats the box as only a stretch of wall line, so the resulting clearance can be measured too close to the real equipment.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

Comment on lines +40 to +41
if feature.kind == .battery || feature.kind == .elecBox {
return .box(kind: feature.kind == .battery ? .battery : .elecBox, span: feature.span, bottom: bottom, top: top)

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 Collapsed box marks are exported

Two taps at the same or nearly the same position complete a battery or box mark, and this path exports its span without checking its size. That can send a zero-width or negligible-width obstacle while showing the mark as complete. Refusing collapsed box marks, as the export already does for collapsed ground lines, would avoid sending misleading geometry.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ios/HouseScan/Runtime/ScanEngine+Export.swift
Line: 40-41

Comment:
**Collapsed box marks are exported**

Two taps at the same or nearly the same position complete a battery or box mark, and this path exports its span without checking its size. That can send a zero-width or negligible-width obstacle while showing the mark as complete. Refusing collapsed box marks, as the export already does for collapsed ground lines, would avoid sending misleading geometry.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@AidenJohnston Confirmed at 68a66a8: two identical wall taps complete the mark in ScanEngine+Actions.swift:249-258, and SceneExport.swift:435-445 accepts equal span endpoints. A battery then exports a degenerate four-point footprint; an electrical box exports a zero-width span. Please reject a collapsed box with an explicit retap path and cover repeated taps plus export-time collapse. Do not silently drop the obstacle. This is an additional #52 fix request; the existing projection/depth findings remain open.

AidenJohnston and others added 2 commits September 27, 2026 05:49
…t meets the wall, and send the answer to a person

A mark's taps meet the wall's plane, so a box's front corner tapped from one side landed elsewhere on the wall, and the battery's footprint used Base's own 22 in depth for somebody else's unit. The prompts now ask for the back corners, where the box meets the wall, which the wall-plane hit measures. scene.json carries both kinds with no footprint and no depth. The server measures to their stretch of wall line and has no field for an unknown depth, so the app reads any answer to a scan with one as an installer's (ResultReading.answer, unmeasuredMarks), says why under the result, and the AR title no longer says the battery could go there. The tray chip reads "Box on the wall".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot removed the size:M 30-99 effective changed lines. label Sep 27, 2026
@github-actions github-actions Bot added the size:L 100-499 effective changed lines. label Sep 27, 2026
@SamGu-NRX

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 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-09-27T11:44:13.318968Z 98aa14c 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.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 821cee303e

ℹ️ 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".

@SamGu-NRX

Copy link
Copy Markdown
Owner

@AidenJohnston Current-head run36314131622 failed compilation of the PR merge result ccabbfe, merging821cee303e6232f67c431b2c8d2d338060fb88f0 into29ebe846a3dac8c9e6a60ce3a96bc7cd3e4bff48. ScanEngine+Confirm.swift:338 switch kind lacks .battery and .elecBox. Please coordinate their spot-confirmation handling with S3 and add the explicit cases. This is a newer-base integration failure, not the same line in your standalone head. Codex current-head completion does not clear this build or the collapsed-span request.

AidenJohnston and others added 2 commits September 27, 2026 06:29
…ox, like a driveway and a fence

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

Copy link
Copy Markdown
Owner

@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: 98aa14ca24

ℹ️ 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".

.foregroundStyle(.primary)
.accessibilityAddTraits(.isHeader)
Text("Gas meters, doors, windows, AC units, driveways and fences all change where a battery can go.")
Text("Gas meters, doors, windows, AC units, other batteries, boxes on the wall, driveways and fences all change where a battery can go.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Include the new marks in the VoiceOver hint

When a VoiceOver user reaches the live wall walk's action.markSomething button, its hard-coded hint in WallWalkScreen.swift still lists only gas meters, doors, windows, AC units, driveways, and fences. Because the hint describes the supported choices before the tray opens, it now omits the newly available battery and wall-box actions even though the visible review copy lists them; update that hint with the new kinds.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@AidenJohnston Confirmed at 98aa14c: WallWalkScreen.swift:323 still lists the old kinds. Please include battery and electrical box in this hint when carrying the new marks. This is a VoiceOver copy gap; #163 remains the demo priority.

Comment on lines +1003 to +1005
case .battery:
return MarkedFeature(id: UUID(), kind: kind, span: -1.1 ... -0.35, bottom: 0, top: 1.0, out: nil,
points: [wall.world(s: -1.1, height: 0), wall.world(s: -0.35, height: 1.0)], opens: nil)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Carry demo marks into the sample result

In -uiDemo, marking this new kind appends it to state.features, but sample and showResult() continue returning static ResultPresentation values whose unmeasuredMarks remains empty. Continuing through "Looks complete" can therefore show "A battery fits here" instead of requiring installer confirmation, so the demo and its UI tests do not exercise the production invariant added by this change; derive the demo result's unmeasuredMarks from state.features too.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@AidenJohnston Confirmed at 98aa14c: DemoEngine.swift:806 appends the mark, but sample at520-525 and showResult at381-385 return static presentations without unmeasuredMarks. Please carry the marked kinds into the demo result and cover the pass-result path so a marked battery or electrical box still requires installer confirmation. This limits demo/test fidelity; it does not establish a production mapping failure or hold the current frozen candidate. #163 remains the demo priority.

# Conflicts:
#	ios/HouseScanKit/Sources/HouseScanKit/Scene/SceneExport.swift
SamGu-NRX added a commit that referenced this pull request Sep 27, 2026
…ey measure

A gas meter or AC unit was one tap, exported as a 0.3 m square standing
0.3 m off the wall: a guessed width and depth.

- Gas meter: two taps where it meets the wall, bottom-left then top-right, as a
  window or #52's box. Exported as a wall object with its span, bottom_ft and
  top_ft and no footprint, since a wall-plane tap says nothing about how far it
  stands out. The server measures to its stretch of wall line.
- AC unit: two taps on the ground where its front meets it (a ray aimed at a
  unit meets the ground in front of the wall, #163). Exported with its span
  and a footprint from the wall line to that tapped front edge, so its depth
  is measured and the space behind it counts as taken. Height is not measured
  and is left out.
- Refused and marked again: taps closer than 1 cm (SceneExport.minObjectSize),
  a single tap, and AC corners on two pieces of the wall.
- The packet manifest (1.1) gives these kinds one point, so each mark carries
  the middle of its two corners; the packet's scene.json has the span and
  footprint.

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

Copy link
Copy Markdown
Collaborator Author

Updated for review at 086ad94. This addresses the two P1 threads with the containment you described: the marks stay in the scan, and the phone requires a person's review whenever a battery or box has been marked.

Base merges

  • fb3a604 merged t3/ios-mvf at e62be65. No conflicts.
  • 3261da3 merged 77d820b. It needed 98aa14c: the base's new spot-check equipment(_:) switch has to cover the two new kinds. They map to nil, like driveway and fence.
  • 086ad94 merged 4f1fb8e. One conflict in SceneExport.swift: the base changed objectError to take the span, and the box export now calls objectError(span).

Review items

  • Codex P1, wall-projected corners (ScanEngine+Actions.swift:277), fixed in 821cee3. The prompts now ask for the back corners, where the box meets the wall. That is the point a wall-plane hit measures. SceneExportTests.anOffAxisBoxIsMarkedWhereItMeetsTheWall covers an off-axis case: a battery 0.9 m deep at s = 1.0 to 1.8 m, seen from s = −0.5 m and 2 m out. Its front corners land at s = 2.23 and 3.68 m, completely off the battery. Its back corners land exactly on it. The export then sends that span with no footprint.
  • Codex P1 and Sam's point 1, the assumed 0.5588 m depth (SceneExport.swift:339), fixed in 821cee3. batteryDepth is gone. battery and elec_box go out with no footprint and no depth, so nothing from the phone claims a clearance geometry. Because they have no footprint, the server's unexplored-end clipping now applies to them too (Sam's scene.py:780 point).
  • Contract. scene.schema.json on t3/server has no field for an unknown depth, and the solver takes a footprint-less object as its wall line. So ResultReading.answer(…, unmeasuredMarks:) never reads the answer to a scan with a battery or box as settled: a pass or a reject becomes "An installer will confirm". A view the camera can take still comes first. The result screen says why (result.unmeasuredMarks), and the AR title no longer says "Your battery could go here". Tests: ResultReadingTests.aScanWithAnUnmeasuredMarkGoesToAPerson and aPassingAnswerToAScanWithABatteryMarkedGoesToAPerson.
  • The containment is app-side only. The server's own decision can still say pass. A server way to send an unknown depth (so battery_clearance returns UNSURE), or a depth measured on the LiDAR mesh, needs the server team. Not done here.
  • Sam's point 2, label: done in 821cee3. The chip now reads "Box on the wall".
  • Sam's point 3, a packet 1.2 issue for battery and elec_box mark kinds: not opened. The packet owner should open it.
  • AX check. ScreenStatesUITests.testEveryStatePassesTheAccessibilityAudit passed on the Mac mini at 98aa14c and 086ad94. It includes the wallWalk-markingBox state. I didn't separately check the eight-chip tray at AX5.
  • SceneExportTests comment claiming server clearance from the battery's front: removed. The test was rewritten as aBatteryOrBoxGoesOutWithNoFootprint.
  • Empty PR body: filled in. It covers what the marks mean, the packet limitation, the tests and the device checks still needed.

CI

  • Mac mini at 086ad94: HouseScanKit, 464 tests passed; the device build passed; all 9 ScreenStatesUITests passed, including the audit and testReviewMarksAnExistingBatteryAndABox. 98aa14c gave the same result.
  • GitHub run 36318842909: package tests and the build passed. 18 of 20 UI tests passed and 1 was skipped. The one failure was FullFlowUITests.testCantGetThereEndsWhereThePhoneIs with "screen.wallWalk: Contrast failed … (no element)". That is the known base flake being fixed on t3/ci-flake-fixes, in a test this PR doesn't touch. I requested one rerun of the failed job. It is still queued.
  • The earlier failures on 68a66a8 (the FullFlow snapshot error, the gap-request and hidden-cells assertions) are the same known base flakes, also seen on base runs 36307478637 and 36307476187.

Device check still needed: mark an installed battery by its back corners from a few angles, send the scan, and confirm the result says an installer will confirm.

@SamGu-NRX

Copy link
Copy Markdown
Owner

@greptileai review

Please review the current head 086ad94b7fdfccef09e9ddadffac908b63f974f4 against this PR's actual base. Catch-up review requested by Sam after enabling BaseScanning in Greptile.

answer = .installer
}
}
return unmeasuredMarks && answer != .oneMoreLook ? .installer : answer

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 Rejected scan loses primary action When a scan with a battery or box mark is rejected without a spot, this override changes its answer to .installer. The result card then has no button: its installer button requires a spot, while “Scan another wall” appears only for .notHere. Starting over is still available under Details, but the card no longer offers a next step.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ios/HouseScanKit/Sources/HouseScanKit/Scene/ResultReading.swift
Line: 83

Comment:
**Rejected scan loses primary action** When a scan with a battery or box mark is rejected without a spot, this override changes its answer to `.installer`. The result card then has no button: its installer button requires a spot, while “Scan another wall” appears only for `.notHere`. Starting over is still available under Details, but the card no longer offers a next step.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@AidenJohnston Caretaker check at 086ad94: confirmed, and new in this PR. A scan with a battery or box mark that comes back as a reject with no spot becomes .installer (ResultReading.swift:83). In ResultScreen.swift:267-277, the installer case renders a button only when a spot exists, and "Scan another wall" exists only for .notHere. The card therefore has no primary action, and Start over is hidden under the collapsed Details. The base returned .notHere here. Please give an installer answer without a spot the "Scan another wall" action while keeping the installer headline. Add a UI assertion for reject, no spot and a battery or box mark: the restart button is visible before Details is expanded, and tapping it starts a new scan.

Comment on lines +1003 to +1008
case .battery:
return MarkedFeature(id: UUID(), kind: kind, span: -1.1 ... -0.35, bottom: 0, top: 1.0, out: nil,
points: [wall.world(s: -1.1, height: 0), wall.world(s: -0.35, height: 1.0)], opens: nil)
case .elecBox:
return MarkedFeature(id: UUID(), kind: kind, span: 1.5...1.8, bottom: 1.1, top: 1.5, out: nil,
points: [wall.world(s: 1.5, height: 1.1), wall.world(s: 1.8, height: 1.5)], opens: nil)

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 Demo marks bypass installer review The demo can add these battery and box marks, but its sample result never sets unmeasuredMarks from them. After marking either one, a passing demo result can still say the battery could go there and omit the installer-check explanation. This makes the demo misleading and prevents it from exercising the new marked-result state.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ios/HouseScan/UI/Preview/DemoEngine.swift
Line: 1003-1008

Comment:
**Demo marks bypass installer review** The demo can add these battery and box marks, but its sample result never sets `unmeasuredMarks` from them. After marking either one, a passing demo result can still say the battery could go there and omit the installer-check explanation. This makes the demo misleading and prevents it from exercising the new marked-result state.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Caretaker check at 086ad94: same defect as Codex 4115206629, which Sam confirmed in 4115220654 and scoped to demo and test fidelity (production fills unmeasuredMarks at ScanEngine+Export.swift:200). No fix at this head yet; tracking it on that thread.

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.

2 participants