Skip to content

Boats & sails: support multiple boat types (Skipperteam Flensburg fleet) with per-boat selectable foresail inventory #54

Description

@DocGerd

Motivation

The app currently models exactly one boat — the Salona 45 from pipeline/polars-source.json — with exactly two foresails baked into the type system (Rig = 'genoa' | 'fock', app/src/types.ts:6). Crews sailing with Schoenicke SKIPPERTEAM (yachts based at Marina Im Jaich, Flensburg — exactly this app's routing area) sail a mixed fleet, so the planner should support selecting the boat you're actually on, and the foresail inventory that boat carries.

Part 1 — More boat types (Skipperteam fleet)

Fleet per skipperteam.de/toerns/yachten (14 own yachts):

Model Boats
Bavaria Cruiser 51 KARIBU
Jeanneau Sun Odyssey 519 RUBIN, TOPAS
Beneteau First 47.7 SKIATHOS, SUNRISE
Beneteau 50.5 (as listed) QUEEN F.
Beneteau Oceanis 473 SPIRIT, ANDROMEDA, SAPHIR
Salona 44 SPEEDY GO!, EASY GO!
Elan Impression 45 BARRACUDA
Elan Impression 444 PIRANJA
Grand Soleil 46 MARIN

Notes:

  • Not all 14 are Baltic-based (e.g. SKIATHOS sails the Atlantic) — during grooming, confirm which models are actually stationed in Flensburg and prioritize those.
  • A boat type is more than a polar: it carries draft (feeds the safety-depth relationship — today's 2.1 m draft is a hard-coded domain rule and the relaxation floor proposed in Router: don't fail hard when a route only exists below the requested safety depth — find it and warn (e.g. Flensburg→Marstal at default 3.0 m) #53), motor speed (today's 6.5 kn default), and per-model maneuver characteristics (tack/gybe penalty default 45 s).
  • Polar sourcing should follow the existing pattern: ORC certificates / VPP data per model where available (build_polars.mjs documents the Salona 45 derivation), with the same validation gates — note the current sanity anchors and the v < 12 kn plausibility bound are Salona-45-specific and must become per-boat.

Part 2 — Multiple foresail types, selectable

Generalize the hard-coded genoa/fock pair into a per-boat sail inventory the user selects from (e.g. ~135% genoa, ~110% fock/jib, self-tacking jib — whatever the boat actually carries; white-sails scope stays, no spinnaker legs).

Touchpoints this implies (from current code):

  • Rig = 'genoa' | 'fock' (app/src/types.ts:6) → data-driven sail identifiers; PolarTable.rig follows.
  • Plan hard-codes genoaReason / fockReason (app/src/types.ts:118-119) and recommended: Rig → becomes a per-sail result list. IndexedDB schema migration needed so existing saved plans still load (Plan must stay structured-clone-safe).
  • The router currently runs twice per plan (genoa + fock polars) and recommends the faster rig — with N foresails that's N solver runs. Either cap the comparison to the user's selected subset (e.g. pick 1–2 sails to compare per plan) or make the run count explicit in progress reporting; worker-time budget matters.
  • Static assets: app/public/data/polar-genoa.json / polar-fock.json → per-boat, per-sail files; all committed and precached (offline planning of the polars themselves must keep working — only wind needs network).
  • UI: boat picker + sail inventory in settings/planner; all labels through both i18n dicts (de/en).

Constraints

  • Boat/sail selection is a user setting resolved at plan time — like safety depth, it must never require regenerating data assets, and a saved plan must keep rendering with the boat/sails it was computed with.
  • The design spec (docs/superpowers/specs/2026-07-14-sail-command-design.md) currently fixes the Salona 45 and the two-rig comparison — this feature needs a spec addendum (main-session edit, user-approved) before implementation.
  • Polar honesty: each new polar must carry its source note (certificate / VPP estimate) like the existing ones — user-facing copy must not overstate calibration.

Acceptance criteria

  • User can select a boat type; at least the Flensburg-based Skipperteam models above are available with sourced, validated polars.
  • Each boat exposes its own foresail inventory; the user selects which sails to plan with; router compares the selected sails and recommends the fastest, as today.
  • Boat draft drives the safety-depth default/floor; motor speed and maneuver penalty defaults are per-boat (still user-tunable).
  • Saved plans from before the change still load (schema migration) and always render with their original boat/sail/polars.
  • Pipeline validation (build_polars.mjs) generalized: per-boat sanity anchors, no Salona-45 constants applied to other hulls.
  • de/en i18n complete; spec addendum merged first.

Related: #53 (safety-depth relaxation floor = boat draft — becomes per-boat with this issue).

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: routingIsochrone solver, polars, mask queriespriority: mediumPlanned, not urgenttype: featureNew user-visible capability

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions