Skip to content

Maintain prepared endpoints through walker movement - #65

Draft
apotema wants to merge 10 commits into
feat/endpoint-preparation-phasefrom
feat/prepared-distance-queries
Draft

apotema wants to merge 10 commits into
feat/endpoint-preparation-phasefrom
feat/prepared-distance-queries

Conversation

@apotema

@apotema apotema commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Provide pure prepared distance/origin queries and bounded pending-only preparation. Ordinary endpoints require current CMNs; MovementNodes use current graph registration. Queries never resolve nearest nodes, repair endpoints or fall back to Euclidean distance.

Following the failed downstream performance review, graph walkers now publish current endpoints from their known route segments after movement, so ordinary movement no longer requires a nearest search. Direct walks refresh the nearest anchor after movement, reusing a previous winner only inside its proven nearest-node region; tracked attached items inherit the validated anchor. External movement, hierarchy changes and graph changes still invalidate immediately. Route anchors on long authored edges are accepted only after validating the point against the current segment; initial approach/direct placement retains the ordinary snap radius and vertical eligibility policy.

Static/unexplained pending endpoints use the deterministic bounded queue. Optional worker execution uses immutable snapshots and a whole-batch same-frame join; serial remains the default. Existing legacy query APIs remain separate.

Published integration prerelease: v4.3.0-rc.2 (requires engine v4.2.0-rc.1). These are installable release candidates, not performance acceptance. CI pins engine 21ea60c. Validation: 13 unit tests, 81 specs, 33 real-engine integration tests and WASM compile check pass; CI passes. Coverage includes low-fps walking without routine searches, long links, carrying/drop, direct movement, graph changes, fairness/cancellation and serial/worker publication equivalence.

Stacked on #64. Downstream #1048 now indexes destination candidates and shares scans across sources on the same prepared node. Revised serial performance and device/gameplay acceptance remain pending; the previous revision failed performance acceptance.

The optional registration array now falls back to the canonical registration map when unavailable, without forcing graph rebuilds. Regression coverage includes switching direct-mover anchors within the old snap radius, carried items, and unchanged graph epochs with a missing fast index.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@apotema

apotema commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Build break for the web target, from measuring moca-tecnologia/flying-platform-labelle#1048 on this branch: src/nav/controller.zig:212 declares var next_preparation_world = std.atomic.Value(u64), and wasm32 has no 64-bit atomics — labelle build --platform=wasm fails in std/atomic.zig:53 ("expected 32-bit integer type or smaller; found 64-bit integer type"). The controller runs single-threaded, so a plain u64 counter (or std.atomic.Value(u32)) is enough. This comes from the #64 commit, so it affects the whole stack.

Android emulator numbers for the stack are on the FP PR; wasm numbers will follow once this builds.

https://claude.ai/code/session_01LpszrcSrLLijQcxxvgWyUQ

@apotema

apotema commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Re-measured with d11e297 in Flying Platform big_colony (both platforms now build; details on moca-tecnologia/flying-platform-labelle#1048):

  • Nearest-node reuse is frame-rate dependent. wasm at 60 fps: ~50 of each 128-entity batch reused, searches 128 → 78 per frame. Android emulator at ~8 fps: 0–3 reused per batch (0 in 1 349 of 1 869 frames) — a walking worker moves further per frame than the reuse radius, so the certified region never matches. The SM-T505 (15–25 fps) will land between. Worth documenting as "saves searches when per-frame displacement < radius", not as a fixed gain.
  • Preparation cost per frame (prepareEndpointBatch + publishEndpointBatch, 12 domain views, budget 128): wasm 0.59 / 0.73 / 0.78 / 1.56 ms (median / p95 / p99 / max), emulator 0.48 / 1.4 / 2.2 / 4.6 ms — every frame, because discovered is ~3 200–3 800 participants per frame and the 128 budget is consumed every frame by movers that are invalidated again on their next step (pending ≈ 180–195, never drains).
  • The indexed freshness / registered_entities changes did lower the per-query cost: the scheduling regression on the emulator went from +26 % to +20 % at p99, on wasm it is +11 %. Still above the position-cache path it replaces.

The lever that would change the numbers is in the game's participant selection (static entities on creation/graph change only; idle workers when they stop), not in this plugin — but a prepareEndpointBatch that accepted "pending only" iteration without walking every participant would make that cheap to do.

https://claude.ai/code/session_01LpszrcSrLLijQcxxvgWyUQ

@apotema apotema changed the title Add pure prepared-distance queries and lifecycle retry gates Complete prepared endpoint queries and deterministic worker execution Oct 15, 2026
@apotema

apotema commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Final numbers for 944d754 (with engine 2c4b0c17, game 5c5144da), serial vs preparation_worker, on moca-tecnologia/flying-platform-labelle#1048 (latest comment). Plugin-side summary:

  • The pending queue works: discovered is now ~170–200 per frame instead of ~3 700, domain discovery only on membership changes. wasm preparation+publication 0.31 ms median / 0.56 ms p99 per frame (was 0.59 / 0.78).
  • The batch is still full every frame (selected = 128, pending ≈ 180–200, up to 2 837 after a graph rebuild): movers invalidate every frame. On the emulator (~8 fps) that is 128 nearest searches per frame (reuse 0); on wasm reuse covers ~50 of 128.
  • Worker execution ran in every frame on wasm (worker_executed 15 477/15 477) and was a wash there; on the 4-vCPU Android emulator it doubled preparation cost (p50 0.43 → 0.84 ms, p99 2.5 → 4.4, max 8.7) and the game ran ~27 % slower, i.e. the join stalls the frame. Serial default is right.
  • preparedDistance is still +12 % (wasm) / +22 % (emulator) at p99 on the scheduling pass versus the position-cache path.

https://claude.ai/code/session_01LpszrcSrLLijQcxxvgWyUQ

@apotema apotema changed the title Complete prepared endpoint queries and deterministic worker execution Maintain prepared endpoints through walker movement Oct 15, 2026
@apotema

apotema commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Round 4 with 4.3.0-rc.1 (game 31ca14b3, engine 4.2.0-rc.1), serial, no local overrides — full tables on moca-tecnologia/flying-platform-labelle#1048. Plugin-side:

  • Walker-published endpoints empty the preparation queue: pending ≈ 2 per frame (was ~190), selected/searches ≈ 2, preparation+publication 0.13 ms median on the Android emulator / 0.08 ms on wasm (was 0.43 / 0.31).
  • The cost moved into advance (publication on every mover every frame): emulator [pa] advance 0.37 / 1.4 / 2.4 ms (med / p95 / p99), the pathfinder row is now the costliest untracked script in 833 of 1 780 frames (med 1.06 ms); wasm advance 0.38 ms median. Net per frame is still clearly positive on the tail (all script rows p99 16.9 → 12.3 ms on the emulator) and a wash at the median on wasm.
  • preparedDistancesFrom + the per-node memo: destination lookup 111 → 11 µs on the emulator, memo hits 261/frame. Scheduling p99 9.5 → 3.8 ms (emulator), 1.09 → 0.75 ms (wasm).

https://claude.ai/code/session_01LpszrcSrLLijQcxxvgWyUQ

apotema commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Review candidate for PR #65. Direct movers refresh their nearest endpoint after each movement step, with conservative reuse only while the nearest node remains proven. The optional registration index falls back to the canonical map if unavailable.

Validation: unit/spec/WASM compile checks pass; 33 integration tests pass, including nearer-node crossing with a carried item and missing-index graph stability. No new performance measurements.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant