Spaces:
Sleeping
Sleeping
| # DDA Change Detection β Implementation Plan (Dev Space Only) | |
| **Document:** Scope of Work β Engineering Plan | |
| **Client:** DDA (Delhi Development Authority) | |
| **Target environment:** Hugging Face **`satdetect-dev`** only (`hf-dev` remote, `master` branch) | |
| **Production (`satdetect`) must not receive these changes until explicit sign-off.** | |
| --- | |
| ## 1. Executive summary | |
| The SOW transforms the current βupload two PNGs β run detection β view historyβ demo into a **departmental change-monitoring system** with: | |
| - A **persistent image library** (GeoTIFF, hierarchical metadata, search) | |
| - **Library-driven comparison** (Base T1 vs Comparison T2) | |
| - **Async AI detection** with geo-referenced outputs | |
| - **Reports** (browser + PDF + email) | |
| - **Geo-navigation** and **clickable regions** | |
| - **Human review** (Confirmed / False Positive) and **export to departmental systems** | |
| The existing codebase is a strong **Phase-0 foundation**: AdaptFormer-based detection, region classification, overlay UI, email notifier, and history. Roughly **40% of FR-07** exists; **FR-01βFR-06 and FR-08 are largely net-new**. | |
| **Recommended approach:** Implement behind a **`APP_MODE=dda`** feature flag on dev, extend the database and API vertically, and keep production on the current simplified flow indefinitely. | |
| --- | |
| ## 2. SOW requirements β current state | |
| | ID | Requirement | Current state | Gap severity | | |
| |----|-------------|---------------|--------------| | |
| | **FR-01** | Centralized image library (Zone β Village β Area β Year or Grid) | Zone/Village are free-text dropdowns on upload; images discarded after run except thumbnails | **Large** | | |
| | **FR-02** | GeoTIFF upload + validation + manual georef fallback | PNG/JPEG only, 20 MB, Pillow RGB load | **Large** | | |
| | **FR-03** | Pick Base/Comparison from library or drag from tree | Local file upload only | **Medium** (depends on FR-01) | | |
| | **FR-04** | AI detection as background job; simplified change types; lat/lng per region | Synchronous `/api/detect`; rich internal taxonomy; pixel coords only | **MediumβLarge** | | |
| | **FR-05** | In-app notification + report link; PDF export; persistent reports | Email optional; HTML modal report; no PDF; history exists | **Medium** | | |
| | **FR-06** | Navigate to change by lat/lng; Locate action | No geospatial metadata | **Large** (depends on FR-02) | | |
| | **FR-07** | Clickable regions auto-pan viewer; side-by-side Base/Comparison | Region row hover + compare slider partially exist | **SmallβMedium** | | |
| | **FR-08** | Confirm / False Positive; submit to dept API or export | Not implemented | **Medium** | | |
| ### SOW change-type mapping (canonical output) | |
| Map internal `detection_engine` labels β DDA report types: | |
| | DDA type | Internal sources (examples) | | |
| |----------|------------------------------| | |
| | **New Construction** | New Construction/Building, Temporary Structure, Road/Pavement Change (New) | | |
| | **Demolition** | Demolition/Clearing, Partial/Full Demolition | | |
| | **Extension** | Expansion, Widening, Renovation | | |
| | **Vegetation Change** | Vegetation Change (+ sub-types) | | |
| | **Other** | Water Body, Bare Land/Soil, Unclassified | | |
| Store both `dda_change_type` (report) and `internal_object_type` (model/debug) on each region. | |
| --- | |
| ## 3. Deployment & branching strategy | |
| ``` | |
| master (DDA features) ββpushβββΊ hf-dev β coderuday21/satdetect-dev | |
| production ββpushβββΊ hf β coderuday21/satdetect (frozen) | |
| ``` | |
| ### Dev-only safeguards | |
| 1. **`APP_MODE` env var** on `satdetect-dev`: `dda` enables library, GeoTIFF, jobs, RBAC, review UI. Default / production: `legacy`. | |
| 2. **Separate DB file** on dev: `data/dda_app.db` (avoid mixing guest history with library assets). | |
| 3. **All new routes** under `/api/dda/*` or gated by `APP_MODE` in `main.py`. | |
| 4. **CI checklist** before any production promote: diff must not touch DDA modules unless mode flag documented. | |
| --- | |
| ## 4. Target architecture | |
| ```mermaid | |
| flowchart TB | |
| subgraph ui [Frontend SPA] | |
| LIB[Image Library Tree + Search] | |
| CMP[Comparison Picker T1/T2] | |
| JOB[Job Status / Notifications] | |
| RPT[Report Viewer + PDF] | |
| REV[Review Confirmed / FP] | |
| end | |
| subgraph api [FastAPI] | |
| AUTH[RBAC Auth] | |
| IMG[Image Library API] | |
| DET[Detection Job API] | |
| RPTAPI[Report + Export API] | |
| DEPT[Department Export Adapter] | |
| end | |
| subgraph data [Storage] | |
| DB[(SQLite / PostgreSQL)] | |
| FS[File Store: GeoTIFF + pyramids + overlays] | |
| JQ[Job Queue Table] | |
| end | |
| subgraph proc [Processing] | |
| GEO[rasterio ingest + CRS normalize] | |
| PIPE[detection_engine.run_detection] | |
| GEOOUT[Region centroid β lat/lng] | |
| end | |
| LIB --> IMG | |
| CMP --> DET | |
| DET --> JQ | |
| JQ --> proc | |
| proc --> DB | |
| proc --> FS | |
| JOB --> DET | |
| RPT --> RPTAPI | |
| REV --> DEPT | |
| AUTH --> api | |
| ``` | |
| --- | |
| ## 5. Data model (new & extended) | |
| ### 5.1 Hierarchy (FR-01) | |
| Two organization modes (user selects at library root): | |
| **A. Administrative (default for DDA Delhi)** | |
| `Zone` β `Village` β `Area` β `Year` β `[ImageAssetβ¦]` | |
| **B. Grid** | |
| `GridId` (e.g. `G-12-04`) β `Year` β `[ImageAssetβ¦]` | |
| Implement as normalized tables (not hardcoded JS): | |
| ```text | |
| zones(id, name, mode) -- mode: admin | grid_parent | |
| villages(id, zone_id, name) -- nullable for grid mode | |
| areas(id, village_id, name) | |
| library_years(id, area_id, year) -- optional grouping node; images also store year directly | |
| ``` | |
| For MVP, **Area** can be a free-text field on `ImageAsset` if DDA taxonomy is not finalized. | |
| ### 5.2 `ImageAsset` (FR-01, FR-02) | |
| | Column | Purpose | | |
| |--------|---------| | |
| | `id`, `uuid` | Primary identifiers | | |
| | `zone_id`, `village_id`, `area_name`, `year`, `grid_id` | Hierarchy | | |
| | `capture_date` | Required metadata | | |
| | `source` | `satellite` \| `drone` | | |
| | `format` | `geotiff` | | |
| | `file_path`, `thumb_path`, `preview_path` | Storage | | |
| | `crs`, `bounds_json` | Geo metadata (WGS84 bbox) | | |
| | `width`, `height`, `file_size_bytes` | Validation | | |
| | `has_georef` | bool | | |
| | `manual_location_json` | Fallback corner coords + notes if no embedded georef | | |
| | `uploaded_by`, `created_at` | Audit | | |
| ### 5.3 `DetectionJob` (FR-04 async) | |
| | Column | Purpose | | |
| |--------|---------| | |
| | `id`, `status` | `queued` \| `running` \| `completed` \| `failed` | | |
| | `base_image_id`, `comparison_image_id` | FK β ImageAsset | | |
| | `method`, `params_json` | Detection settings | | |
| | `run_id` | FK β DetectionRun when done | | |
| | `error_message`, `started_at`, `completed_at` | Job lifecycle | | |
| | `notify_email`, `notify_in_app` | FR-05 | | |
| ### 5.4 Extend `DetectionRun` | |
| Add: `base_image_id`, `comparison_image_id`, `job_id`, `report_pdf_path`, `status` (`draft` \| `published`). | |
| ### 5.5 Extend region JSON schema | |
| Each region gains: | |
| ```json | |
| { | |
| "ddaChangeType": "New Construction", | |
| "confidence": 0.87, | |
| "areaSqM": 142.5, | |
| "centroid": { "x": 412, "y": 288 }, | |
| "latLng": { "lat": 28.6139, "lng": 77.2090 }, | |
| "bbox": { "x": 380, "y": 260, "w": 64, "h": 56 }, | |
| "reviewStatus": "pending", | |
| "reviewedBy": null, | |
| "reviewedAt": null | |
| } | |
| ``` | |
| ### 5.6 `RegionReview` + export batch (FR-08) | |
| ```text | |
| region_reviews(id, run_id, region_id, status, reviewer_id, notes, submitted_at) | |
| export_batches(id, run_id, format, payload_path, dept_api_response, created_at) | |
| ``` | |
| ### 5.7 RBAC (FR-01, FR-02) | |
| Extend `User`: | |
| ```text | |
| role: viewer | uploader | analyst | admin | |
| ``` | |
| | Role | View library | Upload | Run detection | Review/submit | | |
| |------|--------------|--------|---------------|---------------| | |
| | viewer | β | | | | | |
| | uploader | β | β | | | | |
| | analyst | β | β | β | β | | |
| | admin | β | β | β | β + manage hierarchy | | |
| **Dev Space:** Re-enable login UI when `APP_MODE=dda`. Production stays guest/no-login. | |
| --- | |
| ## 6. API design (dev / DDA mode) | |
| ### 6.1 Image library | |
| | Method | Path | Description | | |
| |--------|------|-------------| | |
| | `GET` | `/api/dda/hierarchy` | Tree: zones β villages β areas β years (+ counts) | | |
| | `GET` | `/api/dda/images` | Filter: `zone`, `village`, `area`, `year`, `date_from`, `date_to`, `grid_id`, `q` | | |
| | `POST` | `/api/dda/images/upload` | Multipart GeoTIFF + metadata form | | |
| | `GET` | `/api/dda/images/{id}` | Metadata + thumb URL | | |
| | `GET` | `/api/dda/images/{id}/preview` | Downsampled PNG/Web tile for viewer | | |
| | `DELETE` | `/api/dda/images/{id}` | Admin only | | |
| **Upload validation (FR-02):** | |
| 1. Extension `.tif` / `.tiff` | |
| 2. Max size: **500 MB dev** (configurable; DDA responsible for suitable resolution per SOW) | |
| 3. `rasterio.open()` β verify readable, extract CRS/transform | |
| 4. If no georef: require `manual_bounds` (SW/NE lat-lng) + `capture_date` before commit | |
| 5. Generate 512px thumbnail + 2048px preview for UI | |
| 6. Return `{ id, status: "success" | "failed", errors[] }` | |
| ### 6.2 Comparison & detection jobs | |
| | Method | Path | Description | | |
| |--------|------|-------------| | |
| | `POST` | `/api/dda/jobs` | Body: `{ baseImageId, comparisonImageId, method?, notifyEmail? }` β `{ jobId }` | | |
| | `GET` | `/api/dda/jobs/{id}` | Status + progress + runId when complete | | |
| | `GET` | `/api/dda/jobs` | User's recent jobs (in-app notification feed) | | |
| | `POST` | `/api/dda/detect` | **Legacy sync path** for small previews only (keep for HF debugging) | | |
| **Job runner logic:** | |
| ```text | |
| POST /jobs β insert DetectionJob(queued) | |
| β return 202 + jobId | |
| Background worker thread: | |
| 1. Load ImageAsset paths via rasterio | |
| 2. Align pair (same CRS; reproject if needed) | |
| 3. Windowed read β RGB numpy arrays (max 1600px for DL) | |
| 4. run_detection(...) | |
| 5. For each region: pixel centroid β lat/lng via transform | |
| 6. Map object types β ddaChangeType | |
| 7. Save DetectionRun + overlay + update job completed | |
| 8. Fire notification (in-app row + optional email with report link) | |
| ``` | |
| **HF timeout mitigation:** Job endpoint returns immediately; worker runs in-process with status polling. For GeoTIFFs >60s, use **pyramid preview** for detection (DDA supplies suitable resolution) and store full metadata separately. Document in UI: βAnalysis uses optimized resolution; full GeoTIFF retained in library.β | |
| ### 6.3 Reports (FR-05) | |
| | Method | Path | Description | | |
| |--------|------|-------------| | |
| | `GET` | `/api/dda/reports/{run_id}` | Full report JSON (existing shape + geo fields) | | |
| | `GET` | `/api/dda/reports/{run_id}/pdf` | Generate/download PDF | | |
| | `POST` | `/api/dda/reports/{run_id}/notify` | Email with link to report | | |
| **PDF engine:** `weasyprint` or `reportlab` β HTML template reuse from `templates/ChangeDetection.html`. | |
| **In-app notification:** Bell icon + `/api/dda/jobs?status=completed&unseen=true`. | |
| ### 6.4 Geo-navigation (FR-06, FR-07) | |
| No external map SDK required for MVP β **image viewer geo mode**: | |
| - Report shows `lat, lng` per region | |
| - **Locate** button: pan/zoom canvas to bbox center at 200% zoom, pulse highlight | |
| - Optional Phase 2: Leaflet minimap with footprint rectangle | |
| Frontend already has region hover β bbox highlight; extend to **click row β scroll viewer + zoom**. | |
| Side-by-side: keep existing compare slider; add locked **T1 | T2 | Overlay** tabs when images loaded from library. | |
| ### 6.5 Review & departmental export (FR-08) | |
| | Method | Path | Description | | |
| |--------|------|-------------| | |
| | `PATCH` | `/api/dda/reports/{run_id}/regions/{region_id}` | `{ reviewStatus: confirmed \| false_positive, notes? }` | | |
| | `POST` | `/api/dda/reports/{run_id}/submit` | Submit confirmed only | | |
| | `GET` | `/api/dda/reports/{run_id}/export.{csv\|xlsx\|pdf}` | Structured export | | |
| **Department adapter pattern:** | |
| ```python | |
| class DepartmentExporter(Protocol): | |
| def submit(self, run: DetectionRun, regions: list) -> ExportResult: ... | |
| class ApiExporter(DepartmentExporter): # DEPT_API_URL + DEPT_API_KEY | |
| class FileExporter(DepartmentExporter): # CSV/XLSX fallback | |
| ``` | |
| False positives: persist in `region_reviews` with `status=false_positive` for future training export (`/api/dda/training/export` β admin only, Phase 2). | |
| --- | |
| ## 7. GeoTIFF processing pipeline (accurate logic) | |
| ### 7.1 Ingest | |
| ```python | |
| def ingest_geotiff(path: Path) -> GeoImageMeta: | |
| with rasterio.open(path) as src: | |
| crs = src.crs | |
| bounds = transform_bounds(src.crs, "EPSG:4326", *src.bounds) | |
| has_georef = crs is not None and src.transform != rasterio.identity() | |
| # Prefer RGB or first 3 bands; handle 1-band grayscale | |
| preview = read_downsampled(src, max_side=2048) | |
| return meta | |
| ``` | |
| **Dependencies to add (dev only branch):** `rasterio`, `pyproj` (GDAL wheels on Docker). | |
| ### 7.2 Pair compatibility check (before job starts) | |
| | Check | Action | | |
| |-------|--------| | |
| | Same CRS or reproject T2 β T1 CRS | Reproject comparison raster | | |
| | Overlapping bounds | Warn if <50% overlap | | |
| | Band count | Harmonize to RGB | | |
| | Resolution ratio | Warn if >2Γ GSD difference | | |
| ### 7.3 Pixel β lat/lng | |
| ```python | |
| def region_centroid_latlng(region_bbox, transform, crs): | |
| cx = region_bbox.x + region_bbox.w / 2 | |
| cy = region_bbox.y + region_bbox.h / 2 | |
| lng, lat = rasterio.transform.xy(transform, cy, cx, offset="center") | |
| if crs != EPSG:4326: | |
| lng, lat = transform_coords(crs, 4326, lng, lat) | |
| return lat, lng | |
| ``` | |
| ### 7.4 Area in mΒ² | |
| Use pixel area Γ GSDΒ² from transform (for projected CRS in meters). | |
| --- | |
| ## 8. Frontend plan (dev UI) | |
| Replace single upload card with **tabbed workflow** when `APP_MODE=dda`: | |
| ### Tab 1 β Image Library | |
| - Left: collapsible tree (Zone β Village β Area β Year) | |
| - Right: grid of thumbnails + metadata | |
| - Upload modal: GeoTIFF + form (hierarchy, capture date, source, manual bounds if needed) | |
| - Search bar + filters | |
| ### Tab 2 β Change Detection | |
| - Two slots: **Base (T1)** | **Comparison (T2)** | |
| - Drag image from library OR picker dialog | |
| - Show thumb + capture date for each | |
| - Run β job submitted β progress spinner β auto-open report on complete | |
| ### Tab 3 β Reports & Review | |
| - List of completed runs | |
| - Open report β regions table with DDA types, lat/lng, confidence, area | |
| - **Locate** per row; click row β pan viewer | |
| - Toggle Confirmed / False Positive | |
| - Submit to department / Download export | |
| ### Tab 4 β Notifications | |
| - Job completion feed (FR-05 in-app) | |
| **Auth screens:** Login/register return on dev only (`APP_MODE=dda`). | |
| --- | |
| ## 9. Implementation phases | |
| ### Phase 0 β Foundation (3β4 days) | |
| - [ ] `APP_MODE` flag + route gating in `main.py` | |
| - [ ] Alembic-style migrations or existing startup migrations for new tables | |
| - [ ] `rasterio` in `requirements.txt` + Docker GDAL base image update (dev Dockerfile branch) | |
| - [ ] Seed script: import current `DELHI_ZONES` into DB | |
| - [ ] Dev README section | |
| ### Phase 1 β Image library (FR-01, FR-02) (5β7 days) | |
| - [ ] Models: hierarchy + ImageAsset | |
| - [ ] Upload API + validation + thumbnail generation | |
| - [ ] Library tree + search UI | |
| - [ ] RBAC: uploader vs viewer | |
| ### Phase 2 β Library-based comparison (FR-03) (2β3 days) | |
| - [ ] T1/T2 picker from library | |
| - [ ] Drag-and-drop from tree to comparison slots | |
| - [ ] Preview + capture date display | |
| ### Phase 3 β Async detection + geo output (FR-04, FR-06) (5β6 days) | |
| - [ ] DetectionJob worker + polling API | |
| - [ ] GeoTIFF β RGB pipeline integration with `detection_engine` | |
| - [ ] DDA change type mapping + lat/lng + area mΒ² on regions | |
| - [ ] Job failure handling + user messaging | |
| ### Phase 4 β Reports & notifications (FR-05) (3β4 days) | |
| - [ ] In-app notification feed | |
| - [ ] Report page (browser) | |
| - [ ] PDF export | |
| - [ ] Email with report deep link | |
| ### Phase 5 β Interactive viewer (FR-07) (2β3 days) | |
| - [ ] Locate button + click-to-pan/zoom | |
| - [ ] Side-by-side T1/T2/overlay modes | |
| - [ ] Region highlight sync (extend existing hover logic) | |
| ### Phase 6 β Review & export (FR-08) (4β5 days) | |
| - [ ] Per-region review state | |
| - [ ] CSV/XLSX/PDF export of confirmed changes | |
| - [ ] Department API adapter (config-driven) | |
| - [ ] False-positive archive for training | |
| ### Phase 7 β Hardening & UAT (3β5 days) | |
| - [ ] Load test with sample GeoTIFFs from DDA | |
| - [ ] Alignment QA on drone vs satellite pairs | |
| - [ ] Security review (auth, upload limits, path traversal) | |
| - [ ] Deploy checklist for `satdetect-dev` | |
| **Total estimate:** ~25β35 working days (1 senior dev), assuming DDA provides sample GeoTIFFs and hierarchy taxonomy early. | |
| --- | |
| ## 10. Hugging Face dev constraints & mitigations | |
| | Constraint | Impact | Mitigation | | |
| |------------|--------|------------| | |
| | ~60s HTTP timeout (cpu-basic) | Sync detect fails on large TIFF | Async jobs + immediate 202 response; detection on downsampled preview | | |
| | Ephemeral disk | Library loss on rebuild | HF persistent storage volume; document backup to S3 optional | | |
| | No GPU | Slow DL | Keep AdaptFormer on CPU tiles; cap image size; queue one job at a time | | |
| | 50 GB space limit | Large GeoTIFF library | Per-file quota; admin purge; external blob store Phase 2 | | |
| | Single worker | No true queue | SQLite job lock; one `running` job at a time | | |
| **Dockerfile (dev):** Switch base to `osgeo/gdal:ubuntu-small` or install `libgdal` in existing image. | |
| --- | |
| ## 11. File / module layout (new code) | |
| ```text | |
| app/ | |
| βββ dda/ | |
| β βββ __init__.py | |
| β βββ config.py # APP_MODE, limits, DEPT_API_* | |
| β βββ models_library.py # ImageAsset, hierarchy ORMs | |
| β βββ models_jobs.py # DetectionJob, RegionReview, ExportBatch | |
| β βββ geotiff_io.py # rasterio ingest, reproject, preview | |
| β βββ geo_regions.py # pixelβlat/lng, area mΒ² | |
| β βββ change_type_map.py # internal β DDA types | |
| β βββ job_runner.py # async detection worker | |
| β βββ library_routes.py # FR-01, FR-02 API | |
| β βββ jobs_routes.py # FR-04 API | |
| β βββ reports_routes.py # FR-05, PDF | |
| β βββ review_routes.py # FR-08 | |
| β βββ dept_export.py # adapter pattern | |
| βββ static/js/dda/ | |
| β βββ library.js | |
| β βββ comparison.js | |
| β βββ jobs.js | |
| β βββ review.js | |
| βββ templates/ | |
| βββ index.html # legacy (production) | |
| βββ index_dda.html # dev SPA shell | |
| ``` | |
| Wire routers in `main.py`: | |
| ```python | |
| if os.getenv("APP_MODE") == "dda": | |
| app.include_router(library_routes.router, prefix="/api/dda") | |
| ... | |
| ``` | |
| Set on **`satdetect-dev`** Space: `APP_MODE=dda`. | |
| Leave **`satdetect`** unset β legacy behavior. | |
| --- | |
| ## 12. Testing strategy | |
| | Layer | Tests | | |
| |-------|-------| | |
| | GeoTIFF ingest | Unit: georef / no-georef / 1-band / 4-band samples | | |
| | Pair validation | CRS mismatch, non-overlap | | |
| | Job lifecycle | queued β running β completed/failed | | |
| | lat/lng accuracy | Known corner coords β centroid within tolerance | | |
| | DDA type mapping | Snapshot tests on classified regions | | |
| | RBAC | Role forbidden on upload/delete | | |
| | PDF | Report renders all mandatory fields | | |
| | E2E | Upload 2 TIFFs β job β report β confirm β CSV export | | |
| Use `scripts/validate_detection.py` extended with GeoTIFF fixtures. | |
| --- | |
| ## 13. Risks & dependencies | |
| | Risk | Owner | Mitigation | | |
| |------|-------|------------| | |
| | DDA GeoTIFF quality / GSD | DDA (per SOW) | Pre-upload validation messages | | |
| | No departmental API spec | DDA IT | File export fallback (FR-08) | | |
| | Area taxonomy not finalized | DDA | Configurable hierarchy admin UI | | |
| | Model accuracy on drone imagery | Shared | Alignment warnings; false-positive review loop | | |
| | HF CPU timeout | Engineering | Async jobs + downsample policy | | |
| **Blocked until DDA provides:** | |
| 1. Sample GeoTIFF pairs (georeferenced + one without georef) | |
| 2. Zone/Village/Area master list (or confirm Delhi list) | |
| 3. Department API spec OR confirm export-only | |
| 4. Grid definition if grid mode required at launch | |
| --- | |
| ## 14. Out of scope (explicit) | |
| - Training/fine-tuning new DL models (use existing AdaptFormer + review feedback archive for Phase 2) | |
| - Full GIS desktop analysis (QGIS replacement) | |
| - Real-time satellite ingestion feeds | |
| - Mobile native apps | |
| - Production deployment of DDA features (until UAT sign-off) | |
| --- | |
| ## 15. Immediate next steps | |
| 1. **Create `satdetect-dev` Space env:** `APP_MODE=dda` | |
| 2. **Phase 0 PR** to `master` β `git push hf-dev master:main` | |
| 3. **Request from DDA:** 3 sample GeoTIFF pairs + hierarchy spreadsheet | |
| 4. **Implement Phase 1** (library + upload) β highest dependency for all other FRs | |
| --- | |
| ## Appendix A β Legacy endpoint compatibility | |
| Keep existing endpoints working in legacy mode: | |
| | Legacy | DDA equivalent | | |
| |--------|----------------| | |
| | `POST /api/detect` (multipart files) | `POST /api/dda/jobs` (library IDs) | | |
| | `GET /api/history` | `GET /api/dda/reports` | | |
| | Guest user | RBAC users with roles | | |
| No breaking changes to production API contract. | |
| --- | |
| ## Appendix B β Sample job response | |
| ```json | |
| { | |
| "jobId": 42, | |
| "status": "completed", | |
| "runId": 108, | |
| "reportUrl": "/api/dda/reports/108", | |
| "summary": { | |
| "regionsCount": 7, | |
| "changePercentage": 2.34, | |
| "byType": { | |
| "New Construction": 3, | |
| "Vegetation Change": 2, | |
| "Other": 2 | |
| } | |
| } | |
| } | |
| ``` | |
| --- | |
| *Plan version 1.0 β derived from **Scope of Work_Change Detection-For Team.docx** (FR-01 through FR-08).* | |