fix(routing): stamp visited only for frontier-cap survivors (#67)#94
Closed
DocGerd wants to merge 1 commit into
Closed
fix(routing): stamp visited only for frontier-cap survivors (#67)#94DocGerd wants to merge 1 commit into
DocGerd wants to merge 1 commit into
Conversation
The isochrone solver stamped `visited` over ALL byKey ring winners BEFORE the MAX_FRONTIER truncation. A node that was stamped and then capped out never expands, yet its visited-stamp permanently prunes every later arrival into that same prune cell (visitedDominates: stamp no later AND no more maneuvers). When a sole gateway cell's first arrival is the capped-out one, the cell is sealed with no surviving subtree, so a still-connected destination is reported unreachable — routes can be missed under a large frontier (filed out-of-scope from PR #66). Move the stamp to after the cap slice, stamping only the survivors (recomputing each survivor's pruneKey). Survivors always expand next ring, so every domination guarantee is preserved; capped-out nodes no longer seal cells they never grew a subtree in. When the frontier fits under the cap, the survivor set equals every byKey winner, so the uncapped path — every real-mask route whose frontier peaks below MAX_FRONTIER — is byte-identical to before. Expose `maxFrontier` as an injectable SolveParams field (defaults to MAX_FRONTIER) so a regression test can drive the cap into the truncating regime without a 30 000-node mask. New test builds an H-shaped water region with a dead-end decoy gulf pointing at the destination and a sole through-corridor: under small caps stamp-before-cap sealed the corridor and returned 'unreachable' for a provably connected destination (mask.cellsConnected), while the fix returns a valid detour route at every cap tested. realmask.repro wall-time unchanged (~90 s; real routes never hit the cap). Closes #67 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner
Author
|
Self-review (pre-merge) — ran a multi-lens adversarial review of this diff. Verdict: clean, no findings → no review threads to post/resolve.
Merging to |
Owner
Author
|
Merged into develop: fix commit 677095a landed via merge commit 83e2ecf (Merge pull request #94), which is now the develop tip. GitHub returned a 504 Gateway Timeout during the merge, so the ref updated correctly but the PR merged-flag, branch auto-delete, and the Closes #67 auto-close did not fire. Reconciling manually: closing this PR (the change is already in develop), deleting the branch, and closing #67 with a reference to the merge commit. |
This was referenced Jul 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The isochrone solver's
solve()stampedvisitedover allbyKeyringwinners before the
MAX_FRONTIERtruncation. A node stamped and then cappedout never expands, yet its visited-stamp permanently prunes every later arrival
into that same prune cell (
visitedDominates: stamp no later AND no moremaneuvers). When a sole gateway cell's first arrival is the capped-out one,
the cell is sealed with no surviving subtree, so a still-connected destination is
reported
unreachable— routes can be missed under a large frontier. Filedout-of-scope from PR #66.
Fix
Stamp
visitedafter the cap slice, over the survivors only (recomputingeach survivor's
pruneKey). Survivors always expand next ring, so everydomination guarantee is preserved; capped-out nodes no longer seal cells they
never grew a subtree in. When the frontier fits under the cap, the survivor set
equals every
byKeywinner, so the uncapped path — every real-mask route whosefrontier peaks below
MAX_FRONTIER— is byte-identical to before.MAX_FRONTIER's value (30 000) and the domination rule are unchanged.Test seam + regression
SolveParams.maxFrontier(defaults toMAX_FRONTIER) so a testcan drive the cap into the truncating regime without a 30 000-node mask.
isochrone.followups.test.ts— issue Solver: visited stamps written before the MAX_FRONTIER cap can seal prune cells with no surviving subtree #67 block: an H-shaped water region with adead-end decoy gulf pointing straight at the destination and a sole
through-corridor. Under small caps
stamp-before-capseals the corridor andreturns
unreachablefor a provably connected destination(
mask.cellsConnected— an independent BFS oracle, not the solver); the fixreturns a valid detour route (all legs navigable; summed length > the blocked
direct line) at every cap tested
[11, 12, 16, 17, 22].stamp-before-cap(fails at cap=11,no-routevsok) and GREEN on the fix; the four issue-Solver follow-ups from #20 review: time-aware visited pruning; direct-arrival substeps; capture-hop validation #21 tests areunaffected either way.
Verification
npm --prefix app run typecheck— cleannpm --prefix app run lint— cleanisochrone(unit + followups) — 12/12 passrealmask.repro.test.ts— 6/6 pass; wall-time 87 s (pre-fix) vs 91 s (fix),within run-to-run variance (real routes never hit the cap → the fix is a no-op
there)
invariants.property+planRoute+motor+postprocess+viaPoints+relaxedDepth— 35/35 passCloses #67