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.
@trek/shared
Single source of truth for TREK's API contracts, expressed as Zod schemas and consumed by both the server (request validation + inferred DTO types) and the client (typed requests/responses).
This package is part of the incremental NestJS + React 19 migration (see the "Brownfield Rewrite" board). It is intentionally dormant until modules start importing it — adding it changes nothing for users.
Rules
- One folder per domain:
src/<domain>/<domain>.schema.ts(+.spec.ts). - Domain-agnostic building blocks live in
src/common/. - A route is only considered migrated once its contract lives here.
- Schemas are the source of truth; server DTOs and client types are inferred from them
(
z.infer<typeof schema>), never hand-duplicated.
Consumption (dev)
Both apps resolve @trek/shared to this package's TypeScript source:
- Server (
tsx): viapathsinserver/tsconfig.json. - Client (
vite): viaresolve.aliasinclient/vite.config.ts(+pathsfor the type-checker).
Production packaging (Docker / workspace wiring) is introduced in card F2, when the server first depends on this package at runtime. Until then prod builds are untouched.
Not yet here
The canonical error envelope is finalised in card F5 (it must match TREK's current Express error responses byte-for-byte), so it is deliberately not invented in F1.