Files
Oleksandr Bezdieniezhnykh 8e15e53782 chore: cycle 2 step 9 task plan artifacts + step 10 state
Carries forward new-task research + solution drafts under
_docs/02_task_plans/uav-batch-upload/ that were not included in
the Step 9 task-spec commit (42a3cc7). Also marks the autodev
state as Step 10 in_progress for cycle 2 implementation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 22:54:36 +03:00

96 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Research Problem — UAV Batch Upload (AZ-485 family)
## Context
- **Project**: SatelliteProvider — .NET 8.0 ASP.NET Core service that downloads/persists satellite imagery tiles. Currently single-source (Google Maps); AZ-484 just shipped multi-source storage (`source` + `captured_at` columns, 5-col unique index, `tile-storage` v1.0.0 frozen contract).
- **Cycle**: 2 (started after cycle 1 closed AZ-484 today, 2026-05-11).
- **Task family** (4 tasks under epic AZ-483, accepted by user):
- **AZ-485** — UAV batch upload endpoint + per-tile persistence loop (5 SP)
- **AZ-486** — Quality gate (6 checks + thresholds + reject reasons) (3 SP)
- **AZ-487** — JWT auth middleware (3 SP)
- **AZ-488** — Geofence whitelist (2 SP)
- **Confirmed user requirements** (from new-task Step 1):
1. Batch upload (multipart with metadata + N image files per request)
2. Minimum metadata only per tile: `latitude`, `longitude`, `tile_zoom`, `tile_size_meters`, `captured_at` (UTC), image bytes
3. Quality gate enforces all of: dimensions, byte size, blank/uniform detection, captured_at age, format=JPEG, geofence containment
4. JWT auth (HS256, signing key in env, required claims: `sub` + valid `exp`)
5. Sync responses: 200 + per-tile `{ tileId, status, reason? }` array; 4xx for auth/format-of-batch failures
6. File storage: same `./tiles/{z}/{x}/{y}.jpg` layout as Google Maps tiles
- **Project constraints to respect**:
- .NET 8.0 only (no .NET 9 features)
- Existing libraries: ImageSharp 3.1.11, Npgsql 9.0.2, Dapper 2.1.35, Serilog 8.0.3
- No auth currently exists in the API (per Security Audit Step 14, 2026-05-11)
- The "geofence" type in the codebase (`SatelliteProvider.Common.DTO.GeofencePolygon`) is actually a bounding box (NorthWest + SouthEast corners), not an arbitrary polygon. There is no existing point-in-polygon implementation.
- The frozen `_docs/02_document/contracts/data-access/tile-storage.md` v1.0.0 is the producer-side contract this work must implement (`source='uav'`, `captured_at` per-row, per-source UPSERT)
- Lessons applied: L-001 (Dapper enum bypass — no new persisted enums planned but the rule applies if any sneak in)
## Specific unknowns to investigate
### Q1 — JWT auth middleware in ASP.NET Core 8 for an internal service (AZ-487)
- Recommended pattern for an internal/trusted-network API that needs to validate HS256 tokens issued by an external system?
- `Microsoft.AspNetCore.Authentication.JwtBearer` (built-in, recommended? version-pinned for ASP.NET Core 8)
- vs. third-party (e.g., custom middleware, OpenIddict, etc.)
- Configuration shape — where does the signing key live? appsettings vs env var vs key vault? (Project constraint: this codebase has no secret manager today.)
- Required claims minimum: just `sub` and unexpired `exp` per user choice. Confirm `JwtBearerOptions.TokenValidationParameters` setup that validates *only* signature + `exp` and tolerates missing `aud`/`iss` (or reject — which is the safer ASP.NET Core 8 default?).
- Swagger integration: how to surface "Bearer <token>" in the Swagger UI for the upload endpoint without changing the existing public endpoints?
- 401 response shape — does the global `GlobalExceptionHandler` need to know about it, or does `[Authorize]` short-circuit before the handler?
- Test strategy — how do current `SatelliteProvider.IntegrationTests` (which call the live API via `HttpClient` per `RouteTestHelpers`) cleanly inject a valid JWT? Pattern for a "test issuer" using the same signing key.
### Q2 — Multipart batch upload in ASP.NET Core 8 minimal API (AZ-485)
- Idiomatic shape for `multipart/form-data` with N files + a JSON metadata document in a minimal API endpoint:
- `[FromForm]` model binding
- vs. manual `MultipartReader` for streaming (relevant if batches are large)
- Request size limits in ASP.NET Core 8 (Kestrel `MaxRequestBodySize` and form options) — defaults and how to override for the upload endpoint only.
- Memory / streaming trade-off: how big can the per-batch `IFormFile[]` get before we should switch to streaming via `MultipartReader`? Practical batch-size guidance for satellite tiles (~50500 KB each).
- Response shape for partial-success batches — common patterns (HTTP 200 with mixed array, HTTP 207 Multi-Status, HTTP 422 with per-item errors).
- Backpressure / concurrency limit on the per-tile persistence loop — should this be sequential or fanned out? Trade-off vs. Postgres connection pool size (current Npgsql defaults).
### Q3 — Image quality heuristics for satellite tiles using ImageSharp 3.1.11 (AZ-486)
- API surface in **ImageSharp 3.1.11 specifically** for:
- Reading width/height without full decode (`Image.IdentifyAsync` returns `ImageInfo`)
- Detecting actual format from magic bytes vs. trusting `Content-Type`
- Computing a "uniformity / blankness" metric efficiently — luminance stddev across the image, or histogram-based, or a perceptual hash
- Performance budget: per-tile quality check ideally under ~10ms on a 256×256 JPEG. Confirm which ImageSharp APIs are streaming vs. full-decode.
- Robustness against adversarial inputs — e.g., a 1-pixel image with a JPEG header. What does ImageSharp 3.1.11 throw, and is there a safe-decode pattern?
- Threshold-tuning approach — recommended way to tune the blank-detection threshold against a small fixture set of real satellite tiles vs. uniform tiles, without having that fixture set today.
### Q4 — Geofence whitelist: bbox vs. polygon (AZ-488)
- The codebase's existing geofence type is a **bounding box** (`NorthWest`+`SouthEast`). Two options for the AZ-488 whitelist:
- **Option A**: reuse the existing bbox shape (simple, consistent with current code, but coarser — a UAV tile inside a non-rectangular real-world allowed area can't be expressed)
- **Option B**: introduce a true polygon (lat/lon list) + ray-casting point-in-polygon, store as JSON in config. Requires writing the ray-cast utility (or pulling in `NetTopologySuite`).
- Recommendation: which one is the right fit given that the user only said "geofence" without specifying shape and the existing code is bbox? Compare with NetTopologySuite as a third option.
- Storage: where does the whitelist live? `appsettings.json` array, dedicated config file, dedicated DB table?
- Performance: typical UAV batch is N tiles vs. M whitelist polygons — is M small enough (e.g., < 10 polygons, < 1000 vertices) that O(N×M) ray-cast per batch is fine?
### Q5 — Per-tile persistence loop: file-system + DB consistency (AZ-485)
- Pattern for "write JPEG to disk, then write DB row" with rollback on either failure:
- Two-phase: write file with a `.tmp` suffix, INSERT, then rename. On INSERT failure, delete `.tmp`.
- vs. write DB row first (ON CONFLICT does UPSERT), then write file. On file failure, what happens to the row? (Could leave a row pointing at a missing file path — bad.)
- Idempotency: if the same (lat, lon, zoom, size, source='uav') tile is uploaded twice, the per-source UPSERT already handles the DB side — but does the file get overwritten cleanly? Atomic file replace pattern.
- Concurrency: two simultaneous batch uploads for adjacent tiles — any shared resource that needs locking?
## Out of scope for this research
- Distributed UAV fleet management, queueing, or real-time streaming (these are upstream concerns the user has not raised)
- Async/202 + status polling response model (user explicitly said sync 200)
- Per-tile encryption, compression beyond JPEG, or alternate image formats
- External IdP integration (HS256 with shared secret only)
- UAV mission/operator/sensor metadata (user explicitly said "not necessary for now")
## Acceptance criteria for the research output
The research deliverable (`solution_draft01.md`) must:
1. Recommend a concrete library + config approach for **Q1 (JWT)**. Include: package name + version, code shape for `builder.Services.AddAuthentication().AddJwtBearer(...)`, key storage recommendation, integration-test pattern.
2. Recommend a concrete request shape + size-limit approach for **Q2 (multipart)**. Include: `[FromForm]` vs streaming decision criteria, response shape choice with rationale, concurrency-limit recommendation.
3. Recommend a concrete API call sequence in **ImageSharp 3.1.11** for **Q3 (quality heuristics)**. Include: blank-detection threshold starting point + tuning plan, performance budget validation, error-handling pattern.
4. Recommend bbox-vs-polygon-vs-NTS for **Q4 (geofence)**. Include: exact-fit verdict per the project's actual operating context (M < 10 polygons, simple ray-cast acceptable, or pull in NTS).
5. Recommend a file+DB consistency pattern for **Q5**. Include: failure modes covered, rollback shape, idempotency story.
6. Include a "do not use" / rejected list for each question — alternatives the research considered and rejected with one-line evidence.
## Execution mode
Mode A — Initial Research. Output class: **Technical-component selection** (recommends specific libraries, ASP.NET Core 8 modes, ImageSharp APIs, optional NTS).
Per-mode API capability verification applies to every recommended library. Saved Minimum Viable Examples required for: `Microsoft.AspNetCore.Authentication.JwtBearer` JWT validation, ASP.NET Core 8 minimal-API multipart, ImageSharp 3.1.11 luminance stddev.