- Set Sentry user context (id, username) in Journal root for all routes
- Tag Planner errors with session_id
- Use reactRouterV7BrowserTracingIntegration for route-aware traces
- Hidden source maps (no sourceMappingURL in bundles, .map deleted after
upload to Sentry)
- Privacy manifest at /privacy documenting all data collection
- robots.txt blocking all bots on both apps
- Suppress i18next promotional console log
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 85 tasks complete. Archive change to openspec/changes/archive/ and
sync 9 capability specs to openspec/specs/ for future reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No users yet — capture everything. Traces, session replays, and error
replays all at 100%. Easy to dial back when traffic grows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Client: VITE_SENTRY_ENVIRONMENT=ci baked in during CI build, disables
Sentry and sets 0 sample rates. Server: checks process.env.CI to
disable and tag as "ci" environment.
Production remains enabled with "production" environment tag.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
"Register" appears 3 times on the page (heading, button text, link).
Use getByRole("heading") to target the h1 specifically.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The i18n change renamed "Create Account" to "Register" — update the
E2E test to match.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Initialize i18n via initI18n() in both root.tsx files and replace all
hardcoded user-facing strings with useTranslation() calls. Adds missing
translation keys for planner (profiles, connection states, save flow)
and journal (auth pages, route management, passkey prompts).
Both English and German translations are complete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- @sentry/vite-plugin uploads source maps during Docker build
- Release tagged with git SHA (SENTRY_RELEASE) on both client and server
- Environment set to production/development for filtering
- CD passes SENTRY_AUTH_TOKEN + SENTRY_RELEASE as Docker build args
- docker-compose passes SENTRY_RELEASE to runtime containers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Planner (client + custom server) and Journal (client + entry.server):
- Client: @sentry/react with browser tracing + replay on error
- Server: @sentry/node for unhandled exceptions
- ErrorBoundary captures React errors via Sentry.captureException
- Disabled in dev, 10% trace sample rate in production
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The production server was sending raw Yjs updates but the client
(y-websocket's WebsocketProvider) expects the y-protocols sync protocol
with message type headers. This caused "Unexpected end of array" errors
on every connection.
Rewrite to match the Vite dev plugin: use y-protocols/sync for document
sync, y-protocols/awareness for presence, and lib0 for encoding.
Verified locally: server starts, WebSocket connects, sync messages
exchange correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Save to Journal button makes a cross-origin POST from
planner.trails.cool to trails.cool. The browser's preflight OPTIONS
request was returning HTML (no CORS headers), blocking the save flow.
Add CORS headers to all responses and handle OPTIONS preflight in a
loader. Origin is restricted to PLANNER_URL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 11.6: i18n wired up in both apps (PR #44)
- 11.7: Responsive layout breakpoints added (PR #45)
- 11.8: Deployed to Hetzner, both apps live at trails.cool
All 11 task groups in phase 1 MVP are now complete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Planner: hide title on small screens, wrap header items, hide waypoint
sidebar below md breakpoint (map takes full width).
Journal: stack route stats and action buttons on mobile, responsive
header layout on route detail.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In CI, Playwright now starts apps with production commands (react-router-
serve, node --experimental-strip-types) instead of dev servers. This
catches runtime issues like missing exports and import resolution errors
before deploy. Locally, dev servers are still used for fast iteration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The custom server.ts only had the React Router request handler — static
assets in build/client/ were never served, causing 404s for all JS/CSS
in production. Add a static file handler with path traversal protection
and immutable caching for hashed assets.
Verified locally: HTML 200, assets 200, Cache-Control: immutable on
/assets/*.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix createRequestHandler → createRequestListener (@react-router/node
removed the old API)
- Move noEmit + allowImportingTsExtensions to tsconfig.base.json so all
packages and apps inherit them consistently
- Add explicit .ts extensions to all remaining relative imports across
packages and apps
Verified locally: node --experimental-strip-types server.ts starts and
responds 200.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Node's --experimental-strip-types requires explicit .ts extensions on
relative imports. Add allowImportingTsExtensions to both app tsconfigs
and update server-side imports in planner and journal for consistency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Planner crashed because Node.js couldn't handle .ts imports — add
--experimental-strip-types to the CMD. Journal crashed because the
database had no schemas — add drizzle-kit push to the deploy script.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GITHUB_TOKEN merges (from automerge action) don't trigger push
events on other workflows. Added:
- workflow_run trigger after Automerge completes
- workflow_dispatch for manual gh workflow run cd.yml
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dockerfiles:
- Add db package.json to deps stage
- Copy full node_modules (including app-level) to runtime
- Copy packages/ dir for workspace imports at runtime
CD workflow:
- docker/login-action v3 → v4
- docker/build-push-action v6 → v7
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CD workflow:
- Uncommented deploy step
- Copy docker-compose.yml + Caddyfile to server via SCP
- Login to ghcr.io with DEPLOY_GHCR_TOKEN (classic PAT, read:packages)
- Download Germany BRouter segments on first deploy (~750MB)
- Pull images, restart services, verify
Infrastructure:
- BRouter segments as bind mount (./segments) not Docker volume
- Added ORIGIN, PLANNER_URL, SESSION_SECRET, NODE_ENV to Journal
- Added NODE_ENV to Planner
- Disabled Garage for now (no media storage yet)
Required secrets: DEPLOY_HOST, DEPLOY_SSH_KEY, DEPLOY_GHCR_TOKEN
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Playwright JSON has file → describe → specs nesting.
Use stats.expected/unexpected for counts, iterate nested suites.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generates a markdown summary table in GitHub Actions showing
passed/failed tests with timing per test. Uses the JSON reporter
to output results, then a Node script to format as markdown
and append to $GITHUB_STEP_SUMMARY.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
check_suite and status events don't reliably trigger for workflows
on the same commit (GitHub prevents infinite loops). Replaced with
workflow_run on CI completion, which fires after all CI jobs finish.
Added comments explaining why each trigger group exists.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pg_isready reports true before PostGIS extension is initialized.
Added a second wait loop that queries PostGIS_Version() to ensure
the extension is fully loaded before running drizzle-kit push.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace service container with manual docker run + cached image tar.
Service containers pull the image before steps run (uncacheable).
Manual approach: cache the image as a tar, load on cache hit.
PostGIS image is ~400MB — first run pulls and saves, subsequent
runs load from cache in ~2s instead of ~19s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>