a8e808e71c
Initial commit. Establishes the .planning/ tree mirroring processor's shape (ROADMAP.md as nav hub + per-phase folders with READMEs and granular task files). Six phases: 1. Slice 1 schema + deploy pipeline — what Rally Albania 2026 needs. Org catalog (orgs, users, vehicles, devices) + event participation (events, classes, entries, entry_crew, entry_devices). db-init/ for the positions hypertable + faulty column. snapshot/apply tooling. Gitea CI dry-run. Dogfood seed of Rally Albania 2026. Nine task files with full Goal / Deliverables / Specification / Acceptance criteria / Risks / Done sections. 2. Course definition — stages, segments, geofences, waypoints, SLZs. PostGIS extension introduced here. 3. Timing & penalty tables — co-developed with processor Phase 2. entry_segment_starts, entry_crossings, entry_penalties, stage_results, penalty_formulas. 4. Permissions & policies — Directus 11 dynamic-filter Policies per logical role. Deployment-time work, deferred to keep early phases focused on the data model. 5. Custom extensions — TypeScript hooks/endpoints implementing the cross-plane workflows the schema implies (faulty-flag → Redis stream emit, stage-open materializer, etc.). 6. Future / optional — retroactivity preview UI, command-routing Flows, audit trails, federation rule import. Not committed. Non-negotiable design rules captured in ROADMAP.md: schema authority in Directus + snapshot-as-code + db-init for non-Directus DDL + sequential idempotent migrations + entrypoint apply order + no application logic in Flows + permissions deferred to Phase 4. Architectural anchors point at the wiki at ../docs/wiki/ — the schema draft, the Rally Albania 2025 source page, plus the existing processor/postgres-timescaledb/live-channel pages. Each task file calls out the wiki refs an implementing agent should read first. README.md mirrors the processor service README structure: quick start, local Docker test, prod/stage deployment notes, env vars, CI behavior.
3.9 KiB
3.9 KiB
Phase 5 — Custom extensions
Status: ⬜ Not started — depends on Phase 3 (timing tables); some extensions can predate Phase 4 (permissions)
Outcome: TypeScript extensions implementing the cross-plane workflows the schema implies. Each extension is small, focused, well-tested, and bundled into the image via extensions/. Together they let Directus react to operator actions in ways that ripple to processor (recompute requests) and to the SPA (live updates).
Why these are extensions, not Flows
- Reviewable, testable, version-controlled. Extensions are TypeScript modules under
extensions/, ESLint-checked, unit-tested, reviewed in PR. Flows are admin-UI configuration that round-trips through snapshots but isn't readable as code. - Domain logic doesn't belong in declarative orchestration. Flows shine for "on event-write, send a Slack message". They're inadequate for "on
positions.faultyUPDATE, compute the affected window, emit a Redis Stream message with payload shape X." - Performance and correctness. Extensions run in the Directus Node process with full access to
services,database, custom error handling, structured logging. Flows are higher-overhead and harder to reason about under load.
Tasks (sketched, not detailed)
| # | Task | Notes |
|---|---|---|
| 5.1 | Faulty-flag → Redis Stream emit | Hook on positions UPDATE where faulty changed. Emits a recompute:requests message with `{ device_id, ts, action: 'set' |
| 5.2 | events.discipline validation hook |
Pre-create / pre-update hook on entries that validates: discipline=rally → vehicle_id required; discipline=trail-run → vehicle_id null + crew is exactly one runner. Per directus-schema-draft decision. |
| 5.3 | Stage-open materialization endpoint | Custom endpoint POST /stages/:id/open. Reads start_order_strategy, queries the strategy's input data, materializes entry_segment_starts rows per category. Race-director-permissioned. |
| 5.4 | CP closing-time computation | Hook or scheduled task that, when the last competitor's ideal start is computed, sets time_control_closed_at on each CP geofence (Rally Albania §9.19: 60 min after last ideal). |
| 5.5 | "Copy crew from previous entry" endpoint | Custom endpoint POST /entries/:id/copy-crew-from/:source_entry_id. Replaces the recipient's entry_crew rows with cloned values from the source. Per directus-schema-draft decision (no crews collection; UX shortcut). |
| 5.6 | Penalty review batch publish endpoint | Custom endpoint POST /stages/:id/publish-results. Validates entry_penalties are all reviewed, writes stage_results.published_at, freezes the stage. Race-director-permissioned. |
| 5.7 | Entry registration validation | Pre-create hook on entries that checks race_number is in the valid range for the vehicle's category (Rally Albania §5.4 number bands). Friendly error if violated. |
| 5.8 | Extension build pipeline | extensions/ builds via pnpm build:extensions to produce the loadable extension files. Wired into the Dockerfile. CI runs the build + extension unit tests. |
Open questions blocking task-level detail
- Hook framework choice. Directus's hook system supports
filter(mutate-allowed, blocking) andaction(post-event, non-blocking). Most of these tasks fit one or the other; explicit per task. Validation hooks arefilter; emit-to-Redis hooks areaction. - Redis client in extensions.
ioredisdirect from inside the Directus process? Or reuse a shared service module? Phase 5 task 5.1 makes the decision. - Test strategy. Vitest for unit tests; a dev Directus container + testcontainers Postgres for integration tests. Mirror the processor split.
- Permission interaction. Custom endpoints check permissions explicitly via
accountability— verify which Phase 4 policy applies, gate the endpoint accordingly.