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.
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/flights:*-arm, dotnet Azaion.Flights.dll |
AZ-549 (B10), AZ-544 (B5) |
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).