mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-27 08:31:13 +00:00
[AZ-1126] Migrate capturedAt to DateTimeOffset
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
**Producer task**: AZ-488 — `_docs/02_tasks/done/AZ-488_uav_tile_upload.md`
|
||||
**Extended by**: AZ-503 — `_docs/02_tasks/done/AZ-503_tile_identity_uuidv5_bulk_list.md` (added optional `flightId` per-item field; per-flight on-disk path; deterministic `tileId`)
|
||||
**Consumer tasks**: `gps-denied-onboard`, mission planner UI, any future UAV-equipped client
|
||||
**Version**: 1.2.0
|
||||
**Version**: 1.2.1
|
||||
**Status**: frozen
|
||||
**Last Updated**: 2026-05-23
|
||||
|
||||
@@ -39,7 +39,7 @@ Multipart form fields (case-sensitive part names):
|
||||
| `longitude` | number | yes | Geographic longitude of the tile center | WGS-84 decimal degrees |
|
||||
| `tileZoom` | integer | yes | Slippy Map zoom level | Must satisfy the same zoom-level policy as the existing tile pipeline (see `MapConfig.AllowedZoomLevels`) |
|
||||
| `tileSizeMeters` | number | yes | Tile size in meters at the captured latitude | Producer-supplied |
|
||||
| `capturedAt` | string (ISO-8601 UTC) | yes | Moment of UAV image capture | Must satisfy the captured-at rule (see Quality Gate, Rule 4) |
|
||||
| `capturedAt` | string (ISO-8601 with explicit UTC offset) | yes | Moment of UAV image capture | Must include an explicit offset (`Z` or `+00:00`); offset-less timestamps are rejected. Must satisfy the captured-at rule (see Quality Gate, Rule 4) |
|
||||
| `flightId` | string (UUID) | no | AZ-503: optional flight identifier. When present, two flights uploading the same cell coexist as separate rows; absent uploads share a single anonymous row per cell. Omitting the field is fully backward-compatible with v1.0.0 clients. | RFC 4122 UUID. Backward-compatible default: `null` |
|
||||
|
||||
Field names are camelCase. Property-name matching is case-insensitive on read.
|
||||
@@ -238,3 +238,4 @@ Each version bump requires updating the Change Log and notifying every consumer
|
||||
| 1.0.0 | 2026-05-11 | Initial contract — batch UAV upload endpoint, 5-rule quality gate, per-source UPSERT, closed reject-reason enum, GPS-permission requirement. Produced by AZ-488. | autodev (cycle 2 step 10) |
|
||||
| 1.1.0 | 2026-05-12 | Minor bump for AZ-503: added optional `flightId` per-item metadata field (backward-compatible default `null`); `tileId` in the response is now a deterministic UUIDv5 derived from `(z, x, y, source, flightId)` instead of a random Guid; on-disk path adds a `{flightId or 'none'}` segment for per-flight evidence isolation. No reject-reason changes, no envelope changes, no permission changes. v1.0.0 clients omitting `flightId` keep working unchanged. | autodev (cycle 5 step 13) |
|
||||
| 1.2.0 | 2026-05-23 | Minor bump for AZ-810: added the **metadata validation** layer (14 rules) wired through a new `UavUploadValidationFilter` that runs BEFORE the per-item Quality Gate. Marks every non-optional metadata axis with `[JsonRequired]`; uses `UnmappedMemberHandling.Disallow` so unknown root/nested fields are rejected. HTTP 400 envelope-error body now matches the shared `ValidationProblemDetails` shape per `error-shape.md` v1.0.0. **Behavior change**: callers previously sending malformed metadata that silently coerced (e.g. `latitude: 0` for a missing field) now receive HTTP 400 instead of HTTP 200 + per-item rejection. No wire-format renames, no reject-reason changes, no permission changes. The Quality Gate (5 rules) is unchanged and continues as defence-in-depth. | autodev (cycle 8 batch 4) |
|
||||
| 1.2.1 | 2026-06-26 | Patch for AZ-1126 / F-AZ810-2: `capturedAt` is typed as `DateTimeOffset` at the metadata DTO layer. Offset-less ISO-8601 strings (e.g. `"2026-06-26T12:00:00"` without `Z` or `+00:00`) are rejected at deserialization with HTTP 400. Compliant clients already sending `Z`-suffixed timestamps are unchanged. Closes security finding F-AZ810-2. | autodev (cycle 13) |
|
||||
|
||||
@@ -269,7 +269,7 @@ Step 9 cycle 13: 1 task created (AZ-1126 = 2 pts) — `DateTime` → `DateTimeOf
|
||||
|
||||
| Task | Depends On | Points | Status |
|
||||
|------|-----------|--------|--------|
|
||||
| AZ-1126 capturedAt DateTimeOffset (F-AZ810-2) | AZ-810, AZ-488 | 2 | Todo |
|
||||
| AZ-1126 capturedAt DateTimeOffset (F-AZ810-2) | AZ-810, AZ-488 | 2 | Done (In Testing) |
|
||||
|
||||
## Coverage Verification
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Batch Report
|
||||
|
||||
**Batch**: 1
|
||||
**Tasks**: AZ-1126_captured_at_datetimeoffset
|
||||
**Date**: 2026-06-26
|
||||
**Cycle**: 13
|
||||
|
||||
## Task Results
|
||||
|
||||
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|
||||
|------|--------|---------------|-------|-------------|--------|
|
||||
| AZ-1126 | Done | 12 files | pass | 4/4 ACs covered | None |
|
||||
|
||||
## AC Test Coverage: All covered
|
||||
|
||||
| AC | Evidence |
|
||||
|----|----------|
|
||||
| AC-1 | `UavTileMetadataValidatorTests`, `UavTileQualityGateTests`, `UavTileUploadHandlerTests` |
|
||||
| AC-2 | `UtcOffsetRequiredDateTimeOffsetConverterTests`, `UavUploadValidationTests.ItemCapturedAtOffsetLess_Returns400` |
|
||||
| AC-3 | `UavUploadValidationTests.HappyPath_Returns200`, existing `UavUploadTests` suite |
|
||||
| AC-4 | `uav-tile-upload.md` v1.2.1 change log |
|
||||
|
||||
## Code Review Verdict: PASS_WITH_WARNINGS
|
||||
|
||||
Self-review: type migration is localized; filter now surfaces `JsonException.Message` for metadata parse failures (improves flightId and capturedAt diagnostics). No architecture drift.
|
||||
|
||||
## Auto-Fix Attempts: 1
|
||||
|
||||
Integration test initially failed because `UavUploadValidationFilter` replaced all `JsonException` messages with a generic parse error; fixed by propagating the converter message.
|
||||
|
||||
## Stuck Agents: None
|
||||
|
||||
## Full Test Run
|
||||
|
||||
`./scripts/run-tests.sh` (mode=full) — all unit + integration tests passed after filter fix.
|
||||
|
||||
## Next Batch: All tasks complete
|
||||
@@ -0,0 +1,16 @@
|
||||
# Product Implementation Completeness — Cycle 13
|
||||
|
||||
**Date**: 2026-06-26
|
||||
**Cycle**: 13
|
||||
|
||||
## Per-Task Classification
|
||||
|
||||
| Task | Verdict | Evidence |
|
||||
|------|---------|----------|
|
||||
| AZ-1126 | PASS | `UavTileMetadata.CapturedAt` is `DateTimeOffset` with `UtcOffsetRequiredDateTimeOffsetConverter`; validator, quality gate, and upload handler compare via `UtcDateTime`; contract patched to 1.2.1; integration + unit tests green |
|
||||
|
||||
## System Pipeline Audit
|
||||
|
||||
No new end-to-end pipelines introduced. UAV upload pipeline remains WIRED (filter → handler → quality gate → repository).
|
||||
|
||||
## Gate Verdict: PASS
|
||||
@@ -0,0 +1,26 @@
|
||||
# Implementation Report — capturedAt DateTimeOffset (Cycle 13)
|
||||
|
||||
**Cycle**: 13
|
||||
**Tasks**: AZ-1126
|
||||
**Date**: 2026-06-26
|
||||
|
||||
## Summary
|
||||
|
||||
Migrated `UavTileMetadata.CapturedAt` from `DateTime` to `DateTimeOffset` with a strict JSON converter requiring explicit UTC offsets. Closes security finding F-AZ810-2.
|
||||
|
||||
## Changes
|
||||
|
||||
- `UtcOffsetRequiredDateTimeOffsetConverter` rejects offset-less ISO-8601 strings at deserialization
|
||||
- FluentValidation freshness rules and quality gate use `UtcDateTime` without `DateTimeKind` branching
|
||||
- `UavUploadValidationFilter` propagates `JsonException.Message` for metadata parse failures
|
||||
- Contract `uav-tile-upload.md` 1.2.0 → 1.2.1
|
||||
|
||||
## Test Evidence
|
||||
|
||||
- Unit: `UtcOffsetRequiredDateTimeOffsetConverterTests`, updated UAV validator/gate/handler tests
|
||||
- Integration: `UavUploadValidationTests.ItemCapturedAtOffsetLess_Returns400`
|
||||
- Full suite: `./scripts/run-tests.sh` passed (mode=full)
|
||||
|
||||
## Handoff
|
||||
|
||||
Full test suite already executed in Step 10; Step 11 (Run Tests) may treat this as pre-verified per implement skill Step 16 handoff.
|
||||
+12
-10
@@ -2,21 +2,23 @@
|
||||
|
||||
## Current Step
|
||||
flow: existing-code
|
||||
step: 10
|
||||
name: Implement
|
||||
status: in_progress
|
||||
step: 11
|
||||
name: Run Tests
|
||||
status: not_started
|
||||
sub_step:
|
||||
phase: 1
|
||||
name: parse
|
||||
phase: 0
|
||||
name: awaiting-invocation
|
||||
detail: ""
|
||||
retry_count: 0
|
||||
cycle: 12
|
||||
cycle: 13
|
||||
tracker: jira
|
||||
auto_push: true
|
||||
|
||||
## Last Completed Cycle
|
||||
cycle: 11
|
||||
step_16_deploy: completed
|
||||
step_16_5_release: skipped (no release harness)
|
||||
cycle: 12
|
||||
step_14_security: skipped
|
||||
step_15_perf: completed
|
||||
step_16_deploy: skipped
|
||||
step_16_5_release: skipped
|
||||
step_17_retrospective: completed
|
||||
verdict: cycle_complete_operator_deploy
|
||||
verdict: cycle_complete
|
||||
|
||||
Reference in New Issue
Block a user