Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
227 commits
Select commit Hold shift + click to select a range
062d6fa
docs(spec): camera pivot (spec 2) — Fable variant for adversarial com…
rulkens Sep 1, 2026
bf54b90
docs(spec): camera pivot (spec 2) — body-fixed navigation behind the …
rulkens Sep 1, 2026
679f966
docs(spec): merge camera-pivot spec variants — T2 union + R1 roll ruled
rulkens Sep 1, 2026
2fa71b9
docs(spec): correct P6 claims — no wheel-gap on main, drain site is t…
rulkens Sep 1, 2026
3e35daa
docs(spec): record P6 as-landed shape — single-drain register mutatio…
rulkens Sep 1, 2026
0ffd766
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 1, 2026
630993b
docs(plan): camera pivot — SDD task breakdown
rulkens Sep 1, 2026
1a4675a
feat(camera): add pose-frame types and surface-regime constants
rulkens Sep 1, 2026
b3844fa
feat(camera): lossless world ↔ body pose-frame conversions
rulkens Sep 1, 2026
b25bb0b
test(camera): sweep the engaged camera path in the one-Mpc-seam gate
rulkens Sep 1, 2026
0af3434
feat(camera): add maxTiltRad tilt-ceiling curve
rulkens Sep 1, 2026
eff9a06
test(camera): bind the pose-frame eye composition to updatePosition
rulkens Sep 1, 2026
a2d6b36
feat(camera): add reanchoredPose — ulp-quantized body-fixed re-anchoring
rulkens Sep 1, 2026
f8f6d3f
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 1, 2026
71c460a
feat(camera): add cursorRayBodyLocal — body-local pick ray from pose …
rulkens Sep 1, 2026
8bd6eff
fix(camera): pin reanchoredPose's ulp basis, trim comment budget (rev…
rulkens Sep 1, 2026
5524e12
feat(camera): add surfaceFloorM descent floor in metres
rulkens Sep 1, 2026
e53f97e
feat(camera): add regimeArmFor — the regime predicate (task 12)
rulkens Sep 1, 2026
2ab0e98
feat(camera): add surfaceReadoutOf — KML LookAt readout at the screen…
rulkens Sep 1, 2026
908c7d6
fix(camera): correct the no-hit fallback comment in surfaceReadoutOf
rulkens Sep 1, 2026
b75456e
feat(camera): anchored drag rotation on the frozen pick sphere
rulkens Sep 1, 2026
bb603eb
feat(camera): anchored zoom step (task 11)
rulkens Sep 1, 2026
08d9891
docs(camera): correct the §6a rotation direction and record the reort…
rulkens Sep 1, 2026
21881f4
fix(camera): close-form pin the cursor-anchor assertions, drop module…
rulkens Sep 1, 2026
a668f3b
refactor(camera): migrate camera.base to the FramedCameraPose union (…
rulkens Sep 1, 2026
8eb47b2
refactor(camera): fix liveWorldPose epoch docblock (task 13 review)
rulkens Sep 1, 2026
897ff38
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 1, 2026
d3468ef
feat(camera): provider B behind the pose seam (task 14)
rulkens Sep 1, 2026
2ceba02
fix(camera): make the provider-B seam test mutation-live (task 14, fi…
rulkens Sep 1, 2026
d21ae5a
feat(camera): the regime fold — body arms become reachable (task 15)
rulkens Sep 1, 2026
8c742d9
fix(camera): key the fold on the stored regime, gate gesture-end (tas…
rulkens Sep 1, 2026
3522458
feat(camera): the surface controller — anchored gestures on the body …
rulkens Sep 1, 2026
b5db931
fix(camera): drag the pose on screen, floor every write, yield to a c…
rulkens Sep 1, 2026
7d7ef9b
feat(debug): add Camera section to debug toolbar for FramedCameraPose
rulkens Sep 1, 2026
ea08229
feat(camera): enforce the surface arm's tilt ceiling on driven writes
rulkens Sep 1, 2026
0579c5a
fix(camera): pin heading through the tilt-clamp, extract headingTiltA…
rulkens Sep 1, 2026
a676cdc
fix(camera): NEAR0 slab honours cam.roll — stars/trails no longer she…
rulkens Sep 1, 2026
87f9f7e
fix(camera): zoom at the cursor at rest, recede from the sub-eye point
rulkens Sep 1, 2026
0e6f017
feat(camera): the retreat returns the view to north-up, as a ceiling
rulkens Sep 1, 2026
c4f82fc
fix(camera): clamp by residual, and keep north up on the way in
rulkens Sep 1, 2026
cc9ac86
fix(camera): keep the look-target on the forward ray — the Earth pop
rulkens Sep 1, 2026
35884c6
fix(camera): don't ratchet a zoom outward from inside the pivot floor
rulkens Sep 1, 2026
0165e2e
fix(camera): retarget the disengage commit to the body centre — the p…
rulkens Sep 1, 2026
89edd09
feat(camera): the north-locked engaged camera — one authority, one de…
rulkens Sep 1, 2026
475c125
refactor(camera): delete reanchoredPose — zero importers, spec-admitt…
rulkens Sep 1, 2026
bd30fdf
refactor(camera): delete surfaceReadoutOf + SurfaceReadout — readout …
rulkens Sep 1, 2026
be218c9
refactor(camera): one live-pose home — the gesture fold writes the re…
rulkens Sep 1, 2026
b4c1668
feat(camera): cursor-anchored recession into the tilt band (rulings 6+7)
rulkens Sep 1, 2026
4a2e462
feat(camera): blend the frame transition over the approach band (ruli…
rulkens Sep 2, 2026
b693e4a
fix(camera): recession tilt rides the ceiling — distributed with zoom…
rulkens Sep 2, 2026
77a3baa
fix(camera): an at-rest wheel notch refreshes the live register — no …
rulkens Sep 2, 2026
0e20a17
docs(camera): sync the spec to rulings 7/R1 + tidy review minors
rulkens Sep 2, 2026
0073143
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 2, 2026
503efdb
fix(camera): the roll target rides the band curve — full return to th…
rulkens Sep 2, 2026
23a2b31
feat(debug): camera section exposes the full orientation pipeline + c…
rulkens Sep 2, 2026
21a4604
fix(camera): the roll ride runs on every driven zoom path — the follo…
rulkens Sep 2, 2026
8464504
fix(camera): the engaged settle norths toward the band-blended up (ru…
rulkens Sep 2, 2026
20958d5
fix(camera): continuity-bound the reference ride, both arms (review R…
rulkens Sep 2, 2026
e495366
fix(camera): hold through the singular neighbourhood, drain the intri…
rulkens Sep 2, 2026
9726265
fix(camera): one orientation codepath — both arms read one field, one…
rulkens Sep 2, 2026
649f052
feat(camera): TILT_GAIN 1.6 — faster tilt handle (user feel ruling)
rulkens Sep 2, 2026
e56657b
feat(camera): round-9 feel levers — log(h/R) blend-space, band slider…
rulkens Sep 2, 2026
a5bf83c
fix(camera): a focus on a different body releases the engaged arm (ro…
rulkens Sep 2, 2026
16892ec
feat(camera): Cesium-style remembered tilt — zoom never authors tilt …
rulkens Sep 3, 2026
762a2d4
fix(camera): rounds 9-11 review findings — R10-1 clip-path reachabili…
rulkens Sep 3, 2026
f62b5e0
fix(camera): tilt lerps IN through the window — the world arm express…
rulkens Sep 3, 2026
e532f64
fix(camera): dead tilt floor at 0 + inverted right-drag heading (ruli…
rulkens Sep 3, 2026
2494ba0
fix(camera): commit sites bake the pre-projection centre-looking pose…
rulkens Sep 3, 2026
90fa55b
fix(camera): GE tilt direction + signed floor budget (rulings 14 + 16)
rulkens Sep 3, 2026
3047a88
fix(camera): authored register + displayed projection box (R12b-1..3)
rulkens Sep 3, 2026
b7800bf
fix(camera): commit-edge override discriminates on the incoming drive…
rulkens Sep 3, 2026
332c394
test(camera): pin the register staying authored across a non-pivoting…
rulkens Sep 3, 2026
7102591
fix(camera): tilt drag direction back to Google-Maps style (ruling 17…
rulkens Sep 3, 2026
740f078
fix(camera): body switch resets the pose memory and lands at the fram…
rulkens Sep 3, 2026
a4c23f4
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 9, 2026
8ab7668
fix(camera): body switch is an eye-preserving flight to the new body …
rulkens Sep 9, 2026
7e58c58
docs(camera): round-14b minors — capture rationale, ease target, pan-…
rulkens Sep 9, 2026
0c4350e
feat(camera): band defaults 0.2/0.4 (ruling 19)
rulkens Sep 9, 2026
1acf67c
feat(camera): surface the hysteresis clamp in the debug readout
rulkens Sep 9, 2026
a8bf59c
test(camera): re-home band-dependent fixtures for the 0.2/0.4 default…
rulkens Sep 9, 2026
7eec6dc
refactor(surface-controller): move types + constants to their homes
rulkens Sep 9, 2026
62a4867
refactor(surface-controller): move helper functions to utils/camera
rulkens Sep 9, 2026
0560a94
refactor(camera): consolidate dot3 to one home
rulkens Sep 9, 2026
f089069
refactor(camera): consolidate bodyFixedEyeM to one home
rulkens Sep 9, 2026
bbdf0a5
test(camera): direct coverage for 4 newly-extracted surface helpers
rulkens Sep 9, 2026
8a11c60
docs(backlog): camera smooth wheel zoom + flick coast — investigated,…
rulkens Sep 9, 2026
40b2a9f
test(camera): golden trace byte bar for the orientation settles
rulkens Sep 9, 2026
0ddbcc1
refactor(camera): un-braid the orientation settles — one level, one t…
rulkens Sep 9, 2026
7709a48
test(camera): golden trace keeps every Nth sample plus leg and mode s…
rulkens Sep 9, 2026
eafb79a
refactor(camera): delete headingTiltAt (no src callers)
rulkens Sep 9, 2026
93189a2
test(camera): add shared runFrame-sim harness, migrate bodySwitchReset
rulkens Sep 9, 2026
249e0c7
test(camera): migrate focusReleaseWhileEngaged onto the shared sim ha…
rulkens Sep 9, 2026
e194a00
test(camera): add tiltOfPose/hrOfPose, migrate tiltRegisterLoop
rulkens Sep 9, 2026
4d0e727
test(camera): migrate tiltCommitIdempotence onto the shared sim harness
rulkens Sep 9, 2026
bb9a8ae
test(camera): migrate tiltLerpRoundTrip onto the shared sim harness
rulkens Sep 9, 2026
6f8935a
test(camera): migrate engageFlipPop onto the shared sim harness
rulkens Sep 9, 2026
e4fe883
test(camera): migrate focusedZoomOutRoundTrip onto the shared sim har…
rulkens Sep 9, 2026
08fa938
test(camera): migrate drainInput onto the shared sim harness
rulkens Sep 9, 2026
148d8df
test(camera): migrate poseFold onto the shared sim harness
rulkens Sep 9, 2026
38c70d6
test(camera): migrate commitOnEdge onto the shared sim harness
rulkens Sep 9, 2026
e15a38e
test(camera): migrate playClipFlyout onto the shared sim harness
rulkens Sep 9, 2026
41007cd
test(camera): add neutralDistance option to the shared sim harness
rulkens Sep 9, 2026
812a54d
test(camera): split harness types into their own files (one-symbol-pe…
rulkens Sep 9, 2026
01c6f61
docs(comments): comment-budget audit over src/utils, src/@types, src/…
rulkens Sep 9, 2026
cd9dac3
docs(comments): keep the why behind absolute drag pixels in InputStep
rulkens Sep 9, 2026
bdb3cbc
docs(comments): comment-budget audit over tests (camera-pivot)
rulkens Sep 9, 2026
3eb61b8
docs(comments): drop the last headingTiltAt mention in surfaceControl…
rulkens Sep 9, 2026
9b155f7
docs(comments): comment-budget audit over src/services, src/component…
rulkens Sep 9, 2026
678ca17
test(camera): re-home band fixtures onto the retuned edges, tighten l…
rulkens Sep 9, 2026
7a97198
refactor(camera): finish the body-local-frame one-home, trim Orientat…
rulkens Sep 9, 2026
2923259
docs(camera-pivot): restate the band figures at the retuned edges
rulkens Sep 9, 2026
7f6797d
test(camera-pivot): make the review-flagged bars and comments load-be…
rulkens Sep 9, 2026
458210e
docs(camera-pivot): state the RB-4 drift rate at both band edges
rulkens Sep 9, 2026
1d44398
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 9, 2026
24182c2
docs(camera-pivot): cameraRuntime single-writer — spec ground section…
rulkens Sep 9, 2026
aa47699
feat(camera): pure Epoch primitives beside the camera clock
rulkens Sep 9, 2026
050905e
feat(camera): surface gesture memory as data
rulkens Sep 9, 2026
ad91f32
chore(camera): trim Epoch headers to the comment budget
rulkens Sep 9, 2026
5364d9b
fix(camera): surfaceStep zoom arm ignores the gesture while the point…
rulkens Sep 9, 2026
2314163
feat(camera): advanceEpochs — one advance site per epoch per frame
rulkens Sep 9, 2026
d88c69b
test(camera): golden trace leg for drivers, epochs and the action stream
rulkens Sep 9, 2026
5bad769
test(camera): advanceEpochs positive-path tween leg; timeless comments
rulkens Sep 9, 2026
d3dd41c
test(camera): driver trace — pan leg, pretty fixture, epoch reads beh…
rulkens Sep 9, 2026
391a208
refactor(camera): CameraClock becomes CameraEpochs + FollowMemory; cl…
rulkens Sep 9, 2026
77a1945
docs(camera): comment budget on the files Tasks 4+5 touched
rulkens Sep 9, 2026
9908829
refactor(camera): clip elapsed in ms like every other epoch
rulkens Sep 9, 2026
ad57bb5
refactor(camera): drivers take a ctx and return their memory
rulkens Sep 9, 2026
b68b145
chore(camera): T7 review polish — test ctx values, header trim, ungat…
rulkens Sep 10, 2026
585634c
refactor(camera): the follow driver owns the notch it swallows
rulkens Sep 10, 2026
c0918be
docs(camera-pivot): T8 ruling — ride stays in the drain, ctx carries …
rulkens Sep 10, 2026
a53711d
chore(camera): T8 review polish — spin bag naming, comment fixes, thi…
rulkens Sep 10, 2026
afa82aa
refactor(camera): the drain returns the follow memory it changed
rulkens Sep 10, 2026
9156c08
chore(camera): T9 review — follow field note names its one writer; sp…
rulkens Sep 10, 2026
88b20c6
refactor(camera): delete the surface controller closure
rulkens Sep 10, 2026
aa72c7d
chore(camera): retire "controller" prose in the surface tests
rulkens Sep 10, 2026
d8ff443
refactor(camera): replayInput is pure and returns its actions
rulkens Sep 10, 2026
74e84cb
docs(camera-pivot): T12 ruling — replay advances the spin epoch local…
rulkens Sep 10, 2026
7fe18a8
refactor(camera): effective intent, dispatch after the register write
rulkens Sep 10, 2026
6ec02c2
refactor(camera): cameraRuntime as five value groups
rulkens Sep 10, 2026
30cdcf5
chore(camera): T14 review polish — type-file comment trims, seedPose …
rulkens Sep 10, 2026
eeb5b39
refactor(camera): stepCameraRuntime — one writer, one frame
rulkens Sep 10, 2026
14a5cfe
chore(camera): restore the simDays landmine, wrap three long doc comm…
rulkens Sep 10, 2026
bfd00a4
test(camera): gate the cameraRuntime single writer
rulkens Sep 10, 2026
7cb52f4
test(camera): engaged-arm clock invariance (spec §14)
rulkens Sep 10, 2026
b8b4946
test(camera): single-writer gate — alias widening, deepFreeze self-test
rulkens Sep 10, 2026
6a87cb4
fix(camera): split the follow driver so autoRotate cannot strand it (…
rulkens Sep 10, 2026
abd30c9
fix(camera): the follow approach yields only after a saturated frame
rulkens Sep 10, 2026
e0985c5
test(camera): mark the dropped-notch cell the driver golden now pins
rulkens Sep 10, 2026
67c6316
docs(camera): supersede T18, record the single-writer prep
rulkens Sep 10, 2026
d43e8c5
chore(camera): delete the dead authoredWorldPose helper
rulkens Sep 10, 2026
ae20ac8
chore(camera): inline projectionOf at its one remaining caller
rulkens Sep 10, 2026
f0244bb
refactor(camera): one winner pick per frame
rulkens Sep 10, 2026
acd4ea5
refactor(camera): the driver row names its epoch
rulkens Sep 10, 2026
5bc460a
refactor(camera): the keep-alive reads the approach's own exit signal
rulkens Sep 10, 2026
c2ae733
refactor(camera): the debug snapshot takes the two gesture facts flat
rulkens Sep 10, 2026
44cd31a
chore(camera): comment budget on the runtime's type files
rulkens Sep 10, 2026
76035a4
chore(tests): drop the EpochCells alias nothing imports
rulkens Sep 10, 2026
d015c75
chore(camera): T20 review — one pick in the test helper, two contract…
rulkens Sep 10, 2026
50e39a5
refactor(camera): drop the two DriverCtx fields no row reads
rulkens Sep 10, 2026
751f237
docs(camera-pivot): spec + plan file lists match the shipped runtime
rulkens Sep 10, 2026
d2c4b63
chore(camera): final-review polish
rulkens Sep 10, 2026
8d8b930
test(camera): the two frame-sim fixtures run the real stepCameraRuntime
rulkens Sep 10, 2026
9bc2f7a
refactor(camera): inline poseOf into its one caller
rulkens Sep 10, 2026
3a24485
docs(backlog): the wheel notch routed by last frame's winner
rulkens Sep 10, 2026
5a4b83e
docs(backlog): correct the notch blocker + helper comment
rulkens Sep 10, 2026
258ce1a
docs(sdd): archive the camera-runtime single-writer ledger + T19 perf…
rulkens Sep 10, 2026
02d1613
feat(camera): lonLatFocusPose authors a body arm
rulkens Sep 10, 2026
cb02b6b
docs(backlog): the fly-to pre-fold frame joins the notch-routing family
rulkens Sep 10, 2026
8e71ba4
feat(animation): frame-tagged keyframe endpoints, interpolated in the…
rulkens Sep 10, 2026
2d79d79
feat(animation): decode a body-framed keyframe to a BodyFixedPose at …
rulkens Sep 10, 2026
b9a9455
feat(animation): logCameraState names the pose frame and prints a bod…
rulkens Sep 10, 2026
ff89eb6
docs(animation): trim the ClipFrameOptions playback note to the load-…
rulkens Sep 10, 2026
bc4e02c
fix(animation): a relative spin never opens a frame leg
rulkens Sep 10, 2026
8010402
refactor(camera): reuse the seam's own entries instead of copying its…
rulkens Sep 10, 2026
7b7df20
docs(camera): correct the frame-conversion instant and drop the dead …
rulkens Sep 10, 2026
04d55ff
test(animation): value-level and driver-level cover for the frame legs
rulkens Sep 10, 2026
e7b0fdb
refactor(camera): DriverId union replaces string ids
rulkens Sep 10, 2026
7418d00
refactor(camera): drivers read the basis pair from DriverCtx
rulkens Sep 10, 2026
47f9f5d
refactor(camera): cameraDebugSnapshotOf reuses nearestBodyHR
rulkens Sep 10, 2026
da68189
refactor(camera): thread the frame's body map into the pin and the ap…
rulkens Sep 10, 2026
24a3c17
docs(backlog): autoRotate epoch read by the replay joins the stale-wi…
rulkens Sep 10, 2026
4673ecc
test(camera): settleGoldenTrace uses the shared harness
rulkens Sep 10, 2026
4ee833d
test(camera): drop mirror and restatement tests in cameraDrivers.test
rulkens Sep 10, 2026
a3f3a67
test(camera): drop the source-text grep from oneMpcSeam.test
rulkens Sep 10, 2026
e15cd43
refactor(engine): state.cam becomes a boot flag
rulkens Sep 10, 2026
3674a72
test(camera): inline recedeUntilDisengaged at its one caller
rulkens Sep 10, 2026
499ae7c
docs(camera): trim over-budget headers on branch-added files
rulkens Sep 10, 2026
7c5aee7
refactor(camera): drop the unused export on selectCameraIntent
rulkens Sep 10, 2026
5aa4389
feat(camera): widen the surface band defaults to 0.45 / 0.9 h/R
rulkens Sep 10, 2026
a94706a
test(camera): re-record the settle golden trace for the 0.45 / 0.9 band
rulkens Sep 10, 2026
82c1446
test(camera): re-home the poseFold disengage premise on SURFACE_REGIME
rulkens Sep 10, 2026
0768016
docs(camera): restore the log-space derivation on blendSpace
rulkens Sep 10, 2026
718de4c
test(camera): make liveRenderCamera's wiring observable
rulkens Sep 10, 2026
4a73aa4
fix(camera): take the wave-end review's minors (b), (c), (e), (f), (g)
rulkens Sep 10, 2026
7350b21
docs(camera): bring the band prose up to the 0.45 / 0.9 re-tune
rulkens Sep 10, 2026
fa35854
test(camera): re-home the two mid-window tilt legs on SURFACE_REGIME
rulkens Sep 10, 2026
c43b757
feat(camera): give the orientation blend its own band + sliders
rulkens Sep 10, 2026
6c02b94
Merge origin/main into camera-pivot
rulkens Sep 10, 2026
9655c68
fix(camera): key the blend tests to TILT_BAND and assert its defaults…
rulkens Sep 10, 2026
3229a16
feat(camera): tilt-blend band defaults 0.06 / 0.60 h/R
rulkens Sep 10, 2026
f0e41a4
test(camera): re-record the golden traces for the 0.06 / 0.60 tilt band
rulkens Sep 10, 2026
ee75dce
docs(grill): camera debug panel redesign transcript (9 rulings)
rulkens Sep 10, 2026
bac4f43
fix(camera): scale the zoom-driven orientation settle by the notch's …
rulkens Sep 10, 2026
e9e2c03
test(camera): re-record the golden traces for the per-log-zoom settle
rulkens Sep 10, 2026
ffca44e
fix(camera): price the settle on the zoom actually spent
rulkens Sep 10, 2026
2039ea1
test(camera): pin the no-whip envelope at the fold clamp's own bound
rulkens Sep 10, 2026
ff48c5c
chore(camera): drop the implied envelope assertion, correct two settl…
rulkens Sep 10, 2026
884eaca
refactor(camera): delete the drag-wall tilt ceiling
rulkens Sep 10, 2026
e7c6c92
refactor(camera): fold pointerDown into the gesture phase
rulkens Sep 10, 2026
c99b0e0
refactor(camera): price drag settles in their own cap
rulkens Sep 10, 2026
8301cdd
test(camera): witness the drag-tilt at the band top; true up §6 and s…
rulkens Sep 10, 2026
b0dfb64
chore(camera): drop the dead ceiling clause from seedRememberedTilt
rulkens Sep 10, 2026
79cd028
feat(debug): per-frame orientation delta + peak hold in the camera de…
rulkens Sep 10, 2026
c67b3d2
feat(debug): rebuild the Camera debug section around per-DOF rows and…
rulkens Sep 10, 2026
1aed921
fix(debug): gate the per-frame orientation record on the panel being …
rulkens Sep 10, 2026
09cc12a
test(debug): make the north-up-off guard fail on a blanked target; dr…
rulkens Sep 10, 2026
4c0c88c
chore(debug): reset the delta on subscribe; restore the clipboard stub
rulkens Sep 10, 2026
3cb63f4
feat(camera): one CameraTuning record, clamp and slice field
rulkens Sep 11, 2026
7b21505
refactor(camera): thread CameraTuning from the store to the math; del…
rulkens Sep 11, 2026
6ce2c01
chore(camera): sweep the old tuning records out of prose
rulkens Sep 11, 2026
20f3d66
chore(camera): read tuning from the seeded store in tests; restate th…
rulkens Sep 11, 2026
420cbd2
docs(backlog): park the camera-pivot adjacent findings
rulkens Sep 11, 2026
f348cd9
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 11, 2026
8f3b083
docs(plan): tick camera-pivot tasks + DoD; restate the retired ceilin…
rulkens Sep 11, 2026
908d88b
docs(plan): mark camera-pivot complete
rulkens Sep 11, 2026
bc4524e
chore(camera): deletion audit — drop the unread debug fields and dupl…
rulkens Sep 11, 2026
4ce5f95
Merge remote-tracking branch 'origin/main' into camera-pivot
rulkens Sep 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ Items with a **→ details** link have a full write-up in [`backlog/`](backlog/)
- [ ] **Fades pop instead of ramping after the render loop idles** `ready` — `fadeRegistry.fadeTo` stamps the ramp's start from the stale last-_rendered_-frame time, not a live clock. → [details](backlog/2026-08-20-fade-pop-after-idle.md)
- [ ] **Liveness guards accept `undefined`; `NaN` alpha class in fixtures** `ready` — `zoneOfAvoidanceLiveness.ts`'s `=== null` guard lets `undefined` slip through, and `focusBlend`-omitting fixtures produce silent `NaN` alphas. → [details](backlog/2026-08-20-liveness-undefined-guards.md)
- [ ] **`createTieredScfdFetcher` factory** `ready` — the Edenhofer dust fetcher will be the third hand-copied tiered-SCFD fetcher (after `mcpmFetcher` and polyphorm's); collapse the three into one `createTieredScfdFetcher(baseName)` factory on next touch.
- [ ] **Body-arm entry doesn't adopt the arriving pose's tilt** `deferred` — disengage can pop up to 37°. → [details](backlog/2026-09-11-camera-arm-entry-adopts-arriving-tilt.md)
- [ ] **World-arm wheel burst has no per-frame turn clamp** `awaiting-decision` — a 20-notch burst can roll 115° in one frame. → [details](backlog/2026-09-11-camera-absolute-arm-wheel-burst-unclamped.md)
- [ ] **`deriveBodyStates`' key type is a pre-existing lie (14 casts)** `needs-design` — callers cast to an unrelated `BodyId` union. → [details](backlog/2026-09-11-derive-body-states-scene-body-id-type.md)
- [ ] **Frame-tagged clip keyframes have no per-endpoint authoring validation** `needs-design` — a straddling channel tween silently freezes. → [details](backlog/2026-09-11-clip-per-endpoint-tag-authoring-validation.md)
- [ ] **f32 half-ulp sightline nudge at the body-arm flip** `needs-verification` — ~4 mas, in two narrow `h/R` windows. → [details](backlog/2026-09-11-camera-f32-half-ulp-sightline-nudge-at-arm-flip.md)
- [ ] **`bodyLikeFraming` ⇄ `focusFraming` import cycle** `ready` — a type-only back-edge; extract the type. → [details](backlog/2026-09-11-framing-import-cycle-bodyLikeFraming-focusFraming.md)
- [ ] **Camera radar residuals: wake vote, channel expiry, authoredOverride** `ready` — three hand-restated facts from the wave-end radar. → [details](backlog/2026-09-11-camera-radar-residuals-h3-m4-m5.md)

## Rendering

Expand Down Expand Up @@ -179,6 +186,8 @@ Items with a **→ details** link have a full write-up in [`backlog/`](backlog/)
- [ ] **Earth caption stamp out-picks occluders** `ready` — the forced-band 18 px Earth pick point punches through a transiting Moon. → [details](backlog/2026-07-29-earth-caption-stamp-outpicks-occluders.md)
- [ ] **Touch picking selects the wrong galaxy** `needs-design` — the pick pad is in device px, so the clickable disc halves on retina/phone. → [details](backlog/2026-07-29-touch-pick-accuracy.md)
- [ ] **Windows touchscreen pinch-zoom dead** `needs-repro` — works on mobile; gesture code is platform-uniform PointerEvents, so event delivery on Windows is the suspect; needs an on-device event log. → [details](backlog/2026-08-16-windows-touchscreen-pinch-zoom.md)
- [ ] **Camera smooth wheel zoom + flick coast** `awaiting-decision` — Google Maps / Cesium feel via synthetic `InputStep`s in the aggregator; revises grill Q8 (no inertia), shape pre-ruled. Follow-up to #647. → [details](backlog/2026-09-09-camera-smooth-zoom-and-flick-coast.md)
- [ ] **Wheel notch routed by last frame's winner** `needs-design` — the replay routes a notch before the frame has picked its winner; four defects follow. → [details](backlog/2026-09-10-wheel-notch-route-by-last-winner.md)
- [ ] **Autorotate + mouse-move jitter** `needs-repro` — intermittent frame jitter seen on `refactor/debug-derivation`, diff-clean per investigation; falsify against base commit from a second worktree. → [details](backlog/2026-08-20-autorotate-mousemove-jitter.md)
- [ ] **StatusBar mobile reflow** `ready` — reflow the StatusBar for narrow viewports (no media queries today). The InfoCard bottom-sheet + SettingsPanel collapse-launcher already shipped.
- [ ] **SettingsPanel polish** `needs-design` — visual cleanup + section re-ordering + per-section icons; 2.3k lines of hand-coded text-only rows today. → [details](backlog/2026-07-22-settings-panel-polish.md)
Expand Down
85 changes: 85 additions & 0 deletions docs/backlog/2026-09-09-camera-smooth-zoom-and-flick-coast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Camera: smooth wheel zoom + flick coast (Google Maps / Cesium feel)

**Raised:** 2026-09-09, user, on the spec-2 camera-pivot branch (PR #647).
Investigated, not built. Follow-up PR after #647 lands.

## Ask

Google Maps and Cesium animate a wheel notch instead of jumping, and a short
drag that ends fast coasts a little after release. skymap does neither: a
notch is applied whole in the frame it fired, and release stops the camera dead.

## Ruling context

Grill Q8 (`docs/grill-sessions/globe-camera-pivot-2026-08-24.md`) ruled **no
inertia in the first landing**, and pre-ruled the only acceptable later shape:
Cesium's flick-only synthetic replay, no persistent velocity, cross-cancel on
any new input, replayed in the body-fixed frame. Building this is a Q8
revision and needs the user's word; the shape is already fixed.

## Why it is cheap here

Every camera motion is an `InputStep` (`src/@types/camera/InputStep.d.ts`)
that `inputAggregator` (`src/services/engine/subsystems/inputAggregator.ts`)
collapses per frame and `replayInput` (`src/services/engine/camera/replayInput.ts`)
replays through one path — the surface controller in a body arm, the world-arm
fold otherwise. `gestureEnd` is the single commit site: it bakes the register,
drops the surface latch, and clears `dragging`. Nothing downstream can tell a
synthesized step from a real one. Both features therefore live in the
aggregator, with `nowMs` passed into `drain()`. No change to the controller,
the drivers, the fold, or the authored/displayed two-box.

## A. Smooth zoom

Today: `factor = e^(deltaY·k)` per notch, applied whole; notches in one frame
multiply (`foldZoom`).

Shape: the aggregator keeps a remaining log-factor plus the latest cursor
pixel, and each frame emits a synthetic zoom step releasing a fraction of it
(exponential approach, ~150–250 ms, retargeting as notches arrive). Cursor
anchoring, `anchoredZoomStep`, the roll ride, the tilt mapping and the floor
come for free.

Two consequences to decide up front:

- The orientation settle's decay is defined **per input step**. Ten small
steps per notch decay unauthored deviation faster than one big step. Make
the step law "per unit of log-zoom" in the settle un-braid rather than
accept a silent feel change.
- At-rest zoom commits to the store per step. Treat the ease as a gesture
(`duringGesture: true` + a synthetic `gestureEnd`) so it commits once.

Trackpads already deliver continuous small deltas (`WHEEL_GESTURE_GAP_MS`
handles their momentum bursts); keep the ease short or they feel laggy.

Estimate: 40–60 lines + two tests (retargeting mid-ease; single commit).

## B. Flick coast

Shape (Cesium `maintainInertia` / `decay`, see
`docs/research/2026-08-24-camera-pivot/cesium-notes.md` §2.9): on release
after a hold under ~400 ms, hold the last drag motion and emit a synthetic
drag step per frame with the motion scaled by `exp(−(1−c)·25·t)`, c ≈ 0.9;
stop under 0.5 px; **only then** emit `gestureEnd`. Deferring `gestureEnd`
keeps the surface latch and the frozen anchor alive and the orbit-drag driver
rendering the register. Any pointer or wheel event cancels the coast (the
aggregator sees it first). Body-fixed replay falls out of the anchored drag
rotation re-solving the ground under the synthetic cursor.

Pan and orbit modes only. Cesium gives tilt and look no inertia, and a
coasting tilt would keep writing the remembered-tilt memory (ruling 12).

Estimate: 80–100 lines + three tests (flick vs hold threshold; decay stops;
cross-cancel).

## Shared hook

The same per-frame synthetic-step emitter would host the gesture-end settle
tween proposed for the jarring tilt-up + zoom-out (SDD ledger, 2026-09-03
ideas list, item 1).

## Sequencing

After the settle un-braid on #647 lands, so the per-step vs per-unit decay
question is answered in one place. Feel constants (ease τ, decay c, flick
threshold) go in `src/data/camera/`.
103 changes: 103 additions & 0 deletions docs/backlog/2026-09-10-wheel-notch-route-by-last-winner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Camera: a wheel notch is routed by LAST frame's winner

**Raised:** 2026-09-10, during PR #647's final review. User ruled: later, own change.

Two pre-existing defects with one cause, and it is stage order: the replay runs
BEFORE the winner is picked, so `replayInput` has nothing to route a notch by
except `cameraRuntime.register.winner` — last frame's answer
(`src/services/engine/camera/replayInput.ts:215`, `:242`). A third case (c) has
the same shape one stage later: the replay routes by the STORED arm, not the arm
the fold resolves this frame.

## (a) The notch on a follow→spin hand-off frame is dropped

On the exact frame `followApproach` saturates and hands off to `autoRotate`, the
notch resolves into follow memory (`isFollowDriverId(winnerLastFrame)`) that the
winning spin never adopts. Pinned by the driver golden trace at
`tests/services/engine/frame/driverGoldenTrace.test.ts:189` — "This notch lands on
the hand-off frame and is DROPPED (routed by last frame's winner into follow memory
the spin never adopts)" — the marker added by `e0985c5d4`.

Splitting the follow driver into `followApproach`/`followHold` gave every spin-on
focus a hand-off frame, so this went from rare to once per focus.

## (b) The notch on a spin-OFF frame zooms a frozen base

`spin: { owns: winnerLastFrame === 'autoRotate', … }` still reads true on the frame
autoRotate goes off, so the notch is applied to the pre-spin `base` the spin froze,
and `commitOnEdge` (`src/services/engine/camera/commitOnEdge.ts`) bakes that stale
pose on the same edge.

## (c) The notch on a fly-to's pre-fold frame takes the other zoom

Raised by T19's review (2026-09-10). `routeToSurface` gates on `camera.base.frame`
(`replayInput.ts:104-106`), and the fly-to saga now commits a body arm from
anywhere. Launched from OUTSIDE the band, `base` is a body arm for the one frame
before `projectFramePose` disengages it, so an at-rest notch in that frame runs
`surfaceStep`/`anchoredZoomStep` (and commits its own body arm) where the orbit
zoom was due. Same when the body arm names a body other than the focused one
(`regimeArmFor` rejects it). One notch's scaling in a ≤ 16 ms window, no pose
discontinuity; the fix is routing on the RESOLVED regime, not the stored one —
gating the saga on the band would put the band predicate back in the instrument
(spec §9).

## Fix shape

Route the notch by THIS frame's winner. The routing stage is the replay, so the
winner has to be known above it — and today the pick cannot simply move up, because
both of its arguments are replay OUTPUTS:

- `pickWinner(drivers, rootState, approachDone)` reads the POST-replay EFFECTIVE
`rootState` — the snapshot with the replay's own actions folded through the camera
reducer (`src/services/engine/camera/stepCameraRuntime.ts:89-95`). That is
load-bearing, not incidental: the drivers must see this frame's commits, `endDrag`
above all, or `orbitDrag` wins one frame too long.
- `approachDone` is `drained.follow.saturated` (`stepCameraRuntime.ts:100-101`) —
the replay's follow memory.

Not a blocker, contrary to first appearances: the epochs. `pickWinner` takes none
(`src/services/engine/camera/cameraDrivers.ts:38-42`), and `advanceEpochs` already
runs AFTER the pick, consuming `winner.epoch` (`stepCameraRuntime.ts:104-112`).

Two directions, neither worked through:

- Pick from the PRE-replay snapshot and re-establish the `endDrag` guarantee some
other way — the gesture-end edge is present in the drained steps themselves,
before the replay folds it.
- Split the pick in two: a ROUTE decision above the replay, needing only the
pre-replay intent plus last frame's follow memory, with the existing post-replay
pick kept for the pose. Whether a correct route can be decided on that much is
the open question.

Either way, re-record `tests/fixtures/camera/driverGoldenTrace.json` with a
parse-compared cell diff in the commit body, and drop the marker at the leg.

## (d) The spin phase the notch zooms off is advanced twice, under two rules

Raised by the wave-end entanglement radar (2026-09-10, finding M3). Same family:
the replay needs a fact the frame has not resolved yet, so it resolves a private
copy.

`replayInput.ts:236-239` advances the autoRotate epoch locally —
`advanceEpoch(ctx.autoRotateEpoch, active ? camera.base : null, nowMs)` — reads
`elapsedMs` off it for `applyWheelZoom`'s `spinElapsedMs`, and throws the row
away (handing it to `advanceEpochs` would keep a fold-time reset the frame's own
rule declines). The frame's advance
(`stepCameraRuntime.ts:106` → `cameraEpochs.ts:59-67`) replays `prev.ref` — a
guaranteed no-op — on every frame the winner is not `autoRotate`.

The two disagree by construction on any frame some other driver wins while
auto-rotate is on: the local advance re-bases on a changed `camera.base` (a
commit from earlier in the same drain) where the frame's does nothing. On that
frame `applyWheelZoom` zooms off a spin position the renderer never showed, and
the commit bakes it — a yaw pop on a wheel notch during auto-rotate, which is
the bug `applyWheelZoom`'s header says it fixed, re-entered through the other
door.

Fix shape: make the spin phase a VALUE rather than a place read twice — resolve
`(base ref, nowMs) → spinElapsedMs` once, above the replay, and let both the
replay and `advanceEpochs` take it. The equivalent: advance the real row before
the replay and let the winner gate only the RESET, never the read. Either
removes the discard, and with it the comment at `replayInput.ts:236-237` that
exists to teach the discard. Golden-trace re-record likely — this moves a phase
the `driverGoldenTrace` fixtures sample.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# World-arm wheel burst has no per-frame turn clamp

**Raised:** 2026-09-10/11, F1 fix rounds on PR #647 (`f1-fix1-report.md` concern
1; `f1-decay-review.md` finding F2). User ruled: backlog, not this PR.

The body arm's zoom settle prices its per-notch turn on `u = |ln
spentZoomFactor(factor)|`, where `spentZoomFactor` (`src/utils/camera/spentZoomFactor.ts`)
folds a frame's wheel events through a `[0.5, 2]` altitude-step clamp before
the turn is computed — a burst can never buy more settle than it bought zoom.
The world (absolute) arm has no equivalent clamp: `u` is the raw folded
`logZoom`, unbounded.

## Where

- `src/data/camera/orientDecay.ts:7-8` still documents the no-whip property
("a reference move beyond `rideBoundRad` in ONE notch is unauthored") — true
of the ride, not of the decay.
- `src/utils/camera/orientStepRad.ts:12` caps at `capRadPerLogZoom · u` with no
ceiling on `u` itself; `src/services/engine/camera/levelledPose.ts:32` same.
- `src/services/engine/camera/replayInput.ts:161,213` pass the raw folded
factor into the world-arm path; nothing clamps it before
`frameAlignedRoll`/`orientStepRad` see it.
- `zoomedDistance.ts:17-35` clamps only at the collision/recession envelope,
which does not bound a large in-band burst.

## Measured consequence

`f1-decay-review.md` F2: a 20-event burst that folds to `e²` gives
`frameAlignedRoll` a `u = 2.0` cap — a 115° one-frame roll, versus the
pre-change bound of `rideBoundRad + capRad ≈ 0.4` rad. The standing "Δscreen-up
never exceeds 23° across 16,000 fuzzed notches" guarantee (H4) no longer holds
on the world arm.

## Why the obvious fix doesn't transfer

The body-arm util (`spentZoomFactor`, an eye-distance ratio) is not reusable
here: the world arm's zoom factor applies to **altitude above the pivot's
surface** (`zoomedDistance.ts:17-35`, `distance′ = radiusMpc + (distance −
radiusMpc)·factor`), not to `distance` itself, and which quantity is right
depends on `pivot.radiusMpc` — a fact only the caller (`replayInput`, three
call sites) has. `frameAlignedRoll` cannot derive `u` from its own arguments;
deriving it correctly needs a pivot-aware `spentDistanceFactor(pre, post,
pivot)` util threaded from those three sites — a larger artifact than the
current single threaded parameter, and it was explicitly flagged for a
controller ruling rather than implemented in the fix round.

## Fix shape

Author `spentDistanceFactor(pre, post, pivot)` (mirrors `zoomedDistance`'s
altitude-vs-plain-distance branch on `pivot.radiusMpc`), thread it through
`replayInput`'s three world-arm call sites in place of the raw folded factor,
and drop the now-redundant `Math.abs(Math.log(step.factor))` duplication
(`f1-fix1-report.md` §"Not collected"). Re-verify `driverGoldenTrace.json` —
`f1-fix1-report.md` measured `displayed[6]` moving at the 1e-17 level under the
naive distance-ratio approach, so the correct pivot-aware form needs its own
fixture check.
51 changes: 51 additions & 0 deletions docs/backlog/2026-09-11-camera-arm-entry-adopts-arriving-tilt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Body-arm entry doesn't adopt the arriving pose's tilt

**Raised:** 2026-09-10/11, orientation-unbraid design pass on PR #647 (design B7).
User ruled: backlog, not this PR.

A pose that _arrives_ at the body arm with more tilt than the arm's own mapping
implies — a fly-to, a clip end, a body switch that wiped the tilt memory — does
not get adopted into `SurfaceMemory`'s remembered tilt on entry. `display =
remembered × bodyUpWeight(h/R)` (ruling 12) reads only what got explicitly
written by a drag or the decay; an arriving pose's actual tilt is not one of
those writes.

## The symptom

A body-arm disengage that started from an unbacked tilt pops by as much as 37°
on the frame it hands off. `orientation-unbraid-design.md` §4 B2 and §5 T-G
(`.superpowers/sdd/2026-09-01-camera-pivot/orientation-unbraid-design.md:405-410,545`)
name this the standing "unbacked tilt pops at disengage" symptom, present
before this branch and explicitly _unchanged_, not worsened, by the branch's
orientation work.

## Second route (drag-authored, near-disengage)

`docs/superpowers/specs/2026-09-01-camera-pivot.md` §6 (lines ~465-479)
documents a second way to reach the same unbacked state: a tilt drag authors
display tilt directly, at any altitude; above `zeroHR` `unmappedTiltRad`'s
`w > 1e-6` guard skips the write back to `remembered`, so the drag-authored
tilt is unbacked even though it was interactively set. Held at the flip, it
crosses onto the absolute arm as roll/tilt — "the B7 symptom's second route."

## Why B7 (adopt-on-entry) is not the fix

Design B7 (`orientation-unbraid-design.md:424-428`) considered making arm
_entry_ adopt the arriving pose's tilt into `remembered`, which would make the
pop unrepresentable. Rejected for this pass: it clobbers a kept memory
whenever the world arm was not itself projecting the tilt (focus null,
`pivotsOnFocusedBody` false) — a case that needs its own ruling on what
"clobber" should mean before B7 can land safely.

## Readiness note

T22's feel gate (post-branch) attested no visible pop at the shipped tilt-band
defaults (`0.45`/`0.9`). The defect is real and reachable (both routes above),
but not observed at current tuning — readiness is `deferred`, not `ready`.

## Fix shape

Rule the "focus null / non-projecting world arm" case first, then let entry
adopt `remembered := display-implied tilt from the arriving pose` only when
the world arm was projecting it. Needs a design pass before a plan; not a
mechanical fix.
Loading
Loading