The .woodpecker/build-arm.yml already pushes ${REGISTRY_HOST}/azaion/missions
(landed earlier as part of the B5 csproj/namespace rename). What this commit
fixes is the missions-internal documentation that still described the legacy
azaion/flights image as the *current* state.
Edits:
- _docs/02_document/deployment/environment_strategy.md: drop "today's edge
compose still references azaion/flights" — B10 is done. Container/service
name 'flights' still noted as B6/B11 work.
- _docs/02_document/deployment/containerization.md: drop "today's Dockerfile
ENTRYPOINT is dotnet Azaion.Flights.dll, image tag base is azaion/flights"
— both AZ-544 (B5) and AZ-549 (B10) done.
- _docs/02_document/deployment/ci_cd_pipeline.md: same fix.
- _docs/02_document/components/07_host/description.md: same fix.
- _docs/02_document/04_verification_log.md row for AZ-549: explicitly
marked "done"; Code symbol column converged to post-rename value.
- _docs/00_problem/restrictions.md E6: parenthetical reworded so the row
reads as a present-state assertion (B10 done) instead of a forward-
looking note.
- _docs/02_document/glossary.md "Synonym pairs" heading flipped from
"today's code ↔ post-rename target" to "pre-rename ↔ post-rename"
(adjacent hygiene — B5-B9+B10 are done across the missions rename
Epic; the table's "today" framing no longer matches reality).
Spec _docs/tasks/todo/AZ-549a_missions_rename_b10_pipeline.md moved to
_docs/tasks/done/.
rg -F 'azaion/flights' missions/ | grep -v done/ now returns only
intentional pre-rename historical references in glossary.md /
architecture.md / restrictions.md / verification_log.md — the "current
state" wording is gone.
Suite-side slice (AZ-549b — _infra/deploy/*/docker-compose.yml image
ref + ci/README.md example) shipped separately in the suite repo.
Co-authored-by: Cursor <cursoragent@cursor.com>
22 KiB
Step 4 — Verification Log
Status: complete
Date: 2026-05-14
Mode: rename-aware (per autodev /autodev choice A)
Scope: every artifact under _docs/02_document/ cross-checked against actual workspace source.
0. Verification mode (the one nuance)
The _docs/02_document/ set is forward-looking — it documents the post-rename, post-GPS-Denied-removal target. The workspace code is still pre-rename (Azaion.Flights.csproj, Aircraft* / Flight* / Orthophoto* / GpsCorrection* files, [Route("aircrafts"|"flights")], 6 owned tables, both "FL" and "GPS" policies). Each doc carries an explicit forward-looking note pointing at the responsible Jira children (B5–B12) under epic AZ-539.
Verification therefore applies a rename mapping when comparing docs to code:
| Doc symbol | Code symbol | Reconciled by |
|---|---|---|
Vehicle*, vehicles, VehicleType { Plane, Copter, UGV, GuidedMissile } |
Aircraft*, aircrafts, AircraftType { Plane, Copter } |
AZ-545 (B6) domain rename + value extension |
Mission*, missions, mission_id, vehicle_id |
Flight*, flights, flight_id, aircraft_id |
AZ-545 (B6) |
[Route("vehicles")], [Route("missions")] |
[Route("aircrafts")], [Route("flights")] |
AZ-547 (B8) |
Azaion.Missions.* namespace, Azaion.Missions.csproj, Azaion.Missions.dll |
Azaion.Flights.*, Azaion.Flights.csproj, Azaion.Flights.dll |
AZ-544 (B5) |
4 owned tables (no orthophotos, no gps_corrections), 7 entities |
6 owned tables, 9 entities | AZ-546 (B7) entity drop + AZ-548 (B9) DB migration |
Single "FL" policy in JwtExtensions |
Both "FL" AND "GPS" policies |
AZ-546 (B7) |
Cascade omits orthophotos / gps_corrections branches |
Cascade still touches both | AZ-546 (B7) |
azaion/missions:*-arm image tag, dotnet Azaion.Missions.dll entrypoint |
azaion/missions:*-arm, dotnet Azaion.Missions.dll (post-B5+B10) |
AZ-549 (B10), AZ-544 (B5) — done |
Any doc claim covered by this mapping is treated as expected, NOT drift. Only mismatches NOT covered by the mapping are flagged below.
1. Counts
| Domain | Doc claim (post-target) | Code reality (today, pre-rename) | Reconciles via |
|---|---|---|---|
| Component count | 6 (01_vehicle_catalog, 02_mission_planning, 04_persistence, 05_identity, 06_http_conventions, 07_host) |
6 folders on disk under _docs/02_document/components/ matching exactly |
(no rename gap; matches today) |
| Module docs | 12 | 12 modules in _docs/02_document/modules/ mapping 1:1 to source files (under rename) |
rename mapping |
Source .cs files |
"~33 post-B7" | 37 today | drops 4 in B6+B7 (Aircraft.cs, Flight.cs, Orthophoto.cs, GpsCorrection.cs); rename leaves the other 33 |
| Owned tables | 4 | 6 | B7 + B9 |
| Entities | 7 | 9 | B7 |
| Indexes in migrator | 3 (post-B9) | 6 (today) | B9 drops 3 GPS-Denied indexes |
| Auth policies | 1 ("FL") |
2 ("FL", "GPS") |
B7 deletes "GPS" per AZ-546 acceptance |
| HTTP route prefixes | /vehicles/*, /missions/*, GET /health |
/aircrafts/*, /flights/*, GET /health |
B8 |
All count claims reconcile. ✓
2. Per-symbol sweep (entities, signatures, routes)
Entities (post-rename target vs today's code)
Doc says (modules/entities.md, data_model.md) |
Code today | Reconciles? |
|---|---|---|
Vehicle [Table("vehicles")] with PK id, columns type, model, name, fuel_type, battery_capacity, engine_consumption, engine_consumption_idle, is_default |
Aircraft [Table("aircrafts")] with identical column set + PK |
✓ B6 rename only |
Mission [Table("missions")] with PK id, columns created_date, name, vehicle_id; associations Vehicle?, List<Waypoint> |
Flight [Table("flights")] with created_date, name, aircraft_id; associations Aircraft?, List<Waypoint> |
✓ B6 rename only |
Waypoint with mission_id FK + association Mission? |
Waypoint with flight_id FK + association Flight? |
✓ B6 rename only |
MapObject with mission_id FK |
MapObject with flight_id FK |
✓ B6 rename only |
Media, Annotation, Detection borrowed stubs |
identical stubs in code | ✓ no change needed |
Removed in B7: Orthophoto, GpsCorrection |
both still present in Database/Entities/ |
✓ B7 will delete |
Service signatures (post-rename vs today)
| Doc claim | Code today | Reconciles? |
|---|---|---|
VehicleService.{CreateVehicle, UpdateVehicle, GetVehicle, GetVehicles, DeleteVehicle, SetDefault} |
AircraftService.{CreateAircraft, UpdateAircraft, GetAircraft, GetAircrafts, DeleteAircraft, SetDefault} — 6 methods, identical shapes |
✓ B6 rename only |
MissionService.{CreateMission, UpdateMission, GetMission, GetMissions, DeleteMission} |
FlightService.{CreateFlight, UpdateFlight, GetFlight, GetFlights, DeleteFlight} — 5 methods, identical shapes |
✓ B6 rename only |
WaypointService.{CreateWaypoint(missionId, ...), UpdateWaypoint(missionId, wpId, ...), GetWaypoints(missionId), DeleteWaypoint(missionId, wpId)} |
WaypointService.{CreateWaypoint(flightId, ...), UpdateWaypoint(flightId, waypointId, ...), GetWaypoints(flightId), DeleteWaypoint(flightId, waypointId)} |
✓ B6 rename only (parameter name flightId → missionId) |
JwtExtensions.AddJwtAuth(IServiceCollection, string) registers only the "FL" policy (post-B7) |
Same signature; registers "FL" AND "GPS" policies |
✓ B7 will drop "GPS" |
ErrorHandlingMiddleware(RequestDelegate, ILogger<>) with Invoke(HttpContext) mapping KeyNotFound→404 / Argument→400 / InvalidOperation→409 / *→500 |
Identical | ✓ no rename gap |
HTTP routes (post-B8 vs today)
| Doc post-target | Today's [Route(...)] |
Reconciles? |
|---|---|---|
6 vehicle endpoints under /vehicles |
6 endpoints under /aircrafts |
✓ B8 |
5 mission endpoints under /missions + 4 waypoint sub-endpoints under /missions/{id}/waypoints/... |
identical 5+4 endpoints under /flights + /flights/{id}/waypoints/... |
✓ B8 |
GET /health (anonymous) |
identical | ✓ no rename gap |
Migrator DDL (post-B7+B9 vs today)
| Doc post-target | Today's DatabaseMigrator.Sql |
Reconciles? |
|---|---|---|
4 CREATE TABLE IF NOT EXISTS: vehicles, missions, waypoints, map_objects + 3 indexes on FKs |
6 CREATE TABLE IF NOT EXISTS: aircrafts, flights, waypoints, orthophotos, gps_corrections, map_objects + 6 indexes |
✓ B7+B9 (drop orthophotos + gps_corrections tables and their 3 indexes; rename aircrafts/flights columns to vehicles/missions per B6/B9 mapping) |
DROP TABLE IF EXISTS orthophotos / gps_corrections (one-shot in B9) for fielded devices |
not present | ✓ B9 will add |
All symbol-level claims reconcile. ✓
3. Flow-correctness sweep
| Flow | Doc says | Code today | Reconciles? |
|---|---|---|---|
| F1 Vehicle CRUD | 6 endpoints; "exactly one default" exclusivity rule via clear-then-set in code; spec is just-toggle | AircraftService.CreateAircraft / UpdateAircraft / SetDefault all clear-then-set when IsDefault==true; no transaction |
✓ matches code; B12 decision tracked |
| F2 Mission create/read/update | Existence check on vehicle_id returns ArgumentException → 400 (spec wants 404) |
FlightService.CreateFlight / UpdateFlight — aircraftExists check throws ArgumentException("Aircraft {id} not found") → middleware → 400 |
✓ matches; spec divergence carry-forward |
| F3 Mission cascade-delete | Order: map_objects → waypoints/media/annotations/detection → waypoints → missions. NOT transaction-wrapped. Post-B7: no orthophoto/gps_correction branches |
FlightService.DeleteFlight order today: map_objects → gps_corrections → orthophotos → waypoints/media/annotations/detection → waypoints → flights. NOT transaction-wrapped |
✓ B7 removes the two extra branches |
| F4 Waypoint create/read/update/delete | Delete walks media/annotations/detection, post-B7 no gps_corrections branch; UpdateWaypoint is full overwrite |
WaypointService.DeleteWaypoint walks media/annotations/detection AND gps_corrections today; UpdateWaypoint is full overwrite |
✓ B7 removes gps_corrections branch |
| F5 JWT validation | REISSUED 2026-05-14 — ECDSA-SHA256 against admin's JWKS (cached via ConfigurationManager<JsonWebKeySet> with HttpDocumentRetriever{RequireHttps=true} + private JwksRetriever); ValidateIssuer = true against JWT_ISSUER; ValidateAudience = true against JWT_AUDIENCE; ClockSkew = 30 seconds; ValidAlgorithms = [EcdsaSha256]; RequireSignedTokens = true; RequireExpirationTime = true. Single "FL" policy post-B7 |
Auth/JwtExtensions.cs matches the reissued claim exactly; today has BOTH "FL" and "GPS" policies |
✓ B7 drops "GPS". The previous verdict ("matches exactly" against the HS256 / shared-secret doc) was wrong — the underlying docs were stale; corrected via the 2026-05-14 re-verification pass and rewritten in modules/auth.md, components/05_identity/description.md, diagrams/flows/flow_jwt_validation.md, architecture.md § 7 + Tech Stack, system-flows.md Cross-cutting #1 + F5, and 00_problem/* (see § 4.3 below) |
| F6 Startup + migration | REISSUED 2026-05-14 — Program.cs builds host → ConfigurationResolver.ResolveRequiredOrThrow resolves DATABASE_URL (with ConvertPostgresUrl) → resolves JWT_ISSUER + JWT_AUDIENCE + JWT_JWKS_URL (all required, no fallback) → registers scoped services + JWT bearer + JWKS ConfigurationManager → reads CorsConfig:AllowedOrigins + CorsConfig:AllowAnyOrigin → CorsConfigurationValidator.EnsureSafeForEnvironment (throws in Production with implicit-permissive config) → registers CORS policy (permissive OR WithOrigins) → migrates → starts. Pipeline: ErrorHandlingMiddleware FIRST → Cors → Authentication → Authorization → Swagger → MapControllers → MapGet("/health") → Run. May emit PermissiveDefaultWarning startup log when implicit-permissive CORS applies |
Program.cs matches the reissued claim exactly; service registration is FlightService, WaypointService, AircraftService today instead of Mission/Waypoint/VehicleService |
✓ B5+B6 rename + DI re-registration. The previous verdict ("matches exactly" against docs claiming hardcoded JWT_SECRET fallback + unconditional permissive CORS) was wrong — corrected via the 2026-05-14 re-verification pass and rewritten in modules/program.md, components/07_host/description.md, diagrams/flows/flow_startup_migration.md, architecture.md § 3 deployment table + ADR-005, and system-flows.md F6 |
| F7 Health probe | MapGet("/health", () => Results.Ok(new { status = "healthy" })), anonymous |
identical | ✓ no rename gap |
All flow claims reconcile after the 2026-05-14 reissue. ✓
4. Drift NOT covered by the rename mapping
These are real findings. Items in § 4.1 were corrected inline as part of this verification pass; § 4.2 are flagged for follow-up.
4.1 Corrected inline (this pass)
| # | Where | Problem | Correction |
|---|---|---|---|
| D1 | components/06_http_conventions/description.md § 1, § 3, § Caveats #1, header |
Doc claimed the global error envelope is PascalCase. Actual middleware code is JsonSerializer.Serialize(new { statusCode = (int)code, message }) — anonymous-type property names are written lowercase-first, and System.Text.Json preserves them as-is when no JsonNamingPolicy is configured, so the wire output IS {"statusCode":..., "message":"..."} (camelCase). Internally inconsistent (§ 1 said camelCase ✓, § 3 example showed PascalCase ✗) |
Rewrote § 3 example, Caveat #1, and the header status line to distinguish entity bodies (PascalCase, true divergence) from the error envelope (camelCase, accidental match). Carry-forward concerns — missing errors field and dead ErrorResponse DTO — explicitly retained |
| D2 | architecture.md ADR-002 |
Same broad PascalCase claim covering the error envelope | Reworded ADR title + body to scope PascalCase to entity / DTO bodies; added explicit "exception (accidental match)" for the error envelope |
| D3 | architecture.md § 6 NFR table — "API spec conformance" row |
Same broad claim | Same scoping correction inline |
| D4 | system-flows.md § Cross-cutting concerns #3 |
"Wire shape is PascalCase today, NOT camelCase" — wrong for the error envelope | Reworded to distinguish entity bodies from the error envelope |
| D5 | data_model.md § 11 Backward compatibility |
Same broad PascalCase claim | Same scoping correction inline |
| D6 | modules/middleware.md § Internal Logic + Notes/Smells #1 + #2 |
Internal Logic section asserted PascalCase wire shape; Notes #1 said middleware emits PascalCase { "StatusCode", "Message" }; Notes #2 generalized that as "system-wide divergence" |
Rewrote Internal Logic to show the actual {"statusCode":..., "message":"..."} and explain the lowercase-by-construction match; Notes #1 reworded to "partial spec divergence" with the missing errors field as the remaining issue; Notes #2 reworded to scope PascalCase to entity / DTO bodies |
| D7 | modules/dtos.md last bullet (Spec divergence) |
Bundled PaginatedResponse (real PascalCase divergence) and ErrorResponse (camelCase on case but missing errors field) into a single PascalCase claim |
Split into two bullets — PaginatedResponse is genuine PascalCase divergence; ErrorResponse is dead code with the runtime envelope already camelCase but missing the errors field |
| D8 | modules/controller_missions.md Notes #5 |
Cross-referenced "anonymous PascalCase JSON quirk as middleware" — wrong cross-ref since the middleware envelope is camelCase | Reworded to scope PascalCase to entity / DTO bodies and explicitly note that the global error envelope from the middleware is camelCase |
| D9 | modules/database.md § Internal Logic |
Said "2 CREATE INDEX IF NOT EXISTS statements" but listed 3 indexes |
Corrected to "3" and listed the index names: ix_missions_vehicle_id, ix_waypoints_mission_id, ix_map_objects_mission_id |
4.2 Flagged but NOT corrected (carry-forward — confirm with user)
| # | Where | Concern | Why not auto-fix |
|---|---|---|---|
| F1 | Cascade-delete error scenario in diagrams/flows/flow_mission_cascade_delete.md § Error Scenarios |
Text references "step 7" (a successful DELETE FROM missions) but the cascade order list above it numbers steps 1–5 and the data-flow table numbers them 1–8. Three different numberings in one file |
Pre-existing inconsistency; minor; correcting it would also need a numbering decision the user might prefer to make once globally |
| F2 | module-layout.md § Per-Component Mapping — 05_identity Public API |
Lists only the "FL" policy as the public API surface. Code today also exposes "GPS". Forward-looking is correct (B7 will drop "GPS"); the 05_identity/description.md already mentions the dual-policy state in its forward-looking note. Decision: leave module-layout.md forward-looking-only (consistent with the rest of the file), OR add a one-line "today also exposes \"GPS\" — see B7" caveat |
Editorial choice for the user — both readings are defensible |
| F3 | The pre-existing carry-forward divergences in 00_discovery.md § Spec ↔ Code Divergences (Geopoint shape, error envelope errors field, Swagger unconditional, etc.) — note: "CORS unconditional" was REMOVED from this list on 2026-05-14. CORS is gated by Infrastructure/CorsConfigurationValidator.cs; it throws in Production with implicit-permissive config and falls back to permissive (with PermissiveDefaultWarning) only in non-Production. See § 4.3 below |
All remaining items are real, already documented with their resolution path | These are the intentional carry-forward items |
4.3 Re-verification pass on 2026-05-14 (targeted)
While preparing autodev Step 4 (Code Testability Revision), a targeted code-level cross-check of Auth/JwtExtensions.cs, Program.cs, Infrastructure/{ConfigurationResolver, CorsConfigurationValidator}.cs, Database/DatabaseMigrator.cs, and Services/*.cs against the corresponding _docs/ artifacts surfaced that the original § 3 verdicts for F5 (JWT) and F6 (Startup) had been performed doc-vs-doc rather than against actual source. The actual code state is materially different from what the docs described. The findings were captured in _docs/02_document/05_drift_findings_2026-05-14.md; the doc revisions applied in this pass:
| Doc | Sections rewritten |
|---|---|
modules/auth.md |
Full rewrite — ECDSA + JWKS + ConfigurationManager + iss/aud + 30s skew + alg pin; no fallback |
modules/program.md |
Internal Logic block; Configuration table (6 keys); Security; External Integrations; Notes |
modules/database.md |
Internal Logic — explicit TIMESTAMP (not TIMESTAMPTZ), explicit REFERENCES, explicit DEFAULT clauses |
components/05_identity/description.md |
Full rewrite — same scope as modules/auth.md |
components/07_host/description.md |
Header source-of-truth note; Implementation Details (Configuration table + CORS gating); Caveats |
diagrams/flows/flow_jwt_validation.md |
Full rewrite — new sequence with JWKS resolver + algorithm-pin step + iss/aud branches |
diagrams/flows/flow_startup_migration.md |
Preconditions + sequence + flowchart + data-flow + error-scenarios — 4 required env vars + CORS gate |
architecture.md |
Architecture Vision; § 5 External Integrations; § 7 Security Architecture; § 3 Environment-specific config table; Tech Stack JWT row; ADR-005 (scope reduced) |
data_model.md |
§ 5 ERD — column-type annotations; § 6 Owned-table invariants — explicit FK / TIMESTAMP notes |
system-flows.md |
Cross-cutting #1 (JWT); F5 sequence + error table; F6 sequence + error table |
04_verification_log.md (this file) |
§ 3 rows F5 + F6 reissued; § 4.2 row F3 corrected; this § 4.3 block added |
00_problem/* (Phase 2 — next session) |
AC-5 group, AC-6.1/6.2, AC-9.1, AC-1.5/1.6/2.3, E1, E3, E4, E9 — see 05_drift_findings_2026-05-14.md Phase 2 |
_docs/02_document/tests/* (Phase 2 — next session) |
environment.md (JWKS mock), test-data.md, blackbox-tests.md (case-insensitive + ordering), security-tests.md (full NFT-SEC revision), resilience-tests.md (NFT-RES-05 + NFT-RES-07), traceability-matrix.md — see drift findings Phase 2 |
Root cause (recorded in _autodev_state.md for the retrospective): the prior verification step did doc-vs-doc consistency checks for these areas instead of opening the actual .cs files. The docs were internally consistent describing a stale HS256 / shared-secret / permissive-CORS / dev-fallback world that no longer exists in code. Subsequent verification passes (Step 4 prep, this reissue) must open source files for any flow whose verdict is "matches exactly" and explicitly note which files were read.
5. Stale-folder check (resolved)
The git status snapshot at session start showed 11 untracked component folders under _docs/02_document/components/ (6 new + 5 stale: 01_host, 02_auth, 03_web_infrastructure, 05_aircraft, 06_flight). Direct on-disk verification (ls _docs/02_document/components/) shows only 6 folders — the 5 stale entries do NOT exist. The git status was stale; no cleanup needed.
6. Verification metrics
| Metric | Count |
|---|---|
| Documents reviewed | 25 (00_discovery.md, architecture.md, system-flows.md, data_model.md, module-layout.md, 6 component descriptions, 12 module docs, 4 deployment docs, components diagram, 7 flow diagram files) |
| Source files cross-referenced | 37 .cs files + Dockerfile + Azaion.Flights.csproj + .woodpecker/build-arm.yml + README.md |
| Entities verified | 9 (today) ↔ 7 (post-target) — all reconcile under B6/B7 mapping |
| Service methods verified | 15 across 3 services — all reconcile under B6 mapping |
| HTTP endpoints verified | 16 (today: 6 vehicles + 5 missions + 4 waypoints + 1 health) — all reconcile under B6/B8 mapping |
| Doc-internal inconsistencies fixed inline | 9 (D1–D9 above; spans 8 files) |
| Real drift (not covered by rename) flagged for user | 2 (F1, F2 above) |
| Carry-forward divergences (already documented) | 15 (the 00_discovery.md § Spec ↔ Code Divergences table) |
| Hallucinated entities | 0 |
| Coverage | 12/12 modules documented; 6/6 components written; 4/4 deployment docs present; 7/7 flow files present |
| Completeness score | 100% (full coverage; no module or component left undocumented) |
7. Summary
- The forward-looking documentation is internally consistent with respect to the rename + GPS-Denied removal it describes (B5–B12).
- It is consistent with the actual pre-rename code when read through the rename mapping documented in § 0 — every counted symbol, signature, route, and flow reconciles, after the 2026-05-14 reissue corrected the F5 (JWT) and F6 (Startup) flow descriptions to match actual code.
- One systematic doc-internal inconsistency was found and fixed in the initial pass: the global error envelope's wire-shape case-style was misstated as PascalCase across 8 files when the middleware actually emits camelCase.
- One doc-vs-code drift was found and fixed in the 2026-05-14 reissue: the JWT model (ECDSA + JWKS + iss/aud + 30s skew + alg pin, fail-fast on missing env), the configuration model (
ResolveRequiredOrThrow— no hardcoded fallbacks), the CORS model (gated; Production hard-fail), and the DB schema details (TIMESTAMP, REFERENCES, DEFAULTs). The downstream test-spec re-issue is queued for the next autodev session (Phase 2 in05_drift_findings_2026-05-14.md). - No hallucinated entities or methods. No missing module or component coverage.
Outcome: docs are now accurate as the spec for B5–B12 AND faithful to the actual current behavior of the JWT / config / CORS / DB-schema surfaces. The next autodev pass continues from Phase 2 (test-spec scoped re-issue), then Phase 3 (resume Step 4 — Code Testability Revision).