Phase B: routing domain core#5
Merged
Merged
Conversation
- Define LatLon interface and sailing domain types (Rig, Board, LegKind, ManeuverKind) - Implement Settings with defaults (depth, speed, penalties) - Add complete domain models: PolarTable, WindGrid, Leg, RigResult, Plan, Harbor, MaskMeta - Implement spherical geo helpers: angle normalization, haversine distance, bearing, destination point calculation - Add cross-track and along-track distance functions for isochrone routing Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
…rd convention - Update Leg.twaDeg comment to clarify >= 0 starboard, < 0 port (head-to-wind edge case favors starboard) - Add crossTrackNm test: verify sign convention (north-of-eastbound-track negative, south positive) - Add alongTrackFraction test: verify behavior on segment (0..1) and extensions (<0, >1) - Format geo.ts with prettier (no behavior change) All tests passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements Polar class with bilinear interpolation over TWA/TWS grid, TWS clamping/scaling, no-go zone taper, and beat/gybe angle lookup. Test fixture (TEST_POLAR) and comprehensive test coverage included. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
Implements bilinear spatial + linear temporal interpolation for wind grids, with u/v vector component handling to correctly wrap across 0°/360°. Includes fixture helpers (uniformWindGrid, makeWindGrid) and comprehensive test suite covering uniform fields, direction wrapping, temporal interpolation, and spatial/temporal clamping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…napping Implement NavMask class with Amanatides–Woo grid traversal for segment collision tests, depth queries with safety depth thresholds, and expanding ring search for snap-to-navigable. Cells outside bbox and with depth=0 (land) are non-navigable; 255→25.4m; other bytes→depth/10. Constructor uses explicit fields per erasableSyntaxOnly constraint. Fine-grid test for snap verifies 300m radius behavior within floating-point precision limits of coordinate indexing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
- Add toRad import to mask.ts and use both latitude and longitude cell dimensions in snapToNavigable maxRing calculation. At high latitudes, longitude cells narrow (cos(lat) factor) and become the limiting axis. The old lat-only bound under-searched the narrower axis. - Add regression test for narrow longitude cells at 54.75°N: 1500 m search should find water at col 155 (east edge of col 150 is ~1450 m away). Old code capped at ring 4 = col 154 due to lat-only cell height. - Delete unused testSnapMask fixture (dead code with wrong comment). - Simplify fine-grid snap test: use makeMask(fn, meta) instead of hand-building NavMask inline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
Implement maneuver detection module for isochrone routing: - boardOf: derive sailing board from signed TWA (≥0 → starboard, <0 → port) - boardForCandidate: handle ±180° ambiguity by inheriting parent board - classifyManeuver: distinguish tack vs gybe by turn angle (|prev|+|next| ≤ 180 → tack) Tests cover edge cases: head-to-wind (0° starboard), dead-run board inheritance, mixed tack/gybe classification, and turn-through-wind rules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… minor solver cleanups Applies four review fixes: - FIX 1: Strengthen island test with proper geometry (lat 54.60 origin/dest, requires 24.3 nm detour through gap at lat 54.75–54.80) - FIX 2: Make frontier-cap comparator 3-way (handle a==b case with proper 0 return) - FIX 3: Replace Math.min with spread-array to plain loop (no array allocation, no arg limit) - FIX 4: Add comment clarifying pruning heuristic for death-counter classification Observed island test: maxLat=54.7528, distance=24.28 nm (within bounds 54.74 and 20–30 nm) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements post-processing pass for merging consecutive sail/motor legs when: - Same kind and board, no maneuver at joint - Heading difference ≤ 10° - Merged segment passes segmentNavigable check - For sail legs, merged heading preserves board at the joint Iterates to fixpoint. TDD: 3 test cases (dog-leg merge, barriers to merge, land-clipping detection) all passing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
Implements planRoute with TDD: snap origin/destination to navigable cells with 500m radius; build WindField; run solve for each rig with independent polars and performance factors; postprocess mergeCollinearLegs; compute totals (maneuverCount, motorDistanceNm, durationMs); recommend faster ETA (ties → genoa; single survivor → that rig); both-fail → genoa's reason. All 4 tests passing: - beam reach with genoa/fock recommends faster - snap handles land edge cases - progress callback fires for each rig - snap-failed-origin propagates correctly Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
- Remove explicit 500m radius from snapToNavigable calls in planRoute.ts so the 300m default (per spec) applies consistently - Update test fixture origins/destinations to cell centers to ensure they snap successfully within 300m (test grid cell centers are ~300-320m apart; test points at arbitrary decimals would miss by floating-point rounding) - Updated shared req object to use cell centers (54.7525, 10.0025) and (54.7525, 10.4025) instead of (54.75, 10.0) and (54.75, 10.4) so all route-planning tests pass with stricter snap radius - "snaps origin off land" test: origin now (54.7525, 10.2095) ~192m west of water col 162; destination now (54.7525, 10.6025) at cell center - Deep-inland test unchanged; still correctly returns snap-failed-origin 4/4 tests + lint + typecheck pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WC1Dw7ZLH3Az7TY73Q2htL
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
commented
Jul 15, 2026
DocGerd
pushed a commit
that referenced
this pull request
Jul 15, 2026
… Leg-union task per user decisions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…verage batch Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
CI note: the first run failed with 10 pure-timeout failures (no behavioral failures) — the Actions runner executes the isochrone solver ~6-10× slower than dev machines. The whole-branch review had triaged the timeout policy as "keep per-test explicit timeouts only", calibrated on local timings; CI evidence overturns that at per-file granularity. Landing: |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 15, 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.
Summary
Phase B of the implementation plan: the complete routing domain core — 13 tasks (B1–B13), each implemented and reviewed via subagent-driven development with per-task review loops, closed by a whole-branch review (final verdict: ready to merge).
planRouteruns genoa + fock, recommends the faster; genoa on ties)RoutingClient: pure testable handler, transferred (not copied) mask buffer, per-rig progress throttling, promises settled on fatal/dispose (incl. dispose-race guard)Test evidence
59/59 tests across 16 files (~209 s incl. property suite),
typecheck+lintclean at a3a71a2. CI runs the same battery.Notable review-driven hardening
Per-task reviews ran with adversarial fix loops; several defects originated in the plan's own code listings and were fixed additively: property-suite vacuous-pass guard (B10), CI-breaking unused import + empty-route guard (B11), promise settling on global fatal/dispose + dispose-race flag (B12), two borderline solver-test timeouts made explicit.
Deferred with named owners (tracked in the SDD ledger)
MaskMetametadata fields (encoding/verticalDatum/sources) whenmask.meta.jsonis first parsed;Planis structured-clone-safe but NOT JSON-safe (documented on the type) — Garmin export (Garmin Boating (ActiveCaptain) route sync — import, and basic push of updates #3) needs a dedicated serializertMsmonotone per rig at via jointsRefs #4
🤖 Generated with Claude Code