e22d9d489a
Two parallel tasks landing together. The boot pipeline is now wired end-to-end: db-init → schema apply → directus bootstrap → pm2-runtime. Live-verified by booting a fresh compose stack to a serving Directus admin UI on :8055. Task 1.6 — snapshot tooling: - scripts/schema-snapshot.sh — host-side, dev-time. Verifies docker is on PATH and the directus compose service is running, runs `node /directus/cli.js schema snapshot --yes` inside the container, copies the YAML out to ./snapshots/schema.yaml. Used after admin-UI schema changes to capture the new state for git commit. - scripts/schema-apply.sh — image-side, boot-time. Reads /directus/snapshots/schema.yaml, runs a dry-run preview, then applies. Gracefully skips when the snapshot is absent or whitespace- only (Phase 1 first-boot path before tasks 1.4/1.5 produce collections). SNAPSHOT_PATH env var override for CI flexibility. - snapshots/README.md — lifecycle doc; warns against hand-editing. Task 1.7 — real entrypoint flow: - entrypoint.sh rewritten from Phase 1.1's placeholder to the 4-step boot per ROADMAP design rule #3: 1/4 db-init → /directus/scripts/apply-db-init.sh 2/4 schema apply → /directus/scripts/schema-apply.sh 3/4 directus bootstrap → node /directus/cli.js bootstrap 4/4 directus start → exec pm2-runtime start ecosystem.config.cjs set -euo pipefail halts boot on any step's non-zero exit. Each step emits a [entrypoint] log marker so an operator reading container logs sees which step failed. Bug found and fixed during live verification: - Both 1.6 scripts initially called bare `directus schema ...` as if the CLI were on PATH. Upstream directus/directus:11.17.4 does NOT expose `directus` on PATH — invocation is via `node /directus/cli.js`, same pattern as the entrypoint's bootstrap step. Both scripts corrected. Also added -T to docker compose exec in schema-snapshot.sh so the script works in non-TTY contexts (CI). Phase 5 follow-up (non-blocking) flagged in 07's Done section: Directus warns "Collection 'positions' doesn't have a primary key column and will be ignored". The positions table uses UNIQUE INDEX (device_id, ts) matching processor's pattern, not a PK constraint. Means positions is not auto-registered as a Directus collection — fine for Phase 1, but the operator faulty-flag workflow will need a custom endpoint or manual collection registration in Phase 5. ROADMAP marks 1.6 + 1.7 done. Phase 1 progress: 5/9 tasks complete (1.1, 1.2, 1.3, 1.6, 1.7); 1.4, 1.5, 1.8, 1.9 remain.
103 lines
8.6 KiB
Markdown
103 lines
8.6 KiB
Markdown
# directus — Roadmap
|
||
|
||
The TRM business plane. Directus 11 instance owning the relational schema and exposing it via REST/GraphQL/WebSockets/Admin UI. Schema-as-code via `snapshots/` + `db-init/`, applied at container startup.
|
||
|
||
This file is the single navigation hub for all implementation planning. Each phase has its own folder with a README and granular task files. Update statuses here as work lands.
|
||
|
||
## Status legend
|
||
|
||
| Symbol | Meaning |
|
||
|--------|---------|
|
||
| ⬜ | Not started |
|
||
| 🟦 | Planned (designed, not coded) |
|
||
| 🟨 | In progress |
|
||
| 🟩 | Done |
|
||
| ⏸ | Paused / blocked |
|
||
| ❄️ | Frozen / future / optional |
|
||
|
||
## Architectural anchors
|
||
|
||
The service is specified by the wiki at `../docs/wiki/`. Implementing agents should read these pages before starting any task:
|
||
|
||
- **Architecture** — `docs/wiki/sources/gps-tracking-architecture.md`, `docs/wiki/concepts/plane-separation.md`, `docs/wiki/concepts/failure-domains.md`
|
||
- **This service** — `docs/wiki/entities/directus.md`
|
||
- **Schema design** — `docs/wiki/synthesis/directus-schema-draft.md`
|
||
- **Reference rulebook** — `docs/wiki/sources/rally-albania-regulations-2025.md` (canonical real-world fixture for federation rule shapes)
|
||
- **Downstream / sibling** — `docs/wiki/entities/postgres-timescaledb.md`, `docs/wiki/entities/processor.md`, `docs/wiki/concepts/live-channel-architecture.md`
|
||
|
||
## Non-negotiable design rules
|
||
|
||
These rules govern every task. Any deviation must be discussed and documented as a decision before code lands.
|
||
|
||
1. **Schema authority lives in Directus.** Collections, fields, relations are defined through Directus and round-tripped via `directus schema snapshot`. The exception is the `positions` hypertable (owned by [[processor]]) and any other DDL Directus cannot represent (PostGIS-specific syntax, custom indexes, hypertable creation) — those live in `db-init/*.sql`.
|
||
2. **`db-init/*.sql` is sequential, idempotent, and guarded.** Files numbered `NNN_name.sql`. Each is internally idempotent (`IF NOT EXISTS`, `ADD COLUMN IF NOT EXISTS`). The runner skips files already recorded in `migrations_applied`. Manual application of out-of-order files is forbidden.
|
||
3. **Apply order at boot:** db-init runner → `directus schema apply --yes` → `directus start`. Any failure halts boot. Implemented in `entrypoint.sh`.
|
||
4. **Snapshot lives in git, edited only via the admin UI.** Hand-editing `snapshots/schema.yaml` is forbidden — round-trip through the UI keeps the format consistent with what `directus schema snapshot` produces.
|
||
5. **One PR = one snapshot regeneration.** PRs that change schema include the regenerated snapshot. CI verifies the snapshot matches what `directus schema snapshot` would produce against an applied database.
|
||
6. **No application logic in Flows.** Flows are reserved for declarative orchestration (notifications, simple field updates, webhook routing). Domain logic lives in `extensions/` (TypeScript hooks/endpoints) where it is reviewed, tested, and version-controlled like any other code.
|
||
7. **Permissions are a separate phase.** Adding a collection in Phase 1–3 does NOT come with its access policies — those land deliberately in Phase 4. Until then collections are admin-only by default. This avoids premature commitment to role definitions before the data model is settled.
|
||
8. **Image starts from `directus/directus:11.x`.** No forking the upstream image. Customizations are: bundled extensions under `/directus/extensions/`, snapshot/db-init artifacts under `/directus/snapshots/` and `/directus/db-init/`, and an entrypoint wrapper.
|
||
|
||
## Phases
|
||
|
||
### Phase 1 — Slice 1 schema + deploy pipeline
|
||
|
||
**Status:** 🟨 In progress (1.1, 1.2, 1.3, 1.6, 1.7 done; 1.4, 1.5, 1.8, 1.9 remaining)
|
||
**Outcome:** A Directus instance with the org-level catalog (orgs, users, organization_users, vehicles, devices and their org junctions) and event-participation collections (events, classes, entries, entry_crew, entry_devices) live and snapshot-tracked. `db-init/` covers the TimescaleDB extension, the `positions` hypertable, and the `faulty` column. Image builds via Gitea Actions with a CI dry-run that catches snapshot drift before deploy. Rally Albania 2026 is registered as the first event in admin UI to dogfood the registration workflow. **This is what Rally Albania 2026 needs.**
|
||
|
||
[**See `phase-1-slice-1-schema/README.md`**](./phase-1-slice-1-schema/README.md)
|
||
|
||
| # | Task | Status | Landed in |
|
||
|---|------|--------|-----------|
|
||
| 1.1 | [Project scaffold](./phase-1-slice-1-schema/01-project-scaffold.md) | 🟩 | pending user commit |
|
||
| 1.2 | [db-init runner script](./phase-1-slice-1-schema/02-db-init-runner.md) | 🟩 | pending user commit |
|
||
| 1.3 | [Initial migrations (extensions, positions hypertable, faulty column)](./phase-1-slice-1-schema/03-initial-migrations.md) | 🟩 | pending user commit |
|
||
| 1.4 | [Org-level catalog collections](./phase-1-slice-1-schema/04-org-catalog-collections.md) | ⬜ | — |
|
||
| 1.5 | [Event-participation collections](./phase-1-slice-1-schema/05-event-participation-collections.md) | ⬜ | — |
|
||
| 1.6 | [Schema snapshot/apply tooling](./phase-1-slice-1-schema/06-snapshot-tooling.md) | 🟩 | pending user commit |
|
||
| 1.7 | [Image build & entrypoint](./phase-1-slice-1-schema/07-image-and-dockerfile.md) | 🟩 | pending user commit |
|
||
| 1.8 | [Gitea CI dry-run workflow](./phase-1-slice-1-schema/08-gitea-ci-dryrun.md) | ⬜ | — |
|
||
| 1.9 | [Rally Albania 2026 dogfood seed](./phase-1-slice-1-schema/09-rally-albania-2026-seed.md) | ⬜ | — |
|
||
|
||
### Phase 2 — Course definition
|
||
|
||
**Status:** ⬜ Not started — depends on Phase 1
|
||
**Outcome:** Stages, segments, geofences (PostGIS polygons), waypoints, and speed_limit_zones as data-layer collections. Operators can define an event's full course before each stage. No processor logic yet — Phase 2 of [[processor]] consumes this data and writes crossings/penalties.
|
||
|
||
[**See `phase-2-course-definition/README.md`**](./phase-2-course-definition/README.md)
|
||
|
||
### Phase 3 — Timing & penalty tables
|
||
|
||
**Status:** ⬜ Not started — co-developed with processor Phase 2
|
||
**Outcome:** `entry_segment_starts`, `entry_crossings`, `entry_penalties`, `stage_results`, and `penalty_formulas` collections. The schema half of the paired schema/code work that produces real timing results. Penalty evaluator registry shipped on the [[processor]] side; rule numeric values shipped here.
|
||
|
||
[**See `phase-3-timing-and-penalty-tables/README.md`**](./phase-3-timing-and-penalty-tables/README.md)
|
||
|
||
### Phase 4 — Permissions & policies
|
||
|
||
**Status:** ⬜ Not started — depends on Phases 1–3
|
||
**Outcome:** Dynamic-filter Policies per logical role (org-admin, race-director, marshal, timekeeper, participant, …) covering each collection × action. Multi-tenant isolation enforced by Directus, not by application code. Deployment-time work, not architectural.
|
||
|
||
[**See `phase-4-permissions-and-policies/README.md`**](./phase-4-permissions-and-policies/README.md)
|
||
|
||
### Phase 5 — Custom extensions
|
||
|
||
**Status:** ⬜ Not started — depends on Phase 3
|
||
**Outcome:** TypeScript extensions implementing the cross-plane workflows the schema implies: faulty-flag → `recompute:requests` stream emit; `events.discipline` validation hook; stage-open trigger materializing `entry_segment_starts`; CP closing-time computation; entry registration "copy crew from previous entry" custom endpoint.
|
||
|
||
[**See `phase-5-custom-extensions/README.md`**](./phase-5-custom-extensions/README.md)
|
||
|
||
### Phase 6 — Future / optional
|
||
|
||
**Status:** ❄️ Not committed
|
||
[**See `phase-6-future/README.md`**](./phase-6-future/README.md)
|
||
|
||
Ideas on radar: retroactivity preview UI for geometry edits (Phase 2.5 of [[processor]] — needs a UI counterpart here), command-routing Flows ([[phase-2-commands]]), audit trail extensions, federation rule import tooling.
|
||
|
||
## Operating model
|
||
|
||
- **Implementation agent contract.** Each task file is self-sufficient: goal, deliverables, specification, acceptance criteria. An agent should be able to complete one task without reading the whole wiki — but should skim the wiki references at the top of the task before starting.
|
||
- **Sequence within a phase.** Task numbering reflects intended order. Soft dependencies are explicit in each task's "Depends on" field. Tasks with no dependencies on each other can be done in parallel.
|
||
- **Status updates.** When a task is started, change its row in this ROADMAP to 🟨 and the task file's status badge accordingly. When done, 🟩 + a one-line note in the task file's "Done" section pointing at the merging commit/PR.
|
||
- **Drift control.** If implementation diverges from a task's spec, update the task file *before* the diverging code lands, with a note explaining why. Do not let plans rot — either fix the plan or fix the code.
|