From 15c543fe6f5940fced3834e64260f62cc61ae218 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Thu, 5 Nov 2026 22:30:47 -0800 Subject: [PATCH] Record the hint latch, the offscreen sector, and the touch route. Swift matches ef68565. Docs only. --- docs/review/device-qa/README.md | 10 ++++++++++ .../review/device-qa/body-clearance-2026-11-05.md | 9 +++++++++ docs/review/device-qa/clip-select-2026-11-05.md | 15 +++++++++++++++ .../device-qa/concealment-flag-2026-11-05.md | 9 +++++++++ .../review/device-qa/court-rehearse-2026-11-05.md | 13 +++++++++++++ docs/review/device-qa/ghost-verify-2026-11-05.md | 11 +++++++++++ docs/review/device-qa/hint-latch-2026-11-05.md | 13 +++++++++++++ docs/review/device-qa/intro-frame-2026-11-05.md | 11 +++++++++++ .../device-qa/offscreen-sector-2026-11-05.md | 11 +++++++++++ docs/review/device-qa/touch-route-2026-11-05.md | 15 +++++++++++++++ .../review/device-qa/upgrade-accept-2026-11-05.md | 11 +++++++++++ 11 files changed, 128 insertions(+) create mode 100644 docs/review/device-qa/body-clearance-2026-11-05.md create mode 100644 docs/review/device-qa/clip-select-2026-11-05.md create mode 100644 docs/review/device-qa/concealment-flag-2026-11-05.md create mode 100644 docs/review/device-qa/court-rehearse-2026-11-05.md create mode 100644 docs/review/device-qa/ghost-verify-2026-11-05.md create mode 100644 docs/review/device-qa/hint-latch-2026-11-05.md create mode 100644 docs/review/device-qa/intro-frame-2026-11-05.md create mode 100644 docs/review/device-qa/offscreen-sector-2026-11-05.md create mode 100644 docs/review/device-qa/touch-route-2026-11-05.md create mode 100644 docs/review/device-qa/upgrade-accept-2026-11-05.md diff --git a/docs/review/device-qa/README.md b/docs/review/device-qa/README.md index 4cbb410..67f3086 100644 --- a/docs/review/device-qa/README.md +++ b/docs/review/device-qa/README.md @@ -336,5 +336,15 @@ Each note records one fact about bytes or draw calls already on main. None of th | [anchor-offset-2026-11-05.md](anchor-offset-2026-11-05.md) | The camera offset | | [view-contains-2026-11-05.md](view-contains-2026-11-05.md) | The view contains a point | | [utf8-order-2026-11-05.md](utf8-order-2026-11-05.md) | The byte order | +| [hint-latch-2026-11-05.md](hint-latch-2026-11-05.md) | The hint, shown once | +| [offscreen-sector-2026-11-05.md](offscreen-sector-2026-11-05.md) | The offscreen sector | +| [clip-select-2026-11-05.md](clip-select-2026-11-05.md) | The selected frames | +| [upgrade-accept-2026-11-05.md](upgrade-accept-2026-11-05.md) | The one upgrade accept | +| [touch-route-2026-11-05.md](touch-route-2026-11-05.md) | The touch route | +| [court-rehearse-2026-11-05.md](court-rehearse-2026-11-05.md) | The rehearsal card | +| [intro-frame-2026-11-05.md](intro-frame-2026-11-05.md) | The intro frame | +| [concealment-flag-2026-11-05.md](concealment-flag-2026-11-05.md) | The concealment flag | +| [body-clearance-2026-11-05.md](body-clearance-2026-11-05.md) | The body clearance | +| [ghost-verify-2026-11-05.md](ghost-verify-2026-11-05.md) | The ghost's second check | Next session: follow [retest-checklist.md](retest-checklist.md) (D-105 allows device checks on any build). diff --git a/docs/review/device-qa/body-clearance-2026-11-05.md b/docs/review/device-qa/body-clearance-2026-11-05.md new file mode 100644 index 0000000..de9d480 --- /dev/null +++ b/docs/review/device-qa/body-clearance-2026-11-05.md @@ -0,0 +1,9 @@ +# The body clearance + +Read 2026-11-05 from `CornerPursuit.bodyClear` on `ef68565`. No corner was moved. + +The live call stays in [corner-pursuit-2026-11-04.md](corner-pursuit-2026-11-04.md). That call leaves body clearance off, so its reached distance stays 8. + +When clearance is requested, a corner within 2 units is skipped, and the corner must also pass `bodyClear`. The ends are truncated to units. A segment that meets the box has distance 0. Otherwise the squared distance is the minimum of each end's squared distance to the box and each of the four corners' squared distance to the segment. An end outside the box uses the outside deltas. A point whose projection falls at or before the start uses the start, and one at or past the end uses the end. Between them the square is `(cross² + len² − 1) / len²`. The path is clear when every solid's squared distance is at least the radius squared. + +This does not turn clearance on. diff --git a/docs/review/device-qa/clip-select-2026-11-05.md b/docs/review/device-qa/clip-select-2026-11-05.md new file mode 100644 index 0000000..f1720b0 --- /dev/null +++ b/docs/review/device-qa/clip-select-2026-11-05.md @@ -0,0 +1,15 @@ +# The selected frames + +Read 2026-11-05 from `ClipTiming.selecting` on `ef68565`. No frame was dropped. + +The 60 Hz walk, the round-up, and the last frame's hold stay in [clip-frame-clock-2026-11-04.md](clip-frame-clock-2026-11-04.md). The delivered paths stay in [frame-backing-2026-11-05.md](frame-backing-2026-11-05.md). + +`selecting` throws `Failure.index` unless the index list is non-empty, every index is inside `frameUnits`, and each index is strictly greater than the one before it. The result is a new timing at the same `unitsPerSecond`, with `frameUnits` taken from those indices. + +`init` throws `Failure.invalid` when the rate is not positive, the unit list is empty, or a unit is not positive. It throws `Failure.overflow` when the running sum overflows. + +`frameIndex` compares full-width products. The elapsed product is the offset times `unitsPerSecond`. Each frame's end is the cumulative units times 60. The index is the first frame whose end is strictly above that elapsed product: a lower high word, or equal high words and a lower low word. The boundary belongs to the following frame. + +`ClipFrameLibrary.timing` calls `selecting` when the clip has authored timing, a direction was asked, and the direction list is non-empty and contains that direction. The indices are the frame ids that contain `__`. An asked direction missing from a non-empty list returns nil. Authored timing with no asked direction, or an empty direction list, returns that timing. With no authored timing, a clip that has `framesPerSecond` builds `uniform` from that direction's frame count and that rate. No rate returns nil. + +This does not change a rate. diff --git a/docs/review/device-qa/concealment-flag-2026-11-05.md b/docs/review/device-qa/concealment-flag-2026-11-05.md new file mode 100644 index 0000000..1f17ba2 --- /dev/null +++ b/docs/review/device-qa/concealment-flag-2026-11-05.md @@ -0,0 +1,9 @@ +# The concealment flag + +Read 2026-11-05 from `PresentationSnapshot` on `ef68565`. No window was changed. + +The mark stays in [combat-feedback-marks-2026-11-04.md](combat-feedback-marks-2026-11-04.md). The until-tick write stays in [player-movement-2026-11-04.md](player-movement-2026-11-04.md). + +`ghostConcealmentActive` is set when Ghost Step is selected, the tick is above 0, the tick is at or before `cameraInvisibleUntilTick`, and the outcome is not terminal. + +This does not change the dodge. diff --git a/docs/review/device-qa/court-rehearse-2026-11-05.md b/docs/review/device-qa/court-rehearse-2026-11-05.md new file mode 100644 index 0000000..7c39aec --- /dev/null +++ b/docs/review/device-qa/court-rehearse-2026-11-05.md @@ -0,0 +1,13 @@ +# The rehearsal card + +Read 2026-11-05 from `GameScene.rehearseCourt` on `ef68565`. No court was added. + +The snapshot stays in [court-capture-2026-11-05.md](court-capture-2026-11-05.md). The receipt guard stays in [receipt-archive-2026-11-04.md](receipt-archive-2026-11-04.md). The ghost store stays in [ghost-file-2026-11-04.md](ghost-file-2026-11-04.md). The clock stays in [run-card-2026-11-04.md](run-card-2026-11-04.md). The titles stay in [terminal-surface-2026-11-04.md](terminal-surface-2026-11-04.md). + +`beginRehearsal` returns false when there is no court snapshot. `rehearseCourt` then returns false. Otherwise it calls `resetForNewSimulation`, sets the ghost to nil, increments `ghostGeneration`, sets `terminalEvidenceStored` true, and redraws. + +`resetForNewSimulation` clears the spoken HUD, the run card, the rehearsal offer, the sound engine, the renderer, combat feedback, camera follow, the last display time, the lockdown tint, the court, the effects, the near-miss edges, the intro, and the instrumentation. It also resets the touch controller, so a held stick does not enter the new run. + +`finishRunIfNeeded` offers rehearsal when the outcome is failure, the court was reached, and a court snapshot exists. A rehearsal card is one row, label `REHEARSAL`, value the clock of the tick minus `courtStartTick`, or of the tick when that start is nil. The card's outcome is nil, and it is marked a rehearsal. The function returns before the ghost store and the medal store. The accessibility surface omits the share rect on that card. + +This does not store a rehearsal. diff --git a/docs/review/device-qa/ghost-verify-2026-11-05.md b/docs/review/device-qa/ghost-verify-2026-11-05.md new file mode 100644 index 0000000..ffef687 --- /dev/null +++ b/docs/review/device-qa/ghost-verify-2026-11-05.md @@ -0,0 +1,11 @@ +# The ghost's second check + +Read 2026-11-05 from `GhostRun` on `ef68565`. No record was rewritten. + +The build, the fade, and the lead stay in [ghost-replay-2026-11-04.md](ghost-replay-2026-11-04.md). The envelope's upgrade index stays in [replay-commands-2026-11-04.md](replay-commands-2026-11-04.md). + +`commandsAreOrdered` returns false when a command carries an upgrade index and `UpgradeID.from` returns nil. + +`replay` returns nil when a step leaves the tick unchanged. After a replay whose tick and digest match the record, the ghost that is kept is a new `Simulation.make` of that seed. `advance` steps while the tick is below the minimum of the live tick plus the lead and the recorded length, and it stops when that simulation is already terminal. + +This does not change a ghost. diff --git a/docs/review/device-qa/hint-latch-2026-11-05.md b/docs/review/device-qa/hint-latch-2026-11-05.md new file mode 100644 index 0000000..0ebf116 --- /dev/null +++ b/docs/review/device-qa/hint-latch-2026-11-05.md @@ -0,0 +1,13 @@ +# The hint latch + +Read 2026-11-05 from `AwarenessHintProjector` on `ef68565`. No hint was added. + +The copy stays in [tutorial-line-offer-2026-11-04.md](tutorial-line-offer-2026-11-04.md). The row that is drawn stays in [takedown-streak-2026-11-04.md](takedown-streak-2026-11-04.md) and [streak-type-2026-11-05.md](streak-type-2026-11-05.md). The card's 300-tick cap stays in [tutorial-timing-2026-11-04.md](tutorial-timing-2026-11-04.md). The heat row stays in [heat-caption-2026-11-04.md](heat-caption-2026-11-04.md). The quiet caption at this y stays in [quiet-approach-tag-2026-11-04.md](quiet-approach-tag-2026-11-04.md). + +`project` stores `shownAt` on the first tick the query is visible. It then returns the copy while `tick` is at or after `shownAt` and fewer than `visibleTicks` have passed. `visibleTicks` is 300. `reset` replaces the projector. `project` does not read `tutorialsEnabled`. + +`referenceRect` is `HUDRect(x: 422 - 180, y: 108, width: 360, height: 24)`. + +The snapshot query keeps an enemy whose `unaware` flag is set and whose absolute deltas from the camera centre are at most half the visible width and half the visible height. The state query keeps a living `isUnaware` enemy whose truncated position `PresentationCamera.contains` reports inside a follow camera of the truncated player. That contains test stays in [view-contains-2026-11-05.md](view-contains-2026-11-05.md). The visible size stays in [camera-follow-2026-11-03.md](camera-follow-2026-11-03.md). + +This does not draw the hint. diff --git a/docs/review/device-qa/intro-frame-2026-11-05.md b/docs/review/device-qa/intro-frame-2026-11-05.md new file mode 100644 index 0000000..365c534 --- /dev/null +++ b/docs/review/device-qa/intro-frame-2026-11-05.md @@ -0,0 +1,11 @@ +# The intro frame + +Read 2026-11-05 from `GameScene.introFrame` on `ef68565`. No frame was added. + +The durations, the skip, and the rule that the simulation does not step while the intro runs stay in [intro-sequence-2026-11-04.md](intro-sequence-2026-11-04.md). + +No intro returns `mayStep` true and an empty chirp list. A finished intro is set to nil and returns the same. A hold at or past the current frame returns `mayStep` false, an empty chirp list, and `held` true. Otherwise the frame advances, the intro is stored, and `mayStep` stays false, with the chirps from that advance. + +The production call passes no hold. `startIntro` builds the sequence from the camera entity ids and `settings.vfx.reducedMotion`. + +This does not add a frame. diff --git a/docs/review/device-qa/offscreen-sector-2026-11-05.md b/docs/review/device-qa/offscreen-sector-2026-11-05.md new file mode 100644 index 0000000..0865a5d --- /dev/null +++ b/docs/review/device-qa/offscreen-sector-2026-11-05.md @@ -0,0 +1,11 @@ +# The offscreen sector + +Read 2026-11-05 from `AudioProjector.sector` on `ef68565`. No speaker was moved. + +The four-letter heading stays in [facing-direction-2026-11-04.md](facing-direction-2026-11-04.md). `atan2Milli` stays in [millidegree-2026-11-04.md](millidegree-2026-11-04.md). The shipped view stays in [camera-follow-2026-11-03.md](camera-follow-2026-11-03.md). The voice cap stays in [effect-voice-ceiling-2026-11-03.md](effect-voice-ceiling-2026-11-03.md). + +A missing source returns nil. The deltas are `unitsTruncated`. A source whose absolute x delta is at most half `baselineWorldWidth`, and whose absolute y delta is at most half `baselineWorldHeight`, returns nil. Past either half, the sector is `MilliDeg.normalize` of `Cordic.atan2Milli(y:dy, x:dx)`, divided by 45,000. `normalize` stays inside 0 through 359,999, so the quotient is 0 through 7. + +After the cues are appended, each candidate stores the next sequence number, starting from 0, and the counter advances. A cue whose `sourceEntityId` is set then stores this sector. + +This does not add a speaker. diff --git a/docs/review/device-qa/touch-route-2026-11-05.md b/docs/review/device-qa/touch-route-2026-11-05.md new file mode 100644 index 0000000..45a01af --- /dev/null +++ b/docs/review/device-qa/touch-route-2026-11-05.md @@ -0,0 +1,15 @@ +# The touch route + +Read 2026-11-05 from `GameScene.beginTouch` on `ef68565`. No control was added. + +The intro skip stays in [intro-sequence-2026-11-04.md](intro-sequence-2026-11-04.md). Pause stays in [pause-2026-11-04.md](pause-2026-11-04.md). Share stays in [share-sheet-2026-11-04.md](share-sheet-2026-11-04.md). + +A missing projector returns false, and the rest of the batch continues. A skip that is still running returns true and consumes the rest of the batch. + +A terminal outcome returns true. A restart hit calls `restartRun`. A rehearse hit calls `rehearseCourt`. A share hit calls `presentShare`. A terminal tap that misses those controls is still consumed. + +An upgrade that is pending, or a paused run, returns true and does not call the controller. Otherwise a stick or dodge touch is handed to the controller. A pause result calls `onPauseRequested`. That path returns false, so a later touch in the same batch still runs. + +`moveTouch` returns immediately while an upgrade is pending or the run is paused. `endTouch` ends that token. + +This does not add a control. diff --git a/docs/review/device-qa/upgrade-accept-2026-11-05.md b/docs/review/device-qa/upgrade-accept-2026-11-05.md new file mode 100644 index 0000000..338b85a --- /dev/null +++ b/docs/review/device-qa/upgrade-accept-2026-11-05.md @@ -0,0 +1,11 @@ +# The one upgrade accept + +Read 2026-11-05 from `GameScene.chooseUpgrade` on `ef68565`. No upgrade was added. + +The sheet's submit stays in [upgrade-choice-layout-2026-11-04.md](upgrade-choice-layout-2026-11-04.md). + +`chooseUpgrade` returns true once. The upgrade must be pending, `session.pendingUpgradeChoice` must be nil, and `upgradeSelection.submit` must accept the index. It then resets the touch controller, stores move x 0, move y 0, and dodge clear, and stores the index on the session. A later call returns false. + +While a redraw sees the upgrade pending and the sheet is not yet presented, the same input clear runs before the sheet synchronizes. + +This does not add an upgrade.