Files
insar-management-system-v2/docs/TIMESERIES_SBAS_SARSCAPE_INTEGRATION_DESIGN_20260429.md
T

587 lines
22 KiB
Markdown

# Time-Series SBAS And SARscape Integration Design
## 1. Problem Statement
The current time-series route can find and run a scene stack, but the system does not yet treat SBAS as a first-class production input. The main gaps are:
- `find-ps-timeseries` returns scenes, not a durable SBAS network.
- `PsTaskBatch` is used as a production input even though it is a thin list of paths.
- Planning context is partly duplicated in `PsTaskItem.remark`.
- `copy-ps-stack` copies source folders, but does not create a stack-level production package.
- The managed time-series runner reconstructs input state at run time.
- SARscape is currently integrated only as a D-InSAR pair processor.
The design goal is to make one immutable stack manifest the source of truth for every SBAS run, then let ISCE2/MintPy and SARscape consume the same contract.
## 2. Target Workflow
```text
AOI + filters
-> time-series stack search
-> SBAS network plan
-> user review and commit
-> immutable stack package
-> processor workflow
-> publish bundle
-> psinsar catalog
```
The stack plan and the production package are separate states. A plan is a previewable proposal; a package is a committed production input.
## 3. Planning Contract
### 3.1 Search API
Add or evolve the current `find-ps-timeseries` route toward:
```text
POST /timeseries/plans/search
```
Core request fields:
- AOI source: uploaded shapefile, region geometry, or GeoJSON.
- Scene compatibility filters: satellite, orbit direction, imaging mode, polarization, date range.
- Scene thresholds: `initial_overlap_threshold`, `final_overlap_threshold`.
- Network thresholds: `time_baseline_min`, `time_baseline_max`, `spatial_baseline_max_meters`, later `perpendicular_baseline_max_meters`.
- Network policy: `strategy`, `num_connections`, `reference_image_id`.
- Processor hint: optional `processor_target`, for example `isce2_stack_mintpy` or `sarscape_sbas`.
### 3.2 Plan Tables
Existing:
- `timeseries_stack_plans`
- `timeseries_stack_plan_items`
New:
- `timeseries_stack_plan_edges`
The edge table stores the selected SBAS graph:
- plan reference
- master/slave plan item references
- master/slave radar scene references
- optional `pairing_metric_cache` reference
- temporal baseline
- spatial/perpendicular baseline
- scene overlap ratio
- AOI pair overlap ratio
- selection reason and score
- enabled flag
This lets the system answer: which pairs were selected, why were they selected, and what graph was actually submitted.
## 4. Production Input Package
Committed production input is represented by a prepared stack manifest. In the
current backend this file is:
```text
backend/runtime/timeseries_work/<run_id>/input/selected_stack_manifest.json
```
This file is not the same thing as a `TimeseriesStackPlan`. The plan is the
candidate pool and audit graph. The prepared stack is the smaller frozen set
submitted to a processor.
Schema:
```json
{
"schema": "insar.timeseries-stack/v1",
"prepared_stack_schema": "insar.prepared-sbas-stack/v1",
"manifest_role": "prepared_sbas_stack",
"mode": "sbas",
"plan_id": "tsp_...",
"prepared_stack_id": "pss_...",
"source_plan_id": "tsp_...",
"source_batch_id": "...",
"processor_code": "sarscape_sbas",
"aoi": {},
"candidate_pool_source": {},
"selection_params": {},
"scenes": [],
"network_edges": [],
"reference_date": "YYYYMMDD",
"production_contract": {
"input_policy": "prepared_stack_only",
"catalog_scan_allowed_after_prepare": false,
"scene_selection_frozen": true
},
"artifacts": {
"selected_network_edges_path_windows": "..."
},
"prepared_stack_validation": {},
"prepared_at_utc": "...",
"manifest_checksum": "..."
}
```
Rules:
- A production run consumes the prepared manifest, not `PsTaskItem.remark` and
not a fresh scan of the full radar catalog.
- The manifest is immutable after `prepare` completes, except for explicit
retry/re-prepare workflows.
- Processor-specific materialization is recorded in a separate processor manifest.
- Source data copying must include the manifest and graph.
### 4.1 Layered SBAS Input Model
The production model is now four layers:
1. Full radar inventory
- The long-lived scene catalog and pairing metric cache.
- It can be large and dirty/rebuilt over time.
2. Candidate time-series pool
- `TimeseriesStackPlanORM`, plan items, and plan edges.
- This is the large pool selected by AOI, date, orbit, baseline, overlap,
and network policy.
- It records why each scene and edge was selected.
3. Prepared SBAS stack
- `selected_stack_manifest.json` with
`prepared_stack_schema=insar.prepared-sbas-stack/v1`.
- Contains only the frozen scenes for this run.
- Writes `input/selected_network_edges.json` as a standalone artifact.
- Records validation results for scene files, graph count/date consistency,
DEM availability when required, and the no-catalog-scan production policy.
4. Processor execution
- SARscape `wf_sbas` consumes the prepared scene stack.
- System `network_edges` are mandatory as the planning/audit graph, but the
native `wf_sbas` path may rebuild the executable graph internally.
- When SARscape's actual graph can be extracted, it should be saved as
`actual_network_edges.json` and compared with `selected_network_edges.json`.
Backend enforcement:
- `prepare_run()` creates the prepared stack contract and validates it.
- `build_sarscape_processor_preflight()` refuses non-prepared manifests.
- `run_sarscape_sbas()` refuses non-prepared manifests and missing
`selected_network_edges.json`.
- `execute_template_workflow()` in the SARscape service has a second guard so
lower-level execution cannot accidentally run from a candidate pool.
## 5. Processor Boundary
Introduce a time-series processor interface:
```text
TimeseriesProcessor
check_available()
preflight(manifest)
build_workflow(run)
prepare_inputs(run)
execute_step(run, step_id)
export_publish_bundle(run)
```
Processor codes:
- `isce2_stack_mintpy`
- `sarscape_sbas`
The existing `timeseries_service` can remain the orchestration service, but processor-specific logic should move behind this interface.
## 6. SARscape SBAS Processor
SARscape SBAS should be a stack-level processor, not an extension of the D-InSAR pair engine.
Suggested steps:
1. `sarscape_preflight`
- Check ENVI, SARscape, taskengine, license, DEM, orbit pool, and output roots.
- Enumerate available SARscape SBAS/E-SBAS task names via `envipyengine`.
2. `sarscape_import`
- Import LT-1 scenes.
- Write `sarscape_import_manifest.json`.
3. `sarscape_connection_graph`
- Prefer the system-selected `network_edges`.
- If SARscape internally rebuilds the graph, export the actual graph as `actual_network_edges.json`.
4. `sarscape_interferogram_generation`
5. `sarscape_inversion`
- Generate time-series, velocity, coherence, and quality products.
6. `sarscape_geocode_export`
7. `export_publish_bundle`
8. `register_psinsar_product`
## 7. Result Contract
One SBAS run registers one `psinsar` product bundle.
Required bundle roles:
- stack manifest
- processor manifest
- selected network edges
- actual network edges if processor modified them
- velocity product
- time-series product
- temporal coherence or equivalent quality product
- geocoded rasters
- quicklooks
- logs
- processor reports
- product manifest
The catalog registers the publish manifest, not the transient work directory.
## 8. Delivery Phases
### Phase 1: Planning Boundary
- Stop auto-creating PS batches after search.
- Persist `TimeseriesStackPlanEdge`.
- Return edges from `/timeseries-plans/{plan_id}`.
- Add network thresholds to `PsRequest` with backward-compatible defaults.
### Phase 2: Manifest Boundary
- Add committed stack package creation.
- Generate immutable `stack_manifest.json`.
- Make the existing ISCE2/MintPy route consume the manifest.
### Phase 3: SARscape Discovery
- Add a SARscape SBAS task verifier script.
- Capture task names and required parameters per installed SARscape version.
- Add `sarscape_sbas` preflight endpoint.
Initial implementation points:
- `scripts/verify_sarscape_sbas_tasks.py`
- `POST /idl/inspect/sarscape-sbas`
- `POST /timeseries-production/sarscape-sbas/preflight`
- `python -m backend.app.services.envi_runner_cli --inspect-sarscape-sbas`
These entry points must stay read-only. They instantiate ENVI task definitions
and inspect parameters, but do not execute SBAS processing.
The time-series SARscape preflight endpoint builds a processor manifest from
the committed PS batch/stack plan context. It reports the selected network
edges, the SARscape task sequence, required publish roles, and current blockers.
At this phase it must return `ready_for_pipeline_design=true` when ENVI/SARscape
is discoverable, but `ready_for_execution=false` until a checked-in parameter
template and job handler are implemented.
Current implementation status:
- `sarscape_sbas` is a selectable time-series processor.
- The production UI defaults to `ENVI/SARscape SBAS` with `Preflight only`.
- `POST /timeseries-production/runs` accepts `processor_code` and
`execution_mode`.
- SARscape runs use workflow `psinsar_sarscape_sbas_chain`.
- Preflight-only SARscape runs execute `prepare` plus
`sarscape_processor_preflight`, then complete the task without launching the
long SARscape stack execution.
- Full execution is gated by `SARSCAPE_SBAS_ALLOW_EXECUTION=true` and a
`validated=true` parameter template at
`SARSCAPE_SBAS_PARAMETER_TEMPLATE_PATH`.
- The checked-in template at
`backend/templates/sarscape_sbas_parameter_template.example.json` is a
placeholder contract and is intentionally not executable.
Observed on the target workstation:
- Lightweight `Engine.tasks()` discovery succeeds.
- Static `.task` extraction succeeds without starting taskengine. The extractor is:
- `scripts/extract_sarscape_sbas_task_templates.py`
- The installed SARscape exposes native workflow metatasks:
- `wf_sbas`
- `wf_esbas`
- `wf_sbas` is an ENVI metatask at
`C:\Program Files\Harris\ENVI56\user_custom_code\wf_sbas.task`.
It is not listed by `Engine.tasks()` on this workstation, but
`Engine("ENVI").task("wf_sbas")` can instantiate it successfully. Discovery
therefore combines `Engine.tasks()` with static `.task` file detection.
It contains an embedded 11-node DAG:
- `SARscape_setting_output_folders`
- `SARsLoadPreferences`
- `SARsImportSarSelector`
- `ENVIEXTRACTELEMENTSFROMARRAYTASK`
- `SARscapeSuggestLooks`
- `SARsInSARStackSBASGenerateConnectionGraph`
- `SARsInSARStackSBASInterferogramGeneration`
- `SARsInSARStackSBASInversionStep1`
- `SARsInSARStackSBASInversionStep2`
- `SARsInSARStackSBASGeocode`
- `SARscapeEnviuriToShape`
- The static `wf_sbas.task` file contains 18 parameter entries including the
embedded `DAG` default. Live taskengine `QueryTask` exposes 17 callable
parameters; it does not require the caller to pass `DAG`.
- The core production inputs are:
- `INPUT_FILE_LIST`
- `SARSCAPE_PREFERENCE`
- `DEM_SARSCAPEDATA`
- `OUTPUT_FOLDER`
- `GEOCODE_RG_GRID_SIZE`
- `ESTIMATE_RESIDUAL_HEIGHT`
- `DISPLACEMENT_MODEL_TYPE`
- `OUTPUT_ENVI_CARTOGRAPHIC_SYSTEM`
- `wf_sbas` returns SBAS product handles:
- `DISPLACEMENT_SARSCAPEDATA`
- `DEM_OUT_SARSCAPEDATA`
- `CORRECTION_H_SARSCAPEDATA`
- `COHERENCE_SARSCAPEDATA`
- `ALOS_SARSCAPEDATA`
- `ILOS_SARSCAPEDATA`
- `VELOCITY_SARSCAPEDATA`
- `OUTPUT_SHAPES`
- The installed SARscape also exposes these stack tasks:
- `SARsInSARStackSBASGenerateConnectionGraph`
- `SARsInSARStackSBASInterferogramGeneration`
- `SARsInSARStackSBASInversionStep1`
- `SARsInSARStackSBASInversionStep2`
- `SARsInSARStackSBASGeocode`
- `SARsInSARStackSBASVariogram`
- `SARsInSARStackESBASInterferogramGeneration`
- `SARsInSARStackESBASInversion`
- `SARsInSARStackESBASGeocode`
- `SARsInSARConnectionGraphESBAS`
- Reading `.parameters` for stack SBAS tasks can hang taskengine. Parameter
discovery must therefore be optional, subprocess-isolated, and timeout-bound.
Processor implementation should use a checked-in task template or SARscape
help/SML-derived parameter contract rather than relying on live parameter
introspection at run time.
- Timeout cleanup must remove only taskengine processes spawned by the timed-out
inspection subprocess. Existing user-launched ENVI/taskengine sessions should
not be killed by name.
- SARscape/taskengine can create zero-byte `env_*.xyz` and `IDL*.tmp` files in
the process current working directory. ENVI runner cwd and temp variables must
point at `backend/runtime/idl_worker/envi_cwd`, not the repository root.
Root-level `env_*.xyz` and `IDL*.tmp` are disposable taskengine leftovers.
### Phase 3.5: SARscape Native Workflow Strategy
The short-term production strategy is to integrate SARscape through `wf_sbas`.
This is the lowest-risk ENVI/SARscape path because SARscape already wires import,
connection graph generation, interferogram generation, inversion, geocoding, and
shape export in one metatask DAG.
The backend template contract now supports two execution strategies:
- `native_workflow_metatask`
- Preferred first implementation.
- Executes `wf_sbas` once with the committed stack manifest converted into
`INPUT_FILE_LIST`, configured DEM, output folder, and basic SBAS options.
- Does not directly consume the system-selected `network_edges`.
- Requires post-run extraction of SARscape's actual connection graph for audit.
- `explicit_stack_tasks`
- Future controllable implementation.
- Executes `SARsInSARStackSBASGenerateConnectionGraph`,
`InterferogramGeneration`, `InversionStep1`, `InversionStep2`, and
`Geocode` as separate tasks.
- May allow tighter control of graph settings, but direct injection of the
system-selected edge list is not verified yet.
Current rule:
- `network_edges` remain mandatory in the stack manifest because they are the
system planning decision and task-dispatch audit record.
- When using `wf_sbas`, SARscape may rebuild the graph internally. The output
bundle must therefore contain both:
- `selected_network_edges.json`
- `actual_network_edges.json`, when it can be extracted from SARscape outputs
Current code points:
- `backend/app/services/envi_service.py`
- Discovers `wf_sbas`, `wf_esbas`, support tasks, and stack tasks.
- Cleans up only newly spawned `taskengine.exe` PIDs on timeout.
- Runs subprocess and in-process envipyengine calls from
`backend/runtime/idl_worker/envi_cwd` so taskengine temp files do not pollute
the project root.
- `backend/app/services/sarscape_sbas_service.py`
- Builds processor manifests with `execution_strategy`.
- Reports both native and explicit strategy availability.
- Requires `insar.prepared-sbas-stack/v1` before execution.
- Executes `native_workflow_metatask` only when the template is validated and
execution is explicitly enabled.
- `backend/app/services/timeseries_service.py`
- Treats `TimeseriesStackPlan` as the candidate pool.
- Creates `selected_stack_manifest.json` as the prepared stack in
`prepare_run()`.
- Writes `input/selected_network_edges.json` before SARscape preflight or
execution.
- Refuses SARscape preflight/execution when the prepared stack validation
fails.
- `backend/templates/sarscape_sbas_parameter_template.example.json`
- Records the `wf_sbas` parameter contract and DAG summary.
- Keeps `validated=false` until a controlled run validates parameters and
output capture.
- `scripts/extract_sarscape_sbas_task_templates.py`
- Regenerates the static parameter report from installed `.task` files.
Open engineering items:
- Confirm `wf_sbas.INPUT_FILE_LIST` accepts the same LT-1 `*.meta.xml` list used
by current SARscape import tasks.
- Confirm whether `DAG` must be passed explicitly or SARscape uses the embedded
default from `wf_sbas.task`.
- Locate SARscape's written connection graph or auxiliary processing file and
convert it into `actual_network_edges.json`.
- Map `VELOCITY_SARSCAPEDATA`, `DISPLACEMENT_SARSCAPEDATA`,
`COHERENCE_SARSCAPEDATA`, and `OUTPUT_SHAPES` into the unified `psinsar`
publish bundle.
- Decide later whether to invest in `explicit_stack_tasks` for strict graph
injection, depending on whether SARscape exposes a supported graph import or
connection-list parameter.
Smoke test on 2026-04-30:
- Applied the non-destructive `008_timeseries_stack_plan_edges.sql` migration.
- Backfilled two edges for test plan `tsp_d89bfc5bded744e6bf9b60c1` from
`pairing_metric_cache` because the plan was created before the edge table
existed.
- Ran SARscape SBAS preflight for batch
`e240a63a-5941-4a86-8aae-182a6bc95dae`.
- Result:
- `scene_count=3`
- `network_edge_count=2`
- `ready_for_pipeline_design=true`
- `ready_for_execution=false`
- `execution_strategy=native_workflow_metatask`
- `missing_required_tasks=[]`
- blockers are only `Template is not marked validated=true` and
`SARSCAPE_SBAS_ALLOW_EXECUTION is false`.
- Created a `preflight_only` run
`b7c2df45-a891-4ff7-b106-013e8d285fbd` and executed its `prepare` plus
`sarscape_processor_preflight` steps. This wrote
`selected_stack_manifest.json` and `sarscape_sbas_processor_manifest.json`
without launching the full SARscape SBAS pipeline.
- Dispatch verification for workflow
`eeaf1d82-7268-490c-9fb5-911a00a475c6` exposed a real workflow bug:
`workflow_service.mark_step_completed()` advanced downstream steps to
`READY`, but the database session has `autoflush=False`, so the immediate
`enqueue_ready_steps()` query did not see the new `READY` status.
`sarscape_processor_preflight` therefore stayed `READY` without a job.
- Fixed the dispatcher by flushing after `_advance_ready_steps()` and before
`enqueue_ready_steps()`.
- Verified the dispatcher fix in a rollback-only two-step workflow regression
check: completing step `a` immediately advanced step `b` to `RUNNING` and
created its queued job.
- Re-ran the controlled dispatch path for only this workflow:
- `TIMESERIES_PREPARE`: `COMPLETED`
- `TIMESERIES_SARSCAPE_PREFLIGHT`: `COMPLETED`
- workflow status: `COMPLETED`
- task status: `COMPLETED`, progress `100`
- run status: `PREPARED`
- no `TIMESERIES_RUN_SARSCAPE_SBAS` job or `run_sarscape_sbas` step was
created because execution mode was `preflight_only`.
- Root-level taskengine leftovers after the run:
- `env_*.xyz`: `0`
- `IDL*.tmp`: `0`
ENVI status now reports runner cwd as
`backend/runtime/idl_worker/envi_cwd`.
Parameter template validation on 2026-04-30:
- Initial live `Engine("ENVI").task("wf_sbas")` parameter inspection failed
with `ENVITASK: No task matches: wf_sbas`, even though the static
`wf_sbas.task` file was present.
- Root cause: SARscape installed `wf_sbas.task` under
`C:\Program Files\Harris\ENVI56\user_custom_code`, while taskengine only
auto-loads deployed custom tasks from `ENVI_CUSTOM_CODE`, the ENVI
`custom_code` directory, the application user directory, or IDL packages.
- Backend runner now sets `ENVI_CUSTOM_CODE` to the discovered SARscape
`user_custom_code` directory. This is process-local to the runner and does
not modify the machine-level environment.
- After the fix, live `wf_sbas` parameter inspection succeeds:
- `available=true`
- `parameter_count=17`
- required inputs: `INPUT_FILE_LIST`
- outputs: `OUTPUT_SHAPES`, `DISPLACEMENT_SARSCAPEDATA`,
`DEM_OUT_SARSCAPEDATA`, `CORRECTION_H_SARSCAPEDATA`,
`COHERENCE_SARSCAPEDATA`, `ALOS_SARSCAPEDATA`,
`ILOS_SARSCAPEDATA`, `VELOCITY_SARSCAPEDATA`
- Added repeatable validation script:
`scripts/validate_sarscape_sbas_template.py`.
- Validation report:
`backend/runtime/sarscape_sbas_template_validation_latest.json`.
- Current 3-scene validation result:
- `ok=true`
- validation scope: template contract only, no `task.execute()`
- manifest scene count: `3`
- network edge count: `2`
- `INPUT_FILE_LIST_count=3`
- scene `meta_path`, `tiff_path`, and folders all exist
- DEM base, `.sml`, and `.hdr` all exist
- remaining execution gate issue: checked-in template is still
`validated=false`
Prepared stack boundary implementation on 2026-04-30:
- Added `prepared_stack_schema=insar.prepared-sbas-stack/v1` to
`selected_stack_manifest.json`.
- Added `prepared_stack_id`, `source_plan_id`, `source_batch_id`,
`candidate_pool_source`, and `production_contract`.
- Added `input/selected_network_edges.json` as the frozen planning/audit graph
artifact.
- Added prepared stack validation for:
- scene count and dates
- required scene folder, TIFF, and metadata XML paths
- zero-size source files
- network edge count and edge date consistency
- SARscape DEM dependency when SARscape is the selected processor
- missing `selected_network_edges.json`
- SARscape processor preflight and execution now reject manifests that are not
prepared stacks. The lower-level SARscape executor repeats this guard before
calling any ENVI task.
Prepared stack UI/API update on 2026-04-30:
- Added read-only backend summary endpoint:
`GET /timeseries-production/runs/{run_id}/prepared-stack`.
- The endpoint reads only existing run artifacts and does not trigger catalog
scans, preflight, or SARscape execution.
- The summary reports:
- prepared stack state
- `prepared_stack_id`
- manifest and selected network edge artifact paths
- scene count and network edge count
- prepared stack validation result
- SARscape processor manifest readiness and blockers
- `TimeseriesProductionPanel` now shows a dedicated `Prepared SBAS Stack`
section in run details.
- The SARscape preflight card now states that batch preflight is against the
candidate pool, while production freezes a prepared stack before processor
execution.
- `usePairingLogic` now marks created PS batches as candidate time-series pools
in the planning context and logs that production will freeze a prepared SBAS
stack during `prepare`.
### Phase 4: SARscape Execution
- Implement the SARscape SBAS processor steps.
- Serialize taskengine execution through the existing ENVI lock.
- Persist step manifests and logs.
Initial execution skeleton is in place:
- `TIMESERIES_SARSCAPE_PREFLIGHT`
- `TIMESERIES_RUN_SARSCAPE_SBAS`
- `backend/app/services/sarscape_sbas_service.py`
The execution handler resolves template macros and calls `execute_envi_task`
only after the template is readable, structurally valid, marked
`validated=true`, required tasks are discoverable, and execution is explicitly
enabled.
### Phase 5: Unified Result Management
- Normalize ISCE2/MintPy and SARscape outputs into the same publish bundle roles.
- Keep processor-specific files as secondary assets.
- Show products by role in the UI, not by processor-specific filenames.