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.
31 lines
3.9 KiB
Markdown
31 lines
3.9 KiB
Markdown
# 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.faulty` UPDATE, 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'|'unset' }` for [[processor]] to consume. |
|
|
| 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
|
|
|
|
1. **Hook framework choice.** Directus's hook system supports `filter` (mutate-allowed, blocking) and `action` (post-event, non-blocking). Most of these tasks fit one or the other; explicit per task. Validation hooks are `filter`; emit-to-Redis hooks are `action`.
|
|
2. **Redis client in extensions.** `ioredis` direct from inside the Directus process? Or reuse a shared service module? Phase 5 task 5.1 makes the decision.
|
|
3. **Test strategy.** Vitest for unit tests; a dev Directus container + testcontainers Postgres for integration tests. Mirror the [[processor]] split.
|
|
4. **Permission interaction.** Custom endpoints check permissions explicitly via `accountability` — verify which Phase 4 policy applies, gate the endpoint accordingly.
|