mirror of
https://github.com/azaion/missions.git
synced 2026-06-21 09:21:07 +00:00
78dea8ebab
ci/woodpecker/push/build-arm Pipeline was successful
Enhanced the .gitignore to exclude test results and updated the Dockerfile to include a new entrypoint script for improved container initialization. Refactored JWT configuration to support additional parameters for automatic refresh intervals, ensuring better control over token management. Updated the ConfigurationResolver to enforce required environment variables without hardcoded fallbacks, enhancing security and flexibility.
220 lines
23 KiB
Markdown
220 lines
23 KiB
Markdown
# Solution — Azaion.Missions
|
||
|
||
> **Status**: derived-from-code (autodev `/document` Step 5, 2026-05-14).
|
||
> **Mode**: retrospective synthesis from the verified `_docs/02_document/` set.
|
||
> **Forward-looking caveat**: this document describes the **post-rename, post-GPS-Denied-removal** target the documentation already reflects. Today's source still uses `Azaion.Flights.*`, `Aircraft*`/`Flight*`/`Orthophoto*`/`GpsCorrection*` filenames, and `[Route("aircrafts"|"flights")]`. The implementation gap is tracked under Jira AZ-EPIC (AZ-539) children B4–B12; the doc-vs-code reconciliation table lives in `_docs/02_document/04_verification_log.md` § 0. References to "the implemented solution" in this document mean the code as it exists today **plus** the deltas closed by B4–B12.
|
||
|
||
---
|
||
|
||
## 1. Product Solution Description
|
||
|
||
`missions` is the **edge-tier .NET 10 REST service** that owns the **mission domain** of an Azaion deployment — vehicle inventory (Plane / Copter / UGV / GuidedMissile), mission plans, ordered waypoints, and the cross-service cascade-delete that keeps the rest of the edge stack consistent when missions or waypoints are removed.
|
||
|
||
**Runtime topology**: exactly one container per device (Jetson Orin / OrangePI / operator-PC), co-located with `annotations`, the detection pipeline, `autopilot`, `gps-denied`, and the React `ui`. All edge services share **one local PostgreSQL** on the device; each migrates and writes only the tables it owns. JWTs are minted by the central `admin` service (ECDSA-signed) and validated locally by `missions` against `admin`'s JWKS endpoint — request-path validation is local after the JWKS is cached, but the first protected request after a cold start triggers a synchronous JWKS HTTPS GET against `admin`. Key rotation publishes a new `kid` in `admin`'s JWKS and propagates to validators on the cache-refresh tick (no coordinated redeploy).
|
||
|
||
### Component interaction (high-level)
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
ui[[Operator UI]]
|
||
admin[[admin service<br/>JWT issuer]]
|
||
autopilot[[autopilot]]
|
||
annotations[[annotations]]
|
||
detection[[detection pipeline]]
|
||
gps[[gps-denied service]]
|
||
|
||
subgraph missions["missions (this service, one .NET process)"]
|
||
direction TB
|
||
h07[07_host<br/>Program.cs / DI / startup]
|
||
c01[01_vehicle_catalog]
|
||
c02[02_mission_planning]
|
||
p04[04_persistence<br/>AppDataConnection + Migrator]
|
||
i05[05_identity<br/>JWT bearer + FL policy]
|
||
h06[06_http_conventions<br/>error envelope + pagination]
|
||
end
|
||
|
||
pg[(postgres-local<br/>shared per device)]
|
||
|
||
ui -- "REST + JWT" --> c01
|
||
ui -- "REST + JWT" --> c02
|
||
admin -- "JWKS over HTTPS (lazy fetch + refresh)" --> i05
|
||
c01 --> p04
|
||
c02 --> p04
|
||
c02 -. "cross-service cascade delete" .-> annotations
|
||
c02 -. "cross-service cascade delete" .-> detection
|
||
autopilot <-- "DB read missions/waypoints<br/>DB write map_objects" --> pg
|
||
p04 --> pg
|
||
annotations <--> pg
|
||
detection <--> pg
|
||
gps -. "no runtime coupling<br/>(GUID refs only)" .-> pg
|
||
|
||
h07 --> c01
|
||
h07 --> c02
|
||
h07 --> p04
|
||
h07 --> i05
|
||
h07 --> h06
|
||
```
|
||
|
||
The HTTP surface is summarised in `_docs/02_document/system-flows.md` (7 flows, F1–F7); the per-component HTTP routes are listed in `_docs/02_document/components/0[1,2]_*/description.md`.
|
||
|
||
---
|
||
|
||
## 2. Architecture (as implemented)
|
||
|
||
The dominant pattern is **thin ASP.NET Core controller → service class → linq2db active-record over a per-request scoped `DataConnection`**, with **no repository abstraction** and **no in-process message queue / event bus**. ADR rationale (ADR-001 .. ADR-008) is in `_docs/02_document/architecture.md` § 8.
|
||
|
||
### 2.1 Per-component solution table
|
||
|
||
| # | Component | Solution (what it does) | Tools / libs | Advantages | Limitations | Requirements satisfied | Security | Cost | Fit |
|
||
|---|-----------|-------------------------|--------------|------------|-------------|------------------------|----------|------|-----|
|
||
| 01 | `01_vehicle_catalog` | Vehicle CRUD + `is_default` exclusivity. Controller `[Authorize(Policy="FL")]` → `VehicleService` → `ITable<Vehicle>` | ASP.NET Core, linq2db `ITable<Vehicle>`, `[Authorize]` | Single owner of the inventory abstraction; same exact pattern as `02_mission_planning` so engineers context-switch cheaply | "Exactly one default" is enforced by clear-then-set without a transaction → race window (B12 decision pending); no input validation on `Name`/`BatteryCapacity` (carry-forward) | Spec § 6.1 (Vehicle Catalog), suite roles `FL` | `[Authorize(Policy="FL")]` on every action; no per-method authz | One service file + one controller (~190 LoC together) | **Good** — matches operator-paced load, vertical scale only |
|
||
| 02 | `02_mission_planning` | Mission + Waypoint CRUD + the **cross-service cascade-delete walk**. Existence-checks `vehicle_id` on create/update; paginates `GET /missions` (the only paginated endpoint). | ASP.NET Core, linq2db, `PaginatedResponse<T>` (`06_http_conventions`) | One canonical place that knows the full mission ownership graph; cascade walks `map_objects → media → annotations → detection → waypoints → missions` in FK order | **Cascade is NOT transaction-wrapped** (ADR-006) → partial failure leaves orphans; `UpdateWaypoint` is a full overwrite even though DTO looks partial; `vehicle_id` missing returns `400` (spec wants `404`); LinqToDB does not eager-load `[Association]` so `Vehicle` and `Waypoints` serialize null/empty | Spec § 6.2 (Mission Planning + Waypoints), spec § cascade contract | `[Authorize(Policy="FL")]` on every action; **no audit log**, no correlation id | Two service files + one controller (~370 LoC together); sequential I/O (4–7 round-trips per cascade) — single-digit ms typical against local Postgres | **Acceptable today; will need transaction wrap (one-line) before SLO commitments** |
|
||
| 04 | `04_persistence` | `AppDataConnection : DataConnection` exposes `ITable<T>` for every persisted entity (4 owned post-B7+B9 + 3 borrowed read-only stubs). `DatabaseMigrator` runs `CREATE TABLE IF NOT EXISTS` + `CREATE INDEX IF NOT EXISTS` at startup; B9 adds a one-shot `DROP TABLE IF EXISTS orthophotos / gps_corrections` for fielded devices | linq2db 6.2.0, Npgsql 10.0.2, raw `Execute` for DDL | Lightweight; no migration tool dependency; idempotent every restart; `ITable<T>` lets cross-component reads/cascades stay typed | No schema versioning; column drops / type changes need manual SQL or a future migration tool; no connection-pool tuning beyond Npgsql defaults | Spec § database schema, suite ER diagram (post-B7) | DB credentials are env-driven (`DATABASE_URL`); no column-level encryption; relies on PG-level access control | One file for the connection (~70 LoC) + one for the migrator (~120 LoC post-B9) | **Good for current schema scale (4 owned tables)**; will become limiting when schema starts evolving frequently |
|
||
| 05 | `05_identity` | `JwtExtensions.AddJwtAuth(issuer, audience, jwksUrl)` registers `JwtBearer` with **ECDSA-SHA256** (algorithm pin), iss + aud validation, `ClockSkew = 30s`, and the named policy `"FL"`. Signing keys are pulled from `admin`'s JWKS via `ConfigurationManager<JsonWebKeySet>` with `HttpDocumentRetriever { RequireHttps = true }` | `Microsoft.AspNetCore.Authentication.JwtBearer` 10.0.5, `Microsoft.IdentityModel.Protocols`, `JsonWebKeySet` | `admin` outage AFTER the JWKS is cached does NOT take this service down; key rotation publishes a new `kid` and propagates on the refresh tick — **no coordinated redeploy**; iss + aud + alg-pin closes the CMMC L2 row 3 finding in this service's code | First protected request after a cold start triggers a synchronous JWKS fetch → if `admin` is unreachable at that exact moment the request 500s (new failure mode vs the legacy local-only model); the policy code `"FL"` retains the legacy "Flight" wording (fleet-wide auth change deferred); user-id claim is parsed but **not consumed** anywhere (no per-user audit) | Spec § auth, `../../suite/_docs/00_roles_permissions.md` | Asymmetric: `admin` holds the private key; this service holds only public-key configuration + the `JWT_ISSUER` / `JWT_AUDIENCE` / `JWT_JWKS_URL` env vars (no shared secret on this side anymore) | One file (~80 LoC) | **Good**; the cold-start dependency on `admin` reachability is the cost of the rotation-without-redeploy operational win |
|
||
| 06 | `06_http_conventions` | `ErrorHandlingMiddleware` (global exception → JSON envelope) + `PaginatedResponse<T>` + the **dead** `ErrorResponse` DTO | ASP.NET Core middleware, `System.Text.Json` (defaults) | Single chokepoint for HTTP wire shape — error mapping is uniform across components | **Two divergences from the suite spec carry forward** (ADR-002): entity/DTO bodies are PascalCase (no `JsonNamingPolicy.CamelCase`); error envelope misses spec's `errors` field. The error envelope IS already camelCase by accidental match (anonymous-object literal). The `ErrorResponse` DTO is dead on the wire and has the wrong shape (`List<string>?` instead of spec's `object?` keyed by field name) | Spec § Error Response Format, § Pagination | `LogError(ex, ...)` only — no PII redaction (none in payload today); fallback `500` body shows the generic message, NOT the stack trace (logged only) | One middleware file + two DTO files (~80 LoC together) | **Acceptable until the suite-wide camelCase migration**; cutover is all-or-nothing because UI + autopilot consume PascalCase today |
|
||
| 07 | `07_host` | `Program.cs` composition root: resolve four required config values via `Infrastructure/ConfigurationResolver.ResolveRequiredOrThrow` (`DATABASE_URL`, `JWT_ISSUER`, `JWT_AUDIENCE`, `JWT_JWKS_URL`); env → Npgsql connection string adapter (`ConvertPostgresUrl`); JWT registration; scoped DI for `AppDataConnection` + service classes; run migrator at startup; `CorsConfigurationValidator.EnsureSafeForEnvironment` gating CORS; mount middleware in correct order; `MapGet("/health")`; mount Swagger | ASP.NET Core minimal host APIs, `Infrastructure/ConfigurationResolver.cs`, `Infrastructure/CorsConfigurationValidator.cs` | One file you can read top-to-bottom in one sitting; **fail-fast on missing required config** — no silent boot with insecure defaults; **CORS gated by environment** — Production refuses an empty allow-list unless `AllowAnyOrigin=true` | Swagger UI is still NOT gated on `IsDevelopment()` (surviving branch of ADR-005); a misconfigured `JWT_JWKS_URL = http://...` passes config resolution but fails at first JWKS fetch (detected at runtime, not startup) | Spec § service composition; container `EXPOSE 8080`; Watchtower restart contract | Required config is loud-fail (`InvalidOperationException`) on absence; **no hardcoded dev fallbacks anywhere**. Swagger surviving branch remains a tracked carry-forward | One file (~180 LoC) plus the two `Infrastructure/*.cs` helpers (~70 LoC together) | **Good** — the security posture is materially improved over the pre-2026-05 state |
|
||
|
||
### 2.2 Cross-cutting design choices
|
||
|
||
| Choice | Rationale | Status |
|
||
|--------|-----------|--------|
|
||
| One PostgreSQL per device, shared by all edge services (ADR-001) | 6× operational overhead saved per device; cross-service cascade is physically possible in one DB connection | **Implemented** |
|
||
| Manual cascade-delete in code, NOT `ON DELETE CASCADE` (ADR-003) | Schema-level cascade would couple `annotations` / detection schemas to this service's lifecycle | **Implemented** (transaction-wrap missing — ADR-006 carry-forward) |
|
||
| `CREATE TABLE IF NOT EXISTS` schema bootstrap (ADR-004), no migration tool | 4-table schema; no column drops or type changes; restart-driven deploy via Watchtower | **Implemented** (B9 adds the one explicit `DROP TABLE IF EXISTS` block for fielded devices) |
|
||
| JWT validation against `admin` JWKS, request-path local after cache (ADR-005, F5) | Asymmetric trust + rotation-without-redeploy; closes the CMMC L2 iss/aud finding in this service's code while keeping `admin` off the per-request hot path | **Implemented** (ECDSA-SHA256 with algorithm pinning, iss + aud validation, HTTPS-only JWKS retrieval, cold-start synchronous fetch trade-off documented) |
|
||
| One csproj, one root namespace (ADR-008); layering by convention not by compiler | Service is small enough that 6 csprojs add more navigation cost than safety value | **Implemented** (post-B5); enforcement via `module-layout.md` § Allowed Dependencies + `/code-review` Phase 7 |
|
||
| GPS-Denied moved to a sibling service (ADR-007, B7+B9) | Different scaling + deployment cadence; GPS-Denied owns its tables and lifecycle | **Doc-only today**; B7 (code) + B9 (DB migration) close the gap |
|
||
|
||
### 2.3 Implementation order (relative to other components)
|
||
|
||
The 6 components have no circular dependencies. Implementation/refactor order (lowest layer first), per `_docs/02_document/module-layout.md`:
|
||
|
||
1. `04_persistence` (Layer 1) — depends only on linq2db + Npgsql.
|
||
2. `05_identity`, `06_http_conventions` (Layer 2) — depend on ASP.NET Core only.
|
||
3. `01_vehicle_catalog` (Layer 3) — depends on `04_persistence`, `05_identity`.
|
||
4. `02_mission_planning` (Layer 4) — depends on `01_vehicle_catalog` (vehicle existence check), `04_persistence`, `05_identity`, `06_http_conventions` (paginated envelope).
|
||
5. `07_host` (Layer 5) — depends on every other component (composition root).
|
||
|
||
Cross-component reads happen via the shared `AppDataConnection` (e.g. `02_mission_planning` reads `vehicles` to existence-check `vehicle_id`); the codebase does **not** wrap that lookup behind an interface in `01_vehicle_catalog`. This is intentional (one csproj, one DB) — see ADR-008.
|
||
|
||
---
|
||
|
||
## 3. Testing Strategy (as observed)
|
||
|
||
> **Status (today)**: **NO automated tests are present in this codebase.** The Tech Stack table in `_docs/02_document/architecture.md` § 2 says "Tests: None present"; `_docs/02_document/00_discovery.md` § Repository Layout confirms there is no `tests/` directory and the csproj has no test project sibling. The autodev `existing-code` flow's Phase A Steps 3 → 7 (Test Spec → Decompose Tests → Implement Tests → Run Tests) is the planned path to close this gap.
|
||
|
||
### 3.1 What exists today
|
||
|
||
| Layer | Coverage | Where it is |
|
||
|-------|----------|-------------|
|
||
| Unit | None | — |
|
||
| Integration / functional | None | — |
|
||
| Non-functional (perf, load, security) | None | — |
|
||
| Health probe | One endpoint (`GET /health` returns `{ status: "healthy" }`) | `Program.cs` `MapGet("/health")` |
|
||
| Schema sanity | Indirect — `DatabaseMigrator` runs at every startup; if a column/table is missing the process crashes | `Database/DatabaseMigrator.cs` |
|
||
| Wire-shape verification | Manual diff against `../../suite/_docs/00_top_level_architecture.md` § Error Response Format + § Pagination | Code review |
|
||
|
||
### 3.2 What the autodev `existing-code` flow will produce
|
||
|
||
- **Step 3 (Test Spec)** → `_docs/02_document/tests/traceability-matrix.md` + per-flow scenario files for F1–F7. The 8 ADRs and 7 carry-forward concerns from `architecture.md` are the seed set for test scenarios.
|
||
- **Step 4 (Code Testability Revision)** → minimal, surgical fixes if the codebase blocks tests from running. The 2026-05-14 re-verification confirmed that the JWT/CORS/Config evolution actually made the code MORE testable than the docs described (env-first `ResolveRequiredOrThrow`, JWKS retrievable via an in-process ECDSA keypair + ephemeral JWKS HTTP service mock, explicit CORS config), so this step is expected to land "all scenarios testable as-is". Scope: smallest set of changes; deeper refactors deferred to Step 8.
|
||
- **Step 5 (Decompose Tests)** → per-test task files in `_docs/02_tasks/todo/`, plus `_test_infrastructure.md`.
|
||
- **Step 6 (Implement Tests)** → `tests/Azaion.Missions.Tests/` sibling project (xUnit is the suite-standard choice; per `coderule.mdc` "follow the established directory structure", no `src/` layer).
|
||
- **Step 7 (Run Tests)** → green test suite forms the safety net for Step 8 (Refactor) and every Phase B feature cycle thereafter.
|
||
|
||
### 3.3 Scenarios likely to land first (anticipated, not yet specified)
|
||
|
||
These are obvious test seams given the F1–F7 flows and the 7 carry-forward concerns; the actual scenario set is produced by Step 3.
|
||
|
||
| Priority | Scenario family | Why it lands first |
|
||
|----------|-----------------|--------------------|
|
||
| 1 | `MissionService.DeleteMission` — full cascade in dependency order | Critical-flow F3, NOT transaction-wrapped today; tests would catch any future regressions in the cascade chain immediately |
|
||
| 1 | `WaypointService.DeleteWaypoint` — scoped cascade variant | Same reason as F3; same NO-transaction caveat |
|
||
| 2 | `MissionService.CreateMission / UpdateMission` — `vehicle_id` existence check + spec-vs-code `400` vs `404` divergence | Locks in the current behaviour so the spec-conformance fix is intentional, not accidental |
|
||
| 2 | `VehicleService.SetDefault` / Create / Update — "exactly one default" race | B12 decision (spec-vs-code stricter behaviour) — tests pin whichever resolution the user picks |
|
||
| 2 | `ErrorHandlingMiddleware` mapping (`KeyNotFoundException → 404`, `ArgumentException → 400`, `InvalidOperationException → 409`, fallthrough → 500) | Wire-shape contract used by every flow |
|
||
| 3 | JWT validation — accept valid ECDSA-SHA256 / reject `alg ∉ [EcdsaSha256]` (HS256-confusion) / reject invalid signature / reject mismatched `kid` / reject expired (with 30s skew) / reject `iss != JWT_ISSUER` / reject `aud != JWT_AUDIENCE` / reject missing-`FL` claim / JWKS rotation picks up new `kid` on refresh tick | F5 cross-cutting; pins the asymmetric-validation contract |
|
||
| 3 | `DatabaseMigrator.Migrate` — idempotent on a fresh DB, idempotent on already-migrated DB, B9 `DROP` on a fielded-legacy DB | F6; tests guard the only explicit destructive step |
|
||
|
||
---
|
||
|
||
## 4. Non-functional behaviour (observed)
|
||
|
||
| Concern | Observed behaviour | Where it is set | Notes |
|
||
|---------|--------------------|-----------------|-------|
|
||
| Latency | Single-digit ms typical; cascade delete = 4–7 sequential round-trips against local Postgres | `Database/AppDataConnection.cs` (per-request scope), `MissionService.DeleteMission` | No SLO in spec; observed under operator-paced load |
|
||
| Throughput | Operator-paced (~1 op/s peak); not load-tested | — | Edge deployment shape; not a hot path |
|
||
| Availability | Best-effort per device; Watchtower restarts on crash; `flight-gate` prevents restart mid-mission | `Dockerfile` + `../../suite/_infra/_compose/`, suite arch doc | No multi-instance HA per device by design |
|
||
| Recovery | RTO ≈ container restart time (~10s); RPO = device-local backup cadence (suite concern) | Watchtower + suite-level backup | — |
|
||
| Cascade atomicity | **Currently violated** (ADR-006); one-line fix queued | `Services/MissionService.cs`, `Services/WaypointService.cs` | Recommended to land with B6 |
|
||
| Wire-shape conformance | **Currently divergent** on entity/DTO case + error envelope's missing `errors` field (ADR-002) | `Program.cs` (no `JsonNamingPolicy.CamelCase`); `Middleware/ErrorHandlingMiddleware.cs` (anonymous object envelope) | Cutover is suite-wide; out of this Epic |
|
||
| Health endpoint | `< 10 ms` typical (no DB ping); used by Watchtower + reverse proxy | `Program.cs` `MapGet("/health")` | Future improvement: gate on DB ping |
|
||
| Resource limits | None in code; container-level limits set by edge compose | `Dockerfile` (no `--memory` / cpu limits inside) | Suite-level concern |
|
||
|
||
---
|
||
|
||
## 5. References
|
||
|
||
### 5.1 Source artefacts (this repo)
|
||
|
||
| Concern | File |
|
||
|---------|------|
|
||
| Web host composition | `Program.cs` |
|
||
| Vehicle catalog | `Controllers/AircraftsController.cs` (post-B6/B8: `Controllers/VehiclesController.cs`), `Services/AircraftService.cs` (post-B6: `VehicleService.cs`) |
|
||
| Mission planning | `Controllers/FlightsController.cs` (post-B6/B8: `Controllers/MissionsController.cs`), `Services/FlightService.cs` (post-B6: `MissionService.cs`), `Services/WaypointService.cs` |
|
||
| Persistence | `Database/AppDataConnection.cs`, `Database/DatabaseMigrator.cs`, `Database/Entities/*.cs` |
|
||
| Identity | `Auth/JwtExtensions.cs` |
|
||
| Configuration / CORS gates | `Infrastructure/ConfigurationResolver.cs`, `Infrastructure/CorsConfigurationValidator.cs` |
|
||
| HTTP conventions | `Middleware/ErrorHandlingMiddleware.cs`, `DTOs/PaginatedResponse.cs`, `DTOs/ErrorResponse.cs` |
|
||
| Container | `Dockerfile` |
|
||
| CI | `.woodpecker/build-arm.yml` |
|
||
| Project | `Azaion.Flights.csproj` (post-B5: `Azaion.Missions.csproj`) |
|
||
|
||
### 5.2 Generated documentation (this repo)
|
||
|
||
| Doc | Path |
|
||
|-----|------|
|
||
| Discovery | `_docs/02_document/00_discovery.md` |
|
||
| Per-module docs (12 modules) | `_docs/02_document/modules/*.md` |
|
||
| Per-component descriptions (6 components) | `_docs/02_document/components/*/description.md` |
|
||
| Module layout (file ownership + layering) | `_docs/02_document/module-layout.md` |
|
||
| Architecture (this solution's source-of-truth) | `_docs/02_document/architecture.md` |
|
||
| System flows (F1–F7) | `_docs/02_document/system-flows.md` + `_docs/02_document/diagrams/flows/*.md` |
|
||
| Data model | `_docs/02_document/data_model.md` |
|
||
| Glossary (confirmed by user) | `_docs/02_document/glossary.md` |
|
||
| Verification log (drift mapping) | `_docs/02_document/04_verification_log.md` |
|
||
| Drift findings (2026-05-14 re-verification) | `_docs/02_document/05_drift_findings_2026-05-14.md` |
|
||
| Deployment notes | `_docs/02_document/deployment/{containerization,ci_cd_pipeline,environment_strategy,observability}.md` |
|
||
|
||
### 5.3 Suite-level cross-references
|
||
|
||
| Concern | File |
|
||
|---------|------|
|
||
| Primary spec for this service | `../../suite/_docs/02_missions.md` |
|
||
| Top-level architecture (error envelope, pagination, topology) | `../../suite/_docs/00_top_level_architecture.md` |
|
||
| Authoritative ER diagram (shared edge Postgres) | `../../suite/_docs/00_database_schema.md` |
|
||
| Roles & permissions (`FL` permission origin) | `../../suite/_docs/00_roles_permissions.md` |
|
||
| GPS-Denied (separate service after B7) | `../../suite/_docs/11_gps_denied.md` |
|
||
| CMMC L2 scorecard (JWT `iss`/`aud` finding) | `../../suite/_docs/05_security/cmmc_l2_scorecard.md` |
|
||
| Repo-config (post-rename) | `../../suite/_docs/_repo-config.yaml` |
|
||
|
||
### 5.4 Tracker (Jira AZ project)
|
||
|
||
| Plan ID | Jira | Type | SP | Status |
|
||
|---------|------|------|----|--------|
|
||
| Epic | [AZ-539](https://denyspopov.atlassian.net/browse/AZ-539) | Epic | — | To Do |
|
||
| B1 (local docs) | [AZ-540](https://denyspopov.atlassian.net/browse/AZ-540) | Task | 3 | **Done** |
|
||
| B2 (suite docs) | [AZ-541](https://denyspopov.atlassian.net/browse/AZ-541) | Task | 3 | **Done** |
|
||
| B3 (state bookkeeping) | [AZ-542](https://denyspopov.atlassian.net/browse/AZ-542) | Task | 3 | **Done** |
|
||
| B4 (repo rename) | [AZ-543](https://denyspopov.atlassian.net/browse/AZ-543) | Task | 3 | To Do |
|
||
| B5 (csproj + namespace) | [AZ-544](https://denyspopov.atlassian.net/browse/AZ-544) | Story | 3 | To Do |
|
||
| B6 (domain rename) | [AZ-545](https://denyspopov.atlassian.net/browse/AZ-545) | Story | 5 | To Do |
|
||
| B7 (drop GPS-Denied) | [AZ-546](https://denyspopov.atlassian.net/browse/AZ-546) | Story | 3 | To Do |
|
||
| B8 (HTTP routes) | [AZ-547](https://denyspopov.atlassian.net/browse/AZ-547) | Story | 3 | To Do |
|
||
| B9 (DB migration) | [AZ-548](https://denyspopov.atlassian.net/browse/AZ-548) | Story | 5 | To Do |
|
||
| B10 (Dockerfile + image tag) | [AZ-549](https://denyspopov.atlassian.net/browse/AZ-549) | Task | 2 | To Do |
|
||
| B11 (consumer cutover) | [AZ-550](https://denyspopov.atlassian.net/browse/AZ-550) | Story | 5 | To Do |
|
||
| B12 (default vehicle rule decision) | [AZ-551](https://denyspopov.atlassian.net/browse/AZ-551) | Task | 2 | To Do |
|
||
|
||
Leftover index: `_docs/_process_leftovers/2026-05-14_rename-flights-to-missions.md`.
|