Merge pull request #153 from trails-cool/chore/update-drifted-specs

Update specs to match implementation
This commit is contained in:
Ullrich Schäfer 2026-04-03 09:15:31 +01:00 committed by GitHub
commit ca057eb9d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 15 additions and 11 deletions

View file

@ -32,7 +32,7 @@ The Planner SHALL elect one participant per session as the "routing host" who is
#### Scenario: Host failover #### Scenario: Host failover
- **WHEN** the current routing host disconnects - **WHEN** the current routing host disconnects
- **THEN** the longest-connected remaining participant becomes the new host within 5 seconds - **THEN** failover is immediate via deterministic Yjs clientID election: the client with the lowest remaining ID becomes host instantly
### Requirement: Route broadcast ### Requirement: Route broadcast
The routing host SHALL store computed route results in the Yjs document so that all participants receive route updates automatically. The routing host SHALL store computed route results in the Yjs document so that all participants receive route updates automatically.
@ -45,7 +45,7 @@ The routing host SHALL store computed route results in the Yjs document so that
The Planner SHALL support selecting a routing profile that determines how BRouter computes the route. The Planner SHALL support selecting a routing profile that determines how BRouter computes the route.
#### Scenario: Switch routing profile #### Scenario: Switch routing profile
- **WHEN** a user changes the routing profile from "trekking" to "shortest" - **WHEN** a user changes the routing profile (available profiles: `trekking`, `fastbike`, `safety`, `shortest`, `car`)
- **THEN** the profile change syncs via Yjs and the routing host recomputes the route - **THEN** the profile change syncs via Yjs and the routing host recomputes the route
### Requirement: BRouter API proxy ### Requirement: BRouter API proxy

View file

@ -5,7 +5,7 @@ Infrastructure SHALL be provisioned on Hetzner Cloud using Terraform with the He
#### Scenario: Provision server #### Scenario: Provision server
- **WHEN** `terraform apply` is run - **WHEN** `terraform apply` is run
- **THEN** a Hetzner CX21 server (2 vCPU, 4 GB RAM, 40 GB SSD) is created with Docker installed - **THEN** a Hetzner cx23 server (2 vCPU, 4 GB RAM, 40 GB SSD) is created with Docker installed
### Requirement: Docker Compose deployment ### Requirement: Docker Compose deployment
All services SHALL be deployed via Docker Compose on the Hetzner server. All services SHALL be deployed via Docker Compose on the Hetzner server.

View file

@ -5,11 +5,11 @@ Both apps SHALL expose a `/health` endpoint returning service and database statu
#### Scenario: Healthy service #### Scenario: Healthy service
- **WHEN** the app is running and the database is reachable - **WHEN** the app is running and the database is reachable
- **THEN** `GET /health` returns `{ "status": "ok", "db": "connected" }` with HTTP 200 - **THEN** `GET /health` returns `{ "status": "ok", "db": "connected", "version": "<string>" }` with HTTP 200 (version from `SENTRY_RELEASE` env var, defaults to `"dev"`)
#### Scenario: Degraded service #### Scenario: Degraded service
- **WHEN** the app is running but the database is unreachable - **WHEN** the app is running but the database is unreachable
- **THEN** `GET /health` returns `{ "status": "degraded", "db": "unreachable" }` with HTTP 503 - **THEN** `GET /health` returns `{ "status": "degraded", "db": "unreachable", "version": "<string>" }` with HTTP 503
### Requirement: Prometheus metrics ### Requirement: Prometheus metrics
Both apps SHALL expose a `/metrics` endpoint with Prometheus-formatted metrics. Both apps SHALL expose a `/metrics` endpoint with Prometheus-formatted metrics.
@ -26,6 +26,10 @@ Both apps SHALL expose a `/metrics` endpoint with Prometheus-formatted metrics.
- **WHEN** the Planner is running - **WHEN** the Planner is running
- **THEN** `planner_active_sessions` and `planner_connected_clients` gauges reflect current state - **THEN** `planner_active_sessions` and `planner_connected_clients` gauges reflect current state
#### Scenario: BRouter latency metrics
- **WHEN** the Planner proxies a routing request to BRouter
- **THEN** `brouter_request_duration_seconds` histogram is updated (buckets: 0.1s to 10s)
### Requirement: Structured logging ### Requirement: Structured logging
Both apps SHALL output structured JSON logs in production. Both apps SHALL output structured JSON logs in production.

View file

@ -45,7 +45,7 @@ The Planner SHALL persist Yjs session state to PostgreSQL so that sessions survi
- **THEN** reconnecting clients recover the full session state from PostgreSQL - **THEN** reconnecting clients recover the full session state from PostgreSQL
### Requirement: Session expiry ### Requirement: Session expiry
The Planner SHALL automatically expire sessions after a configurable period of inactivity (default: 7 days, max: 30 days). The Planner SHALL automatically expire sessions after a configurable period of inactivity (default: 7 days, no hard ceiling enforced).
#### Scenario: Session expires #### Scenario: Session expires
- **WHEN** no edits are made to a session for 7 days - **WHEN** no edits are made to a session for 7 days

View file

@ -1,15 +1,15 @@
## Requirements ## Requirements
### Requirement: Encrypted secrets in repository ### Requirement: Encrypted secrets in repository
Production secrets SHALL be stored as a SOPS-encrypted file in the repository, decryptable only with a single age private key. Production secrets SHALL be stored as SOPS-encrypted files in the repository, decryptable only with a single age private key. Secrets are split into two files: `secrets.app.env` (application secrets) and `secrets.infra.env` (infrastructure secrets).
#### Scenario: Edit secrets #### Scenario: Edit secrets
- **WHEN** a developer runs `sops infrastructure/secrets.env` - **WHEN** a developer runs `sops infrastructure/secrets.app.env` or `sops infrastructure/secrets.infra.env`
- **THEN** the file is decrypted in a temporary editor, and re-encrypted on save - **THEN** the file is decrypted in a temporary editor, and re-encrypted on save
#### Scenario: CD decryption #### Scenario: CD decryption
- **WHEN** the CD workflow runs - **WHEN** the CD workflow runs
- **THEN** the encrypted secrets file is decrypted using the AGE_SECRET_KEY GitHub secret and provided to docker-compose as an env file - **THEN** both encrypted secrets files are decrypted using the AGE_SECRET_KEY GitHub secret and merged at deploy time as env files for docker-compose
#### Scenario: Secret audit trail #### Scenario: Secret audit trail
- **WHEN** a secret is changed - **WHEN** a secret is changed

View file

@ -27,7 +27,7 @@ The `@trails-cool/gpx` package SHALL parse GPX XML into structured data (waypoin
- **THEN** it returns elevation gain, loss, and a profile array of distance/elevation pairs - **THEN** it returns elevation gain, loss, and a profile array of distance/elevation pairs
### Requirement: Map rendering package ### Requirement: Map rendering package
The `@trails-cool/map` package SHALL provide React components for rendering Leaflet maps with configurable base layers and route overlays. The `@trails-cool/map` package SHALL provide core React components (MapView, RouteLayer) for rendering Leaflet maps with configurable base layers and route overlays. Interactive features (route drag-reshape, ghost markers, no-go area drawing, elevation chart, cursor tracking, colored routes) are implemented directly in the Planner app since they are Planner-specific.
#### Scenario: Render map component #### Scenario: Render map component
- **WHEN** the map package's MapView component is rendered with a center and zoom - **WHEN** the map package's MapView component is rendered with a center and zoom

View file

@ -5,7 +5,7 @@ The system SHALL provide a provider-agnostic email sending function that support
#### Scenario: Send email in production #### Scenario: Send email in production
- **WHEN** the system sends a transactional email in production - **WHEN** the system sends a transactional email in production
- **THEN** the email is delivered via the configured provider (Resend) to the recipient - **THEN** the email is delivered via Nodemailer with SMTP (configured via `SMTP_URL` connection string and `SMTP_FROM` env var) to the recipient
#### Scenario: Dev mode skips sending #### Scenario: Dev mode skips sending
- **WHEN** the system sends a transactional email in development - **WHEN** the system sends a transactional email in development