# Phase 1 — Slice 1 schema + deploy pipeline Stand up a Directus 11 instance with the minimum schema needed to register entries and tie them to devices, plus the schema-as-code pipeline (snapshots + db-init) and Gitea Actions CI. **This is what Rally Albania 2026 needs to run as a test event.** ## Outcome statement When Phase 1 is done: - Directus runs locally via `docker compose -f compose.dev.yaml up`, against a Postgres 16 + TimescaleDB + PostGIS container. - `db-init/` contains three migrations applied at boot: TimescaleDB extension, `positions` hypertable creation, `faulty boolean` column on positions. All idempotent, all guarded by a `migrations_applied` table. - `snapshots/schema.yaml` contains 12 collections: `organizations`, `users`, `organization_users`, `vehicles`, `organization_vehicles`, `devices`, `organization_devices`, `events`, `classes`, `entries`, `entry_crew`, `entry_devices`. Relations and required fields per [[directus-schema-draft]] (the org-level catalog and event-participation sections). - The image entrypoint runs db-init, then `directus schema apply --yes`, then `directus start`. All three exit 0 against a fresh Postgres. - Gitea Actions builds the image on push to `main` (when `snapshots/`, `db-init/`, `extensions/`, `Dockerfile`, or workflow file changes), runs the apply pipeline against a throwaway Postgres in CI, and pushes the image to `git.dev.microservices.al/trm/directus:main` only if the dry-run passes. - "Motorsport Club Albania" exists as an organization, "Rally Albania 2026" exists as an event under it, and the Rally Albania class catalog is seeded (M-1..M-7, Q-1..Q-3, C-1/C-2/C-A/C-3, S-1/S-2/S-3 from `wiki/sources/rally-albania-regulations-2025.md` §2.2–§2.5). At least one test entry registered with vehicle + crew + devices, used to dogfood the registration workflow. Phase 1 deliberately stops short of: - Course definition (stages, segments, geofences, SLZs) — Phase 2. - Penalty system tables and timing tables — Phase 3. - Permission policies — Phase 4 (collections are admin-only by default). - Custom extension code — Phase 5. ## Sequencing ``` 1.1 Project scaffold └─→ 1.2 db-init runner script └─→ 1.3 Initial migrations ├─→ 1.4 Org-level catalog collections (admin UI work) │ └─→ 1.5 Event-participation collections (admin UI work) │ └─→ 1.6 Schema snapshot/apply tooling │ └─→ 1.7 Image build & entrypoint │ └─→ 1.8 Gitea CI dry-run │ └─→ 1.9 Rally Albania 2026 seed ``` Tasks 1.1 → 1.3 are pure infrastructure and can land before any Directus admin UI work begins. Tasks 1.4 + 1.5 happen against a locally running Directus instance. Tasks 1.6 → 1.8 wire the artifacts together. Task 1.9 is dogfood verification. ## Files modified Phase 1 produces this layout in `directus/`: ``` directus/ ├── .gitea/workflows/build.yml ├── snapshots/ │ └── schema.yaml # generated; edits via admin UI + pnpm run schema:snapshot ├── db-init/ │ ├── 001_extensions.sql # CREATE EXTENSION timescaledb (postgis added in Phase 2) │ ├── 002_positions_hypertable.sql │ └── 003_faulty_column.sql ├── extensions/ # empty — Phase 5 fills this ├── scripts/ │ ├── apply-db-init.sh # numeric-order, guard-table-protected runner │ ├── schema-snapshot.sh # wraps `directus schema snapshot --yes` │ └── schema-apply.sh # wraps `directus schema apply --yes` ├── entrypoint.sh # apply-db-init.sh && directus schema apply && directus start ├── Dockerfile # FROM directus/directus:11.x + bundled artifacts ├── compose.dev.yaml # local dev: directus + timescaledb container ├── package.json # only for the snapshot/apply npm scripts and tooling ├── pnpm-lock.yaml ├── .env.example ├── .dockerignore ├── .gitignore └── README.md ``` ## Tech stack (decided) - **Directus 11.x** (latest stable on the 11.x line at time of build). Pinned in `Dockerfile` `FROM` line. - **Postgres 16 + TimescaleDB + PostGIS** as the database (PostGIS extension added in Phase 2; Phase 1 only uses TimescaleDB). - **pnpm** for any local dev scripts (snapshot wrappers, lint). - **bash** (POSIX-compatible) for `apply-db-init.sh` and `entrypoint.sh`. No Node dependency at runtime — only Directus needs Node, and that's the upstream image's responsibility. - **psql** (from `postgresql-client` package) inside the image for db-init application. - **Gitea Actions** for CI, matching the `processor` and `tcp-ingestion` workflow shape. If an implementer wants to deviate, they must update the relevant task file first. ## Key design decisions inherited from `processor` - **Image is bundled, not assembled at runtime.** `snapshots/`, `db-init/`, and `extensions/` are baked into the image, not mounted as volumes. Reproducible across envs. - **Slim Dockerfile.** Multi-stage if extensions need a build step (Phase 5+); for Phase 1 a single stage is enough. - **CI workflow** — single-job pattern matching `processor/.gitea/workflows/build.yml`. Use `services:` for the throwaway Postgres in the dry-run step. - **No `.env` in image.** All env vars come from the deploy stack (Portainer / compose) at runtime. ## Open questions blocking task-level detail None. The schema draft pinned the org-level catalog and event-participation shape; Phase 1 implements exactly that subset.