Commit Graph

1152 Commits

Author SHA1 Message Date
Maurice 55c6bad295 fix(assistant): readable cards for any tool, and the CI round
- A card for a tool without one of its own names what it works on: the place
  of an assignment, the day, the note, the booking; its arguments sit under
  the labels the other cards use, a new order reads as the names in order.
  28 frequent tools carry their own action name in every language.
- The server's own steps without a card still refuse, as before.
- Tests: the rate limit is asked one request after another, the mention list
  closes on the caret move a browser reports, the place factory hands its
  category over as a plain id, eleven files formatted.
2026-10-21 22:29:59 +02:00
Maurice e5f1f573c5 feat(assistant): every MCP tool of TREK, the core first and the addons on request
The assistant offered 25 hand-picked tools. It now offers every tool the MCP
registry holds, but a short denylist with a reason per tool (deleting the
trip, share, invite and feed links, membership changes, fetching an address
the model picks, documents to the provider, instance-wide categories and
holidays). The scopes widen to every addon but trips:delete, the share
scopes, file contents and plugins.

- A tool annotated read-only runs at once, every other one waits as a change
  card; reads that fetch a planted address wait too.
- A turn starts with the trip's core tools; use_addon_tools loads tours, road
  trip, journey, vacay, atlas, collections, trip chat, files, account or
  plugin tools for the rest of the turn.
- A writing tool without a card of its own gets one from its arguments:
  add, change or remove, the part of TREK it works in, the trip's days,
  places and people named.
- The boot check fails on a tool that shares, invites or publishes and is
  neither denied nor known to only take back.
2026-10-21 22:29:59 +02:00
Maurice a1f97ba548 style: format two ai-connections files 2026-10-21 22:29:58 +02:00
Maurice 3c3a228737 fix(assistant): lint, types and tests for the CI round
- agent loop and decisions without await in loops: steps as recursion,
  calls and applied changes in order through one promise chain, the
  independent reads and claims in parallel; part mapping in its own helpers
- place card, header and weather strip without refs or components created
  in render; long hooks and components split; state reset during render
- trip notes only in a turn's tool session, never an undo's; map view
  reported only while the assistant can run
- place strip scrolls through the strip's own smooth scroll, no physical side
- tests follow the raised limits and the new card fields; toasts read through
  a mocked useToast instead of a window global; baselines lowered where the
  counts fell
2026-10-21 22:29:58 +02:00
Maurice b6f7488591 fix(assistant): green CI round, trip notes the assistant can keep, calmer change cards
- the assistant keeps facts for a trip (remember_for_trip) as a change card,
  applied into the user's trip notes; existing expenses can be changed
- change card fields as a few icon chips with the rest folded under Details,
  the day band marks only what changes, the week grid as compact tiles
- no map button on receipts of things that have no place on the map
- tests follow the composer's accessible name, the receipt's status icon and
  the GL map's container; the hanging stream helper honours an early abort
- Postgres probe: qualified columns in the upsert updates; entities restored
  to the generator's output; strict ratchet reads the SDK client as untyped
2026-10-21 22:29:57 +02:00
Maurice 26df56d76c feat(assistant): one AI tile for assistant, parsing and MCP, a planner-aware chat
The three AI addons share one tile in the integration column; a dialog holds
the connection, the assistant's limits and AI parsing, which now reads with
the assistant's connection unless an install keeps its own. Existing parsing
configs stay where they are and keep winning.

The chat sees the map on screen, offers next questions under an answer,
shows its live step in the window head, previews waiting changes inside the
day list, checks whether a day still works, folds a multi-day plan into a
week grid, keeps per-trip notes and can estimate costs. An empty or cut-off
answer gets one push instead of ending silently, and the assistant's own
step and size limits are raised.
2026-10-21 22:29:57 +02:00
Maurice 214984f398 feat(assistant): trip assistant chat with your own AI connection
Adds an in-app assistant that reads and plans the open trip. Users connect
their own provider (Sign in with ChatGPT, or an Anthropic, OpenAI, Gemini or
OpenRouter key) or use the one the admin set up, including a custom
OpenAI-compatible server. It is an addon and only shows while a connection
is usable.

The chat runs on the existing MCP tools, pinned to the trip. Every change is
held as a change card the user applies or discards, and applied changes can
be undone. On the desktop it is a floating window that can be moved and
resized; on the phone it is fullscreen. Search results show as place cards
that can go onto a day, weather lookups as a strip, proposed places as
ghost markers on the map with a before/after switch, and an answer can be
shared into the trip chat. Booking files can be dropped in and are read
into change cards.
2026-10-21 22:29:56 +02:00
Maurice 3101b36f3d fix(journey,tours): read a day's stays as a read, give the services the request context in the e2e runs and hold the tests to the foot-only walks and the translated notices 2026-10-21 21:16:36 +02:00
Maurice 5dc945145f fix(journey): keep the journey page and maps within their lint gates, format the journey locales 2026-10-21 20:00:07 +02:00
Maurice d62fc284e1 fix(journey): share the stays of a day report only with the recorded days, and hold the share read tests to the two new flags 2026-10-21 19:08:25 +02:00
Maurice 9b71948497 feat(journey): read what a journey map shows beside its entries, and share its plan and recorded days on purpose
GET /api/journeys/:id/map-layers answers the planned places of the linked
trips (one per place, with every day the plan puts it on and the entry
written about it), the trips' GPX routes while the track switch is on, and
the located stays entries were taken over from. It goes through
journey_trips, so a trip nobody wrote about yet shows its plan too. Every
source is read on its own: one that fails says unavailable and the others
still draw. A position that is not one is left out, never moved.

GET /api/public/journey/:token/map-layers cuts the same on the server by the
link: nothing while the map is not shared, the plan and its routes only with
share_plan, the recorded days only with share_recordings and only beside a
published entry, stays only of published entries. Migration 046190 adds both
flags to journey_share_tokens, off by default, so a journey that is public
already shows what it showed before. The share link REST route and the MCP
share tool take them alike; get_journey_map_layers reads the layers over MCP
on the same access, a route by its size only.
2026-10-21 18:15:24 +02:00
Maurice bb10b94046 fix(journey): mark only the days a run reaches as prepared, name trips out of reach, show editors their own status in the day review and say when a fetch is already running 2026-10-21 17:53:39 +02:00
Maurice 94c369b913 feat(journey): prepare the recorded days of a journey's trips in the background, switched on per journey, with each day's state
Journey Settings get "Prepare recorded days automatically" under GPX Tracks,
off by default and set by the owner. With it on, the Dawarich background
check also reads the linked trips of the journey for the owner and every
editor with a connection of their own, within the same 400 day reach and
windows as the trip sync, and only for new and changed stays: a trip the
person travels on is not asked twice, and the stays of a trip they are not
on are filed under no trip. Nothing is written into the journey; the stays
wait at their day, and a stay taken over or left out is never offered again.

Under the switch the caller sees when their stays were last fetched, or why
not (no connection, no dated trip, background check off, the reason of the
last failure beside the state still shown), with a "Fetch now" that the
switch also runs when it is turned on. A day of the trips that is not final
yet carries a dot on its take-over button, on the phone in the stay sheet,
and the tooltip says whether it is still running, was fetched before it
ended, was not fetched yet or shows an older state after a failed refresh.
The day review says so for today too.

Server: migration 046180 adds journeys.auto_prepare_recordings and
journey_recording_prep (the last attempt, last success and last error per
journey and person). JourneyRecordingPrepService in the journey domain lists
the prepared trips and keeps the status; DawarichSyncService walks those
trips in the same loop as the person's own and records each journey's
outcome. GET /api/journeys/:id/recordings/prep answers the caller's status
under a @trek/shared contract, the MCP tool get_journey_recording_prep reads
the same, and update_journey takes auto_prepare_recordings like the PUT
contract. The column stays out of the plugin output. All 27 locales carry
the strings; the Journey, Dawarich and MCP tool wiki pages describe it.
2026-10-21 17:39:33 +02:00
Maurice 3f1d1a57cc fix(journey): take a day over from the phone stay sheet, keep a picked position inside its group and send stays without a place unpinned
A journey on a phone with stays on days that hold no entry offers each waiting day in the Dawarich stay sheet, so a day can be built from its stays alone; desktop and phone read the same days through openStayDatesWithin. A position picked for a group falls back to the longest stay with a place once that stay is unchosen or split off, a merged entry of stays without a position goes out unpinned instead of being refused, and a stay taken over meanwhile is never joined across. review_journey_day_stays is no longer marked read-only, since it refreshes the caller's stored stays. The already-taken notice no longer claims the stays were in this journal. An e2e case checks the drafts stay off the public share.
2026-10-21 16:49:47 +02:00
Maurice 7c1de6ed82 feat(journey): take a recorded day over with its stays, chosen, renamed and joined into drafts or one day report
A journey day now has a list button in its header beside the route, with the
number of stays still waiting, and the row of the day's Dawarich stays gets a
"Take over day" button; on a phone the journey's More options offers it for
the day of the card shown. The dialog reads the day again from the caller's
own Dawarich and shows the route and the stays on one map, numbered in the
order they were lived and pinned in the colour of the day, beside a list with
times, durations, the stays Dawarich only guessed or is unsure about, the ones
without a position, the entries the journal has on the day and how many people
will see the drafts. When Dawarich does not answer, it says why and shows the
stays of the last sync.

Open stays start chosen, the same place twice in a row starts joined as one
entry under a title and a chosen map position, any two neighbours can be
joined or kept apart, a name can be corrected and a stay left out. "Day
report" turns every chosen stay into one entry that lists them with their
times, at a chosen stay or off the map. The day's route can go along when the
journey keeps none yet. Nothing is written before "Take over N drafts", and a
blank name or title holds it back with the reason. Stays left out are dismissed
and can be restored in the stay list.

The take-over writes drafts only, never touches what the journal holds, and is
one transaction: every stay is checked to be the caller's and of that day, the
entries, the stays they hold and the marked suggestions commit together, and
the entries are announced after the commit. A stay the journey holds already
is reported instead of written twice, so a retry after a lost answer is
harmless; a failed attempt reads the day again and keeps the choices, and a
route that fails after the drafts can be kept again alone.

Server: migration 046170 adds journey_entry_stays (one row per stay of an
entry with its own name, place and times, unique per journey, source, person
and visit, gone with its entry), owned by the journey domain through
JourneyEntryStaysService. The single stay accept records its stay there too,
over REST and MCP alike. DawarichSyncService.refreshDay reads one day's visits
and upserts them without flagging anything missing; DawarichDayStaysService
lists and settles a day's stays. JourneyDayImportService in journey-recordings
serves POST /api/journeys/:id/recordings/:date/stays/review and /import with
@trek/shared contracts, and the MCP tools review_journey_day_stays and
take_over_journey_day_stays run on the same service and checks. createEntry
can write without announcing for a caller that announces after its commit.

The desktop dialog and the phone sheet of the route (JRN-01) and of the
take-over share one frame each. All 27 locales carry the strings; the Journey
and Dawarich wiki pages and the MCP tool list describe it.
2026-10-21 16:31:40 +02:00
Maurice 5116f7be2c fix(journey): refuse the second of two saves of a recorded day at once unless it asked to replace 2026-10-21 15:18:01 +02:00
Maurice 4f60cc5c98 fix(integrations): cut a journey day in the zone of its own first track, never in the UTC Dawarich writes times in 2026-10-21 15:18:00 +02:00
Maurice 65bb2d95eb feat(journey): keep the route recorded on a journey day from Dawarich, previewed first, with its gaps
A day header in the journey timeline now carries the Dawarich mark for an
editor whose Dawarich answers (dimmed with the reason while offline). It opens
a dialog that names the journey and the day and reads that one local day from
the caller's own connection: a sketch of the route, distance, first and last
time, pieces, gaps without recording, modes and whether tracks or thinned
points answered. The day is cut in the zone Dawarich records in where its
answer shows one, else in this device's, and a picker cuts it in any other.
Nothing is kept before "Keep route". On a phone the same sheet opens from the
journey's More options for the day of the card shown.

What is kept is the preview that was seen: the save reads the day again and
refuses with recording_changed when its hash moved, and the dialog shows the
new version. The same day taken over twice keeps one version and announces
nothing; a different version is replaced only on "Replace", never touching an
entry or text. A failed read throws before any write, so the kept version
stays. Gaps stay gaps: points are split where silence ends elsewhere, and no
day is joined to the one before. The dialog says how many other people will
see the route; editors may keep their own recording.

Kept days show in the day header (distance, whose recording, since when,
still running) and on the desktop and phone journey maps in the colour of
their day's pins, winning over the live trail of their date, and stay drawn
without Dawarich. A trip's GPX track is now drawn dashed as the plan, recorded
routes solid. Shared links do not show recorded days. Remove route asks first
and works without a connection.

Server: migration 046160 adds journey_recorded_days (one row per journey,
local day and source, segments as JSON, zone, window, fetch time, hash,
importer kept past their account). The journey domain owns the rows through
JourneyRecordedDaysService; the new journey-recordings domain takes a day
over through DawarichTracksService.fetchDay, which reads past the cache, asks
a window wide enough for every zone and refuses with DomainErrors. REST under
/api/journeys/:id/recordings (list, preview, PUT and DELETE /:date) with
@trek/shared contracts, and MCP list_journey_recorded_days,
preview_journey_recorded_day, save_journey_recorded_day and
delete_journey_recorded_day on the same service and access. A new user event
journey:recording:updated keeps every open map current. JourneyTrack gains an
optional kind. All 27 locales carry the strings; the Journey and Dawarich wiki
pages and the MCP tool list say what it does.
2026-10-21 15:05:21 +02:00
Maurice cbca3b2851 test(maps): the overpass place names cases get a suite of their own, the service suite back under its size 2026-10-21 13:53:24 +02:00
Maurice 8a75712041 fix(tours): tell a router that did not answer from a walk no way joins, count each boat of a leg on its own into the day and its arrivals 2026-10-21 13:53:21 +02:00
Maurice 6e13f7e655 fix(tours): keep a city walk on foot across the water, mark a crossing by boat apart from the walking time and save it only once taken 2026-10-21 13:16:43 +02:00
Maurice 41f3267503 fix(tours): name every derived field of a walk's remaining time, read a second run on the same day, say when only the way back is left 2026-10-21 12:35:02 +02:00
Maurice 43353b9394 feat(tours): read the walking time left of a city walk on a day over MCP, apart from its stays and the whole walk 2026-10-21 12:16:57 +02:00
Maurice 4eaf5e6a11 fix(tours): read e-bike chargers as mapped, keep unnamed ones on the map search 2026-10-21 12:05:38 +02:00
Maurice 1a722e63fb feat(tours): find e-bike charging points along a bike tour, only those tagged for bicycles, with what they state 2026-10-21 11:58:39 +02:00
Maurice 924e38472e fix(maps): stretches beside the overpass probe wait for it, empty note only after a full search 2026-10-21 11:43:04 +02:00
Maurice bf2efca8f0 fix(tours): let a failed search along the tour really ask again, with a probing breaker, hedged mirrors and a countdown 2026-10-21 11:35:03 +02:00
Maurice e6002444a1 test(server): narrow a partial ways answer the way the server build can, format three tests 2026-10-20 21:56:06 +02:00
Maurice 20537193b6 test(tours): expect ski_options null on non-ski rows, let the ski choice settle before asserting 2026-10-20 21:43:47 +02:00
Maurice 60611aee65 test(server): run sequential awaits through oneByOne instead of a loop 2026-10-20 21:43:47 +02:00
Maurice 562176bd83 chore(db): number the ski options migration after the Tours default 2026-10-20 21:16:09 +02:00
Maurice ae699187f7 fix(tours): open a saved ski tour as saved and keep what its router planned
A saved ski tour now opens with the line, heights, time, build and
planned legs it was saved with, read from the place this device keeps,
and nothing is asked of the piste router until a point, an option or the
discipline changes. Opened offline, while TREK Places is down or with
piste routing switched off, it no longer turns every leg straight, and a
rename saves the planned tour as it was. A kept line whose length is not
the tour's is another save's, and the tour is planned afresh.

- the save names the legs the router planned (ski_planned_legs, checked
  against the tour's own legs); get_tour_plan reports each leg routed or
  drawn from them instead of from the build alone, a tour that names none
  counting as planned only with the router's time
- copies keep ski_options in every path: in the trip, taken over, kept
  in a collection and made from one (collection_tours gets the column in
  the same unshipped migration)
- the route summary shows the data build, the km of runs, the height
  skied down on them and the lift rides, and the credit the server sends
  in place of a fixed one; a tour opened as saved says so
- answers refused while piste routing is off, or planned on an older kept
  copy, are not remembered, so they are asked again later
- terrain heights between known ones are filled in one walk along the line
- i18n: five summary keys in all 27 locales
2026-10-20 21:15:45 +02:00
Maurice 1d72fbd25f fix(ski-routing): keep one job's timeout to itself and bound the area refresh
The router thread now takes one job at a time from a queue kept on the
main thread, so a job's clock starts when the thread takes it up. A job
that times out ends only its own thread, and only while that thread is
still the current one; the jobs in line go to a fresh thread instead of
waiting out their own timers and killing the new thread in turn. A
thread that dies fails the job it held, and a thread that cannot start
fails each job on its own.

- the daily refresh fetches changed areas two at a time and stops once
  the TREK Places breaker opens; the rest keep their older copies
- the winter index cap is on the unpacked JSON fetch hands over, so it
  follows from the host's 8 MB gzip cap (64 MB) instead of matching it
- a build name is read to the rule a ski tour stores it by, so a build
  TREK could not save is refused when the winter layer is read
- an unused import in the piste router contract is gone
2026-10-20 21:15:21 +02:00
Maurice a85a50e186 feat(tours): keep a ski tour's piste router options, build and time
A ski tour now saves the options its legs were planned with and the piste
data build they ran on, as one JSON column (a new migration, since the
ski migrations have shipped on this branch); the server keeps only the
options its discipline reads. Both stay off every other type. The refusal
of a ski time is lifted: the piste router works one out, and the planner
sends it only when every leg followed the piste data. get_tour_plan names
the legs of a tour the router planned as routed, and list_tours says what
the new fields hold.
2026-10-20 21:14:44 +02:00
Maurice c8eb510bd4 feat(ski-routing): plan ski tours on the piste graphs of TREK Places
A new Nest domain plans the legs of a ski and winter tour on the runs,
lifts, cross-country trails, ski touring and winter paths the Places host
builds from OpenStreetMap, one SGR1 graph per ski area. TREK fetches the
index once, downloads an area by its id when a planner first plans in it,
checks it against the index's SHA-256 and keeps it in data/ski; no
clicked point ever leaves the server. A leg whose area is slower than two
seconds answers loading, and an older copy stands in while the host is
down.

The reader decodes the writer's golden file byte for byte, including the
two edge flags it added (area_has_run, type_unknown). The search is A* in
a worker thread over a profile per discipline (alpine, snowboard,
cross-country by technique, touring, snowshoe, winter hike) with the
times and penalties of the cost model, up to five snap candidates per
point, lift stations as entry points, legs that go on from each other,
pieces of a split network stitched by their boundary nodes, other ways by
the penalty method, and a failed leg explained by relaxing one rule at a
time (difficulty, lift class, technique, riding down, uphill) without ever
handing out the relaxed line.

POST /api/trips/:tripId/ski/route, GET .../ski/area-near and
GET .../ski/network/:areaId sit behind Tours and the trip, with
plan_ski_route as their MCP twin. A daily job looks for a new build and
sweeps areas unused for 90 days. The admin switch ski_routing_enabled is
on wherever TREK Places is.
2026-10-20 21:13:48 +02:00
Maurice d924dcf99d fix(tours): keep the Places layers off the search breaker and keep partial way answers
The ways and routes layers now count failures on a breaker of their own, so
busy or slow layer answers no longer send place search to the fallback for a
minute, and a layer's 404 no longer clears the count of search failures.

When a later batch of /v1/ways fails, the batches that answered are kept and
only the ids still open go to the Overpass mirrors.

The wiki, the compose file and the chart now say that the way ids of a planned
tour and the horse routes box go to the TREK Places API.
2026-10-20 21:12:09 +02:00
Maurice 371083ed47 feat(tours): read way tags and horse routes from the TREK Places layers
The trail grades under a tour (BIK-05, MAP-03, MAP-04, RUN-02) and the
horse routes around a map view (HOR-01) asked the public Overpass mirrors.
The places host now serves both from its own planet build: GET /v1/ways
for the tags of ways by id and GET /v1/routes for route relations per box,
each in Overpass element form.

trek-places.client gains trekPlacesWays (batches of 500, one after
another) and trekPlacesRoutes, both through getJson with its off switch,
breaker, timeout, size cap and instance header, each element validated on
its own. TrekPlacesLayersService in the maps domain hands their answers to
the tours services, or why there is none (switched off, layer not built,
failed), and logs that once per layer until it answers again. Both tours
services ask the layer first and fall back to the existing Overpass path;
the responses TREK sends its clients are unchanged.
2026-10-20 21:12:09 +02:00
Maurice eaeaf68352 chore(server): format the tours MCP test 2026-10-20 21:11:44 +02:00
Maurice 5156fa2c0a test(addons): check the Tours migration against the addon table as it was before
The table holds the seeded addons too, so compare against a snapshot taken before the migration with only tours switched on.
2026-10-20 21:11:44 +02:00
Maurice 5580ff71a9 feat(addons): Tours is on for every install
A fresh install gets Tours enabled from the addon seeder. An install that
already has the row is switched on by a migration that runs once, so an
admin who turns Tours off afterwards keeps it off.
2026-10-20 20:56:19 +02:00
Maurice 7f14dde362 fix(tours): tidy the day times of a tour after review
The get_tour_plan description named the estimate of a tour "such a field"
right after the values of a recording, so it could be read as one of them.
It now follows the sentence on derived fields and says outright that it is
one of them, worked out from distance and heights or the pace of a run.

The arrow between a tour's start and end points from the start to the end
in a right-to-left language as well. A tour just added to a day, which
carries no tour_place_id until the next refresh, now says its overrun like
the row above it. A phone row of an import with a total the travellers
entered shows that one time, the estimate joining its words as on the
desktop. The German overrun line reads naturally without an item name.
2026-10-20 20:52:31 +02:00
Maurice c38eb7ebf0 feat(tours): estimate the walking time of imported tours
A hike imported from GPX, KML/KMZ or wanderer had no time at all, so
several hikes on one day could not be planned against each other. It now
gets the walking time the planner times a hike by (DIN 33466), worked out
from its own distance, ascent and descent; a run gets its distance at the
pace entered. Without heights in the track the time is unknown rather
than the time on the flat, and types timed by a router, by a speed of
their own or by nothing get no estimate.

The estimate is derived on every read and never stored, so tours imported
before get it too and no stored or entered time is touched. The rule moved
to @trek/shared, where the planner, the client and the server read it.
The tour list, the day rows on desktop and phone and the tour detail say
it as an estimate, apart from a planned total; list_tours and
get_tour_plan carry it as time_estimate and name it as derived.
2026-10-20 20:52:30 +02:00
Maurice 7291555e95 test(tours): follow the ski tour changes in the tests
The collection tour fixture no longer writes the dropped winter_activity
column, the drafts are saved as version 11, and a hike no longer offers
the pistes layer, which belongs to a ski track only.
2026-10-20 19:07:12 +02:00
Maurice 69df85bfa2 chore(tours): format the ski and winter files 2026-10-20 18:41:55 +02:00
Maurice be6702f3ab fix(tours): keep the shipped ski migrations and follow the ski tour through
The winter label and four-discipline migrations are already on
feat/tours-board-rest, so a database that ran them would refuse to boot
or fail the old CHECK. 045500, 045520 and 046100 are back as they
shipped, and a new migration drops both winter_activity columns and
adds ski_discipline again with the six disciplines. The entity and
table types follow the column to its new place.

German, Hungarian, Indonesian and English no longer name the whole type
with the ski touring word, and the info dot speaks of ski tours and
tracks. search_tour_pois and its wiki row list rides and ski and winter
tours. Snowshoeing gets a snowshoe glyph instead of the walk's
footprints, a ski import without a discipline can be planned over (the
planner asks for it), and an overlay shown by default tries its tiles
afresh each time it comes on.
2026-10-20 18:40:48 +02:00
Maurice 164a3633ac feat(tours): plan ski and winter tours, snowshoeing and winter hiking among them
Ski becomes "Ski & winter", a type the planner plans with six disciplines
(alpine, snowboard, cross-country, ski touring, snowshoeing, winter hiking),
nothing preselected; saving needs a discipline, and a technique for
cross-country. Every leg is drawn straight between the points through
planner/skiRouting.ts, the one place the piste router will plug in, with its
heights from the elevation source and no ski time worked out.

The hike's winter label goes entirely: its migration, column, field, marks,
GPX rule, MCP field and strings. A hike is summer only; a ski tour with the
snowshoe discipline exports as snowshoeing. The unreleased ski migration takes
the six disciplines and the collection migration loses the winter column.

The pistes are offered for a ski tour, shown until switched off, and with no
tour open, never for a hike. Export, copy, collections, stages (untimed),
actual tracks, the atlas, list and day marks (one glyph per discipline), 3D,
planning over a ski import and list_tours/get_tour_plan know the planned ski
tour.
2026-10-20 18:40:48 +02:00
Maurice fb208a88e2 test(tours): read the MCP fields and stage edits the way the other tests do
The confirmed field is checked through z.object(...).pick() as the dawarich
and doc-sync tests read their tool shapes, and the stage edits pass typed
waypoints through the request contract as the walk tests do. The previous
forms sent the test typecheck out of memory.
2026-10-20 18:19:00 +02:00
Maurice 5ae8ac8e70 test(tours): type the tours tests against the current contracts
Raw request bodies are objects, the collection snapshot names the run, ski,
winter and track time fields, the walk write says it has no hiking limit,
the MCP field shapes are read as zod shapes, and the stage edits go through
the tour request contract the API parses them with.
2026-10-20 18:05:41 +02:00
Maurice 4d81180fc1 test(tours): bring the server tests in line with the tours board
Expectations follow createAssignment's options, the stage_ends column, the editor's trip access and the broadcasts of the take-over. Refusals in the run and ski e2e tests go one by one so supertest does not reset a connection under another request. A parity test keeps the source hint enums in step with the contract.
2026-10-20 17:51:27 +02:00
Maurice b284095ee0 fix(tours): count a multi-day tour in the activity sums only once all its stages are on days
The activity sums never read which stage a day holds, so a multi-day tour
counted its whole route on the last dated stage day while other stages
were on no day yet, and a recording on one stage took the place of the
whole plan beside it. With stages on days of their own, the route now
counts once every stage is on a dated day of the user's, on the last of
them, and is uncounted until then; a recording counts as what was done
and the plan is not added beside it.
2026-10-20 17:00:27 +02:00