Repository navigation
ios: review follow-ups for #132 (AR double draw, result chevron, aim legend contrast) - #137
Conversation
…out the battery in view Review of #132 (Part of #67): once the AR scene let go of the result, it had to be seen drawing the battery again before the Canvas stepped aside. With the phone on the meter and the battery out of view, a model rebuilt for a wall that moved more than 5 cm, or a moment of lost tracking, brought back the second, unoccluded battery and cable for as long as the phone stayed there. The policy now remembers the first take-over and takes the result back as soon as the AR scene holds it again. While tracking is limited the disabled model still counts as held, since the screen draws neither layer then, so both come back together. Test: afterATakeOverHoldingAgainTakesOverWithoutTheBatteryInView. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…one button Review of #100 (S2) and #132: the chevron hid whenever the spot projected anywhere inside a 24 pt inset of the screen, so a battery under the instruction card or the Done button got neither a chevron nor a view of itself. In view now means in the band the chrome leaves clear. The chevron's words sit on the card's solid scrim, as the aim legend's do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The every-state accessibility audit failed on beta (run 36302099002): wallWalk-aim, "Contrast failed" on aim.legend. Its see-through black let the demo's bright siding show through. It now uses the instruction card's scrim and text colour, which pass the audit on every screen. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.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: trueThanks 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 |
AidenJohnston
left a comment
There was a problem hiding this comment.
Adversarial review of #137 at 5c83ad4. #137 and #117 were both marked merged at 08:51 today. beta (6bc1df0) already contains 5c83ad4 through 50a2953 and #117's 8b42e13 through 7724c42, so everything below is a follow-up for beta, not a gate on this branch. I read the diff, the files it touches on the branch and on beta, #132's review and #67.
Must fix
None.
Should fix
ResultARScreen.swiftchevronPlacement: the 260 pt / 160 pt clear band is only right at the default text size.betanow has the measured edges, so use them.- Measured in full-screen points (
SpotDirectionignores the safe area):- Card bottom, Dynamic Island phones: top inset about 59, plus 4 pt padding, a 36 pt status row and a 10 pt gap, plus the card. That comes to about 215–235 pt with a two-line detail.
- A three-line detail reaches about 250–265 pt on a 375 pt-wide notched phone (13 mini, top inset 50). The sample-spot text ("No server checked this scan. …") is the likeliest to wrap to three lines.
- Done button top: about 98 pt up on home-indicator phones and about 64 pt on an SE, so 160 pt leaves 60 pt or more to spare there.
- Large text breaks the 260 pt top. From xxxLarge up, the card passes 260 pt. At AX sizes it can reach 400–700 pt, or fill the screen and scroll. Two failures follow:
- A spot under the enlarged card still counts as "in view", so no chevron shows. This is the #100 S2 bug again, for larger text.
- The chevron's lane also starts at 260 pt, and
SpotDirectionsits underCameraChromein the ZStack. When the chevron does show, the card covers both the arrow and its words.
- This is the problem #117 solved for the aim legend.
CameraChrome(openArea:)reports the card's real bottom and the controls' real top. - Suggested fix:
- Give
ResultARScreenan@State var openArea: OpenCameraArea?and passopenArea: $openAreatoCameraChrome. - Build both
clearandlanefrom it, converting from global coordinates asWayfindingOverlay.legendViewdoes, and keep 260/160 and 260/300 as the fallback when it's nil. - When
top >= bottom(the chrome fills the screen), no chevron can be seen. Put "Your battery spot is off screen" into the card'slegend:line instead, as the aim legend's.underCarddoes.
- Give
- Measured in full-screen points (
Notes
- Can (1) fail the other way (#67)? No. I walked the state machine (
confirmed,usesRealityKit,drawnSince; in the enginedrawnimpliesheld):- Before the first take-over:
heldalone never takes over (the first branch needsusesRealityKit || confirmed). The Canvas shows untildrawnhas held for 0.5 s, so theheldButNeverDrawnKeepsTheCanvasguarantee is intact. - After a take-over, any break in the chain gives the Canvas back on the next look (≤100 ms):
- the anchor entity unanchors, so
anchoredis false; - the result is removed (
resultIsDrawnreturns false for all three); - the model is disabled while tracking is
.normal. - I checked two places where this could fail. The rebuild path (
showResultInCamera(rising: false)) keeps the same watcher, soconfirmedsurvives a rebuild as intended.ingestsetsstate.trackingand callssetResultVisiblein the same main-actor turn, so the watcher never sees "normal, but disabled".
- the anchor entity unanchors, so
- Long limited tracking: the new
anchored && tracking != .normalterm keepsresultInCameratrue.ResultARScreendraws neither layer then and shows "Point at your meter", as it did before, so this is no worse. - What
confirmedgives up: after a break, the result goes back to RealityKit without the re-check thatdrawnused to give. That check was only the same chain plus "the focus projects on screen", and a rebuild uses the same pipeline that was already confirmed on this phone. The cost is small. - The failure no version can see, "chain OK but nothing rendered", is unchanged.
- Non-LiDAR phones never reach this path (
showResultInCameraguards onsupportsMesh). They always get the Canvas, which was #67's case.
- Before the first take-over:
- Cheap hardening, repeated from #132's review:
heldandanchoredstill ignoreinSession. Withconfirmedsticky, anAnchorEntitythat staysisAnchoredafter ARKit drops or replaces the meter anchor would now retake at once, with nodrawncheck.anchored && inSessioninLiveCapture.resultIsDrawncosts nothing. - Flicker at the band edges: the in-view test has no hysteresis. A spot resting on the card's bottom edge, which is where it sits when the phone tilts up toward the meter, will toggle the chevron and its words with hand jitter. #117 added a 40 pt switch margin to the legend for the same reason, and a similar margin here would stop the blink.
- Rebuild flash: as the Risk section says, a rebuilt model's new anchor can be unanchored for one look, which shows the Canvas copy alone for about 100 ms. That's acceptable, and it's one battery, not two.
- Test gaps:
- No UI or unit test asserts
ar.spotDirectioneither way. The claim that "the demoresultARstate is unchanged" rests on where the demo spot happens to project. - Because the chevron never shows in any audited state, the audit has never checked its words' new capsule scrim.
- Moving
chevronPlacementinto a pure function (it already takes a size and a projection) would make both testable. afterATakeOverHoldingAgainTakesOverWithoutTheBatteryInViewcovers the policy. The engine's tracking term has no test.
- No UI or unit test asserts
- Merge with #117 (the contrast fix): 7724c42 resolved it correctly.
- #117's
legendText, inside theViewThatFitsand measured-openArealayout, now carries this PR'sPalette.chalkonScrimShape.rounded(14). - #117's under-card legend (
CameraChrome.legendLine) already used the same scrim and chalk, so bothaim.legendpresentations share one surface. - They still differ in type: subheadline semibold, centred, beside the ring;
Typeface.hintwith a scope icon under the card. That's cosmetic. ViewThatFitssizing is unaffected, because the padding didn't change.- The solid box is heavier over the wall next to the ring than 70% black was, which is the price of passing the audit.
- The
t3/41-target-ringhead still has the old 70% black. Don't re-merge that branch overbeta.
- #117's
- Verification on the combined head: the Mac mini PASS was on
5c83ad4alone, before #117's layout and the practice-meter work joined it onbeta. GitHub's iOS job was skipped on this draft. The first every-state audit of the combination is iOS run 36306893657 on6bc1df0, still in progress. Check it before the next TestFlight. - Demo risk: low. On a LiDAR demo phone, (1) removes the only remaining double-cable path I can find, and (2) adds a chevron in a case that showed nothing before. Before the demo, run the two device checks listed in the body. Also check once at a larger text size if the demo phone uses one.
- Swift:
if held, usesRealityKit || confirmedbinds as intended. The tuple change inresultIsDrawnhas one caller.drawnSincecan go stale while the first branch returns, but that's harmless: the firstdrawn == falseresets it, and in the enginedrawnimpliesheld. - Public-repo rules: the diff, the three commit messages and the PR body contain no names, addresses, meter readings,
private/material or internal thresholds. The "cream siding" is the bundled demo image.
Verdict: ready-with-notes
🤖 Generated with Claude Code
What changed
The review of #132 found three things still wrong on
beta(25f1f45). This PR fixes them:The second AR battery could still come back (ios: draw the AR result with the Canvas until RealityKit is seen drawing it #100). Once RealityKit had let go of the result, it had to see the battery on screen again before it took over from the Canvas. A lost hold happens when:
ScanEngine.showResultInCamera), oringestdisables the model).If that happened while the phone pointed at the meter, the Canvas's unoccluded battery and cable stayed on top of RealityKit's copy for as long as the phone stayed there.
ResultOverlayPolicynow remembers the first take-over and takes the result back as soon as the AR scene holds it again, with no need for the battery in view. While tracking is limited, the engine counts the disabled model as held. The screen draws neither layer then, so both come back together.The AR result's chevron is now shown for a spot hidden under the card or the Done button (ios: draw the AR result with the Canvas until RealityKit is seen drawing it #100 S2). Before, the chevron hid whenever the spot projected anywhere inside a 24 pt inset of the screen. Now "in view" means the band the chrome leaves clear: from 260 pt down to 160 pt above the bottom. In the demo's
resultARstate the spot sits inside that band, so that state is unchanged. The chevron's words now sit on the card's solid scrim.The every-state accessibility audit failed on beta (run 36302099002). The failure was
wallWalk-aim, "Contrast failed" onaim.legend. The legend's background was 70% black, so the demo's bright siding showed through. The legend now uses the instruction card's solid scrim and text colour, which pass the audit on every screen.#132's body wrongly marked #100 S2 as already fixed. I corrected its table.
Verification
testEveryStatePassesTheAccessibilityAudit(FULL=1 ci-local.sh t3/41-review-followups-2 followups-2): PASS on 5c83ad4. Package tests ran with warnings as errors, the device build passed, and 25 UI tests ran with 1 skipped and 0 failures. The every-state audit passed in 475 s. The first attempt at the same SHA also passed the audit, but failedAuditIssueKeyTests.testAGoneElementIsReadWithoutRecordingAFailure. The tap on "Gravel" didn't register within 5 s. That test doesn't touch any code this PR changes, and it passed on the rerun and in GitHub run 36302099002, so it looks like a tap-timing flake.ResultOverlayPolicyTests.afterATakeOverHoldingAgainTakesOverWithoutTheBatteryInView.confirmedThenOutOfViewButHeldStaysOnRealityKitno longer checks the re-confirmation, which is the behaviour this PR removes.wallWalk-aim: the legend sat over the cream siding.Needs a device (LiDAR phone), on "See it on your wall":
Risk
ScanEngine.watchResultInCameraandLiveCapture.resultIsDrawnonly, both on the AR result path.Part of #41. Part of #67.
Checklist
🤖 Generated with Claude Code