Files
julian a8e808e71c Scaffold directus service planning structure
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.
2026-05-01 20:42:44 +02:00
..

Phase 3 — Timing & penalty tables

Status: Not started — co-developed with processor Phase 2 Outcome: The schema half of the paired schema/code work that produces real timing results. Adds entry_segment_starts, entry_crossings, entry_penalties, stage_results, and penalty_formulas collections. Penalty evaluator registry ships on the processor side; the rule numeric values ship here. After Phase 3, operators can review computed penalties, override them, and publish official stage results.

Why this is co-developed

  • Schema and writer must land together. entry_crossings rows are written by processor Phase 2; defining the collection without the writer is dead weight, defining the writer without the collection is broken code. Land both in the same release window.
  • Penalty formula seeding is event-specific. Rally Albania 2026's SLZ brackets come from the Supplementary Regulation (published 60 days before the event per regs §1.8). Phase 3 needs that data, or a reasonable placeholder, before the rally.
  • Snapshot pattern requires care. entry_penalties snapshots its inputs (peak speed, count, formula values). The schema must capture this faithfully so the recompute strategy in processor works.

Tasks (sketched, not detailed)

# Task Notes
3.1 penalty_formulas collection Per directus-schema-draft: event_id, belongs_to_type, belongs_to_id, type, offence_min, offence_max, operator, penalty, retroactive, enabled. Both bracket-style and flat-style coexist.
3.2 entry_segment_starts collection entry_id, segment_id, start_position, target_at, manual_override. Materialized at stage open.
3.3 entry_crossings collection Per-position-derived crossing events. Idempotent on (entry_id, geofence_id, ts). Written by processor.
3.4 entry_penalties collection entry_id, type, formula_id, formula_snapshot (JSONB), inputs (JSONB), seconds, evaluated_at, recomputed_at, manual_override. Snapshot inputs and rule rows for cheap recompute.
3.5 stage_results collection entry_id, stage_id, clean_time, penalty_seconds, total_time, position_in_class, position_overall, published_at. The next-stage seeding input is clean_time.
3.6 Custom interface for penalty review Operator-facing panel showing all entry_penalties rows for a stage, with diff between auto-computed and manual override. Likely a custom extension (Phase 5 territory). Phase 3 produces the schema; the UI follows.
3.7 Snapshot regeneration + CI verification All new collections in the snapshot; CI dry-run still passes.
3.8 Rally Albania SLZ bracket seed Once the Supplementary Regulation is published, seed penalty_formulas rows with the actual brackets. Single SQL/Directus-API script.

Open questions blocking task-level detail

  1. Does entry_crossings need PostGIS metadata on each row (e.g. the exact geometry-relative position), or just (entry_id, geofence_id, ts, kind)? Default: minimal, the position is in positions and the join key gets you back to it.
  2. Where does position_in_class and position_overall get computed — DB view, materialized view, or processor-written column? Trade-off: view is simpler but slower; column is faster but needs invalidation.
  3. Penalty review workflow UX — is the operator approving each row individually, or bulk-approving the auto-computed set with manual exceptions? Drives whether manual_override is a single bool or a richer state.