[AZ-1124] Cycle 12 closure docs and cycle 13 task slate

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-06-26 13:21:00 +03:00
parent 7dac986996
commit b055450e40
11 changed files with 216 additions and 10 deletions
+3 -2
View File
@@ -263,12 +263,13 @@ Step 9 cycle 9: 2 tasks created (AZ-1074 = 5 pts, AZ-1075 = 3 pts) — total 8 p
Step 9 cycle 10: 1 task created (AZ-1113 = 2 pts) — REST 400 error message sanitization (F-AZ795-1/2, F-AZ810-1). Child of AZ-795.
Step 9 cycle 11: 1 task created (AZ-1123 = 1 pt) — document `docker-compose.perf.yml` host-port conflict playbook (cycle 10 retro action).
Step 9 cycle 12: 1 task created (AZ-1124 = 3 pts) — PT-10 gRPC `DeliverRouteTiles` stream perf scenario (cycle 911 retro carry-over).
Step 9 cycle 13: 1 task created (AZ-1126 = 2 pts) — `DateTime``DateTimeOffset` on `UavTileMetadata.capturedAt` (F-AZ810-2). Child of AZ-795.
### Step 9 cycle 12 (PT-10 gRPC stream perf — AZ-1124)
### Step 9 cycle 13 (capturedAt DateTimeOffset — AZ-1126)
| Task | Depends On | Points | Status |
|------|-----------|--------|--------|
| AZ-1124 PT-10 gRPC stream perf scenario | AZ-1074, AZ-1075, AZ-492 | 3 | Todo |
| AZ-1126 capturedAt DateTimeOffset (F-AZ810-2) | AZ-810, AZ-488 | 2 | Todo |
## Coverage Verification
@@ -0,0 +1,106 @@
# Migrate UAV upload capturedAt to DateTimeOffset
**Task**: AZ-1126_captured_at_datetimeoffset
**Name**: Migrate UavTileMetadata.capturedAt to DateTimeOffset (F-AZ810-2)
**Description**: Close security carry-over F-AZ810-2 by typing `UavTileMetadata.CapturedAt` as `DateTimeOffset` instead of `DateTime`, eliminating ambiguous `DateTimeKind.Unspecified` handling on the UAV upload metadata input path.
**Complexity**: 2 points
**Dependencies**: AZ-810 (HARD — metadata validation layer); AZ-488 (original upload endpoint)
**Component**: SatelliteProvider.Common (UavTileMetadata) + SatelliteProvider.Api (validators) + SatelliteProvider.Services.TileDownloader (quality gate + upload handler)
**Tracker**: AZ-1126
**Epic**: AZ-795
## Problem
Security finding F-AZ810-2 (cycle 8, open through cycle 12) flags that `UavTileMetadata.CapturedAt` is typed `DateTime` rather than `DateTimeOffset`. `DateTime` accepts `DateTimeKind.Unspecified` values that deserialize from offset-less ISO-8601 strings, forcing manual `Kind` normalization in the upload handler and quality gate. This is a time-handling correctness gap that can skew freshness-window checks in non-UTC dev environments.
## Outcome
- `capturedAt` on the UAV upload metadata input is unambiguously UTC-aware at the type level
- Offset-less or `Unspecified` timestamps are rejected before persistence
- F-AZ810-2 is marked resolved in the next security audit cycle
- Wire compatibility preserved for clients sending ISO-8601 UTC with explicit offset (`Z` or `+00:00`)
## Scope
### Included
- Change `UavTileMetadata.CapturedAt` from `DateTime` to `DateTimeOffset`
- Update FluentValidation rules, quality gate, and upload handler to compare via UTC without manual `Kind` branching
- Reject offset-less / ambiguous `capturedAt` values with HTTP 400
- Unit tests for the new rejection path and existing freshness-window rules
- Integration test proving offset-less `capturedAt` is rejected
- Patch `_docs/02_document/contracts/api/uav-tile-upload.md` 1.2.0 → 1.2.1 (change log + clarify offset requirement)
### Excluded
- `TileInventoryEntry.CapturedAt` response field (remains `DateTime?` — DB read path)
- `TileEntity.CapturedAt` persistence layer type
- Changes to gRPC tile delivery or other non-UAV-upload surfaces
- MAJOR contract version bump (wire JSON shape unchanged for compliant clients)
## Acceptance Criteria
**AC-1: Type migration**
Given the UAV upload metadata DTO
When deserialized from JSON
Then `CapturedAt` is `DateTimeOffset` and freshness comparisons use UTC without manual `DateTimeKind` normalization
**AC-2: Reject ambiguous timestamps**
Given a UAV upload batch with `capturedAt` lacking an explicit UTC offset (offset-less ISO string)
When POST `/api/satellite/upload`
Then HTTP 400 with a validation or deserialization error referencing `capturedAt`
**AC-3: Backward-compatible UTC clients**
Given a UAV upload batch with `capturedAt` as ISO-8601 UTC (`...Z` or `...+00:00`)
When POST `/api/satellite/upload` with otherwise valid payload
Then HTTP 200 (or the same non-timestamp rejection as before timestamp validation)
**AC-4: Contract patch**
Given the implementation is complete
When `uav-tile-upload.md` is reviewed
Then version is 1.2.1 with a change-log entry documenting the offset requirement and F-AZ810-2 closure
## Non-Functional Requirements
**Compatibility**
- Compliant clients already sending `Z`-suffixed timestamps must not break
**Security**
- Closes F-AZ810-2 (Low / informational time-handling finding)
## Unit Tests
| AC Ref | What to Test | Required Outcome |
|--------|-------------|-----------------|
| AC-1 | `UavTileMetadataValidator` freshness window with `DateTimeOffset` | Future/too-old rules still fire |
| AC-2 | Deserializer or validator with offset-less `capturedAt` | Rejected |
| AC-1 | `UavTileQualityGate` captured-at rules | Still accept/reject correctly |
## Blackbox Tests
| AC Ref | Initial Data/Conditions | What to Test | Expected Behavior | NFR References |
|--------|------------------------|-------------|-------------------|----------------|
| AC-2 | Valid JPEG + metadata with `capturedAt: "2026-06-26T12:00:00"` (no offset) | POST `/api/satellite/upload` | HTTP 400 mentioning `capturedAt` | — |
| AC-3 | Valid JPEG + metadata with `capturedAt` as `DateTime.UtcNow.ToString("o")` | POST `/api/satellite/upload` | HTTP 200 (happy path unchanged) | Compatibility |
## Constraints
- Must remain a child of epic AZ-795 strict-validation theme
- No breaking wire change for offset-aware clients
## Risks & Mitigation
**Risk 1: Client sends offset-less timestamps**
- *Risk*: Legitimate clients using `"2026-06-26T12:00:00"` without `Z` start failing
- *Mitigation*: Contract patch documents requirement; rejection is intentional per F-AZ810-2
## Contract
This task patches the producer contract at `_docs/02_document/contracts/api/uav-tile-upload.md` (1.2.0 → 1.2.1).
Consumers: `gps-denied-onboard`, mission planner UI, any UAV upload client.
### Document Dependencies
- `_docs/02_document/contracts/api/uav-tile-upload.md` v1.2.0 (patch to 1.2.1)
- `_docs/02_document/contracts/api/error-shape.md` v1.0.0 (unchanged)