Skip to content

Commit 1c5cd11

Browse files
docs: define Response ↔ Impact boundary rules + synthetic fixture (#48)
Add the source-agnostic decision logic for the Response/Impact boundary, plus a concrete fixture to anchor it: - Add docs/model/response-impact-boundary.md: data-pattern catalogue (P1–P9), per-attribute decision tree (Mermaid + prose), ETL splitting algorithm with the exact Impact→Response derived_from link block and idempotent corr_id/id conventions, and CQL2 query patterns to re-pair both halves of a record. - Add synthetic illustrative fixture under examples/_response-impact-pairing/: one eo-gra Response split into two thematic Impact items (pattern P4), linked via shared monty:corr_id and rel: derived_from. Validates with npm test. Two role-scoped collections (responses/impacts) keep the fixture schema-valid under the roles oneOf. - Cross-link the boundary doc from response-best-practices.md (§5/§6), docs/model/README.md, and README.md; add CHANGELOG entry. Uses current v1.3.0 schema field names only (monty:response_detail.type/ status/monitoring_number); no legacy response_type/status_code/monitoring or source-prefixed type codes. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6629946 commit 1c5cd11

11 files changed

Lines changed: 684 additions & 5 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- `docs/model/response-impact-boundary.md` — source-agnostic Response ↔ Impact boundary rules: data-pattern catalogue (P1–P9), decision tree, ETL splitting algorithm + exact `derived_from` link block, and CQL2 query patterns for re-pairing Response and Impact items
13+
- Synthetic illustrative fixture under `examples/_response-impact-pairing/` — one `eo-gra` Response item split into two thematic Impact items (pattern P4), linked via shared `monty:corr_id` and `rel: derived_from`
14+
- Cross-links to the boundary-rules doc from `docs/model/response-best-practices.md`, `docs/model/README.md`, and `README.md`
15+
1016
### Changed
1117

1218
- Promoted `docs/model/response-taxonomy.md` from a working document to the canonical Monty Response taxonomy reference for v1.3.0: dropped the working-document/pending-review status header, foregrounded the adopted response type codes and classification rules, aligned the `monty:response_detail` field reference with the shipped schema, and moved the framework survey to an appendix

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,10 @@ This section describes in details the usage of the fields and links for the resp
301301
More detail on the field definition is available in the [Montandon model analysis](https://ifrcgo.org/monty-stac-extension/model#response).
302302
The taxonomy of response type codes, the framework survey and the Sendai Framework crosswalk are documented in the [Response Taxonomy](https://ifrcgo.org/monty-stac-extension/model/response-taxonomy.md).
303303
The extension-layering rules per response type are documented in the [Response Best Practices](https://ifrcgo.org/monty-stac-extension/model/response-best-practices.md).
304+
The rules for deciding whether a given piece of data belongs on a Response item, on a paired Impact item, or on neither — the data-pattern catalogue, decision tree, ETL splitting algorithm, and query patterns — are documented in the [Response ↔ Impact Boundary Rules](https://ifrcgo.org/monty-stac-extension/model/response-impact-boundary.md).
304305

305306
- Examples:
306-
- TBD
307+
- [Synthetic Response ↔ Impact pairing](examples/_response-impact-pairing/) (illustrative fixture exercising the multi-thematic split, pattern P4)
307308

308309
The Response class represents an **action taken or a product produced in response to a disaster** — e.g., a Copernicus EMS Grading map, an International Charter Value-Added Product, a UNOSAT damage assessment, an IFRC DREF operation, or a humanitarian shelter distribution. The scope boundary with Impact is strict: **Response = action / product; Impact = estimated effect on people or assets**. A CEMS Grading Product is a Response that *informs* Impact items; it is not itself an impact.
309310

docs/model/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ Response data represent an **action taken or a product produced in response to a
278278

279279
In the Monty model, a Response is **ALWAYS** linked to one or multiple event(s) through `monty:corr_id`, and SHOULD reference the hazard(s) it addresses (via `monty:hazard_codes` and `related-hazard` links) as well as any acquisition / source imagery items it derives from (via STAC `derived_from` links). The canonical landing page or product URL in the source system (e.g., CEMS activation page, Charter activation page, UNOSAT product page) SHOULD be expressed as a STAC `rel: derived_from` link rather than embedded as a property under `monty:response_detail`. Where the response product informs Impact items, the canonical provenance edge is carried on the **Impact** side: each derived Impact item SHOULD declare a STAC `rel: derived_from` link pointing back to the Response item. The Response item itself MAY add `rel: related` links (with `roles: ["impact"]`) to the Impact items it informs, but those are an optional back-reference — the `derived_from` edge on the Impact item is the authoritative provenance.
280280

281-
The full design rationale, framework survey, taxonomy of response type codes, and Sendai Framework crosswalk are documented in [Response Taxonomy](response-taxonomy.md). The extension-layering rules and worked-example combinations are in [Response Best Practices](response-best-practices.md).
281+
The full design rationale, framework survey, taxonomy of response type codes, and Sendai Framework crosswalk are documented in [Response Taxonomy](response-taxonomy.md). The extension-layering rules and worked-example combinations are in [Response Best Practices](response-best-practices.md). The rules for deciding, per piece of data, whether something belongs on a Response item, on a paired Impact item, or on neither — the pattern catalogue, decision tree, ETL splitting algorithm, and query patterns — are in [Response ↔ Impact Boundary Rules](response-impact-boundary.md).
282282

283283
The Response class has the following attributes:
284284

docs/model/response-best-practices.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,16 @@ The following patterns SHOULD be avoided:
189189

190190
- **Duplicating `disaster:` fields under `monty:response_detail`** on Charter items — e.g., adding a parallel `monty:response_detail` field for any concept already covered by `disaster:activation_id`, `disaster:activation_status`, `disaster:resolution_class`, etc. On Charter items declaring `disaster:`, the `disaster:` field is canonical.
191191
- **Carrying `sar:` / `eo:` / `sat:` fields on the Response product item.** Those describe the source imagery, which lives in separate acquisition items linked via `derived_from`.
192-
- **Stuffing damage statistics into `response_detail`.** Damage counts, affected populations, building destruction counts MUST become Monty Impact items (separate STAC items, `roles: ["impact"]`) linked to the Response via `monty:corr_id` (and optionally `rel: related` with `roles: ["impact"]`).
192+
- **Stuffing damage statistics into `response_detail`.** Damage counts, affected populations, building destruction counts MUST become Monty Impact items (separate STAC items, `roles: ["impact"]`) linked to the Response via `monty:corr_id` (and optionally `rel: related` with `roles: ["impact"]`). See [Response ↔ Impact Boundary Rules](response-impact-boundary.md) for the data-pattern catalogue and the decision tree that resolves, per attribute, whether something stays on the Response item or becomes a paired Impact item.
193193
- **Mixing source code into the type code.** Use `eo-del` (source-agnostic) rather than `eo-cems-del` or `eo-charter-del`. Source provenance lives in `monty:response_detail.source_id`, `monty:response_detail.producer`, and `derived_from` links.
194194
- **Creating multiple `eo-del-mon-N` codes for monitoring iterations.** Use a single `monty:response_detail.monitoring_number = N` field on the monitoring item, mirroring the CEMS API.
195195

196196
---
197197

198198
## 6. Linkage summary
199199

200+
> For the algorithm that decides **when** to split a source record into a Response item plus paired Impact items, the exact `derived_from` link block to emit, the idempotent `corr_id` / id conventions, and the CQL2 queries that re-pair the two halves, see [Response ↔ Impact Boundary Rules](response-impact-boundary.md).
201+
200202
A Monty Response item typically participates in the following links:
201203

202204
| `rel` | Target | `roles` (on link) | Purpose |
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Response ↔ Impact Boundary Rules
2+
3+
> **Status:** Boundary-rules document accompanying the v1.3 `monty:response_detail` model. It complements [Response Best Practices](response-best-practices.md) by specifying **how to decide, per piece of data, whether something belongs on a Response item, on a paired Impact item, or on neither**.
4+
5+
The overlap between **Response** and **Impact** is the most common source of ETL modelling
6+
confusion. A CEMS Grading Product that reports "200 buildings destroyed" is unambiguously a
7+
**Response** product — an action was taken to produce it — but the building-damage count itself is
8+
**Impact** data. Without explicit rules, two ETL authors will model the same source record
9+
differently.
10+
11+
This document publishes a **source-agnostic** decision procedure: a catalogue of recurring data
12+
patterns, a decision tree an ETL author runs per attribute/layer, the splitting algorithm and exact
13+
link block to emit, and the queries that re-pair the two halves.
14+
15+
- **Response** = an *action taken or product produced* in response to a disaster (a CEMS map, a Charter VAP, a UNOSAT assessment, an IFRC DREF).
16+
- **Impact** = an *estimated/realised effect on people or assets* (deaths, people affected, buildings destroyed, economic loss).
17+
18+
> **Scope.** This document is **source-agnostic**. Per-source data-element classification (which
19+
> Charter / CEMS / UNOSAT / IFRC attribute maps to which pattern) belongs to the analysis
20+
> deliverable of each source epic, which applies the pattern catalogue below. The governing
21+
> principle — *"statistical figures go to Impact items"* — is stated in
22+
> [Response Best Practices §1.5](response-best-practices.md#1-governing-principles); this document
23+
> operationalises it.
24+
25+
---
26+
27+
## 1. Pattern catalogue
28+
29+
Run this catalogue over **each attribute, layer, or table** carried by an incoming response product.
30+
For every piece of data, find the matching pattern and apply its modelling outcome. The catalogue is
31+
non-exhaustive; refine it as new source patterns appear.
32+
33+
| # | Data pattern in a Response product | Examples (any source) | Modelling outcome |
34+
|---|---|---|---|
35+
| **P1** | **Geometry-only delineation** — a polygon/raster describing event extent, with no per-feature attributes beyond geometry | Flood-extent polygon, fire perimeter, ash-fall area | **Response only.** Geometry on the Response item; no Impact derived. |
36+
| **P2** | **Categorical classification per feature** — polygons/pixels carry a hazard or damage *class* but no numeric estimate of effect | Damage grade per building polygon (`none` / `minor` / `major` / `destroyed`); `flooded` / `not flooded` per pixel | **Response only** when the class is intrinsic to the product. **Both → split** if the ETL aggregates the classification into a per-class **count** (the count becomes the Impact, see P4). |
37+
| **P3** | **Numeric estimate of effect on people/assets** — a quantitative figure in human/asset units, regardless of how it was produced | `affected_population: 12300`, `buildings_destroyed: 87`, `hectares_flooded: 1450`, `economic_loss_usd: 2.5e6` | **Impact.** Always becomes one or more Impact items; **never** carried under `monty:response_detail`. |
38+
| **P4** | **Multi-thematic statistics table** — one numeric estimate per thematic class | CEMS GRA `affected` / `total` per thematic (population, buildings, roads, …) | **Both → split:** one Response item **+ one Impact item per thematic class.** |
39+
| **P5** | **Provenance / methodology metadata** — analyst, method, software, sensor, processing chain | `producer`, `methodology`, `processing:software`, `sat:platform` on linked acquisitions | **Response only.** Carried under `monty:response_detail.producer` / `methodology` (or on the linked acquisition item for sensor metadata). |
40+
| **P6** | **Lifecycle / status flag** — production state of the product itself | CEMS `statusCode`, monitoring iteration number | **Response only.** Carried under `monty:response_detail.status` / `monitoring_number`. |
41+
| **P7** | **Activation / request record** — an administrative entry that *triggers* a response but is not itself a product | Charter activation record, CEMS activation event | **Neither Response nor Impact — it is an Event.** Modelled as a Monty Event item. (See [Response Best Practices §3.2](response-best-practices.md#32-international-charter): Charter activations are Events; only VAPs are Responses.) |
42+
| **P8** | **Free-text narrative / situational summary** with no machine-readable figures | CEMS Situational Report prose, UNOSAT report PDF | **Response only.** Carries the narrative as an asset. Any figures a human/NLP step extracts from it are then modelled as Impact items in addition (P3). |
43+
| **P9** | **Pre-event baseline** — reference data of assets/exposure *before* the event | CEMS Reference Map, pre-event population layer | **Response only.** `monty:response_detail.type = eo-ref`. Exposure baselines are **not** Impact items — no realised effect has occurred. |
44+
45+
**Key distinction P3 vs. P9:** a numeric figure is Impact (P3) only when it expresses a *realised
46+
effect of the event*. A pre-event exposure baseline (P9) is the same kind of number measured *before*
47+
the event and stays on the Response item.
48+
49+
---
50+
51+
## 2. Decision tree
52+
53+
Apply this per attribute/layer. It resolves every catalogue pattern to one of four outcomes:
54+
**Event**, **Response only**, **Impact**, or **Both → split**.
55+
56+
```mermaid
57+
flowchart TD
58+
A([Attribute / layer in a Response product]) --> B{Is it an activation /<br/>request record that triggers<br/>a response?}
59+
B -- yes --> EV[/"Event item<br/>(P7) — not Response, not Impact"/]
60+
B -- no --> C{Is it a numeric estimate of a<br/>realised effect on people/assets?}
61+
C -- "yes, single figure" --> IM[/"Impact item (P3)<br/>one monty:impact_detail"/]
62+
C -- "yes, one figure per thematic class" --> SP[/"Both → split (P4)<br/>1 Response + 1 Impact per thematic"/]
63+
C -- no --> D{Is it geometry, a per-feature<br/>class, a pre-event baseline,<br/>or narrative?}
64+
D -- "geometry / class / baseline / narrative" --> RO[/"Response only<br/>(P1, P2, P8, P9)"/]
65+
D -- no --> E{Is it provenance, methodology,<br/>or a lifecycle/status flag?}
66+
E -- yes --> RF[/"Response only — carry under<br/>monty:response_detail (P5, P6)"/]
67+
E -- no --> RO
68+
RO -. "ETL aggregates a per-feature<br/>class into per-class counts" .-> SP
69+
```
70+
71+
**Prose fallback (run top to bottom, first match wins):**
72+
73+
1. **Activation / request record?****Event** item (P7). Stop.
74+
2. **Numeric estimate of a realised effect** on people/assets?
75+
- Single figure → **Impact** item (P3).
76+
- One figure per thematic class (a statistics table) → **Both → split** (P4): one Response item + one Impact item per thematic.
77+
3. **Geometry, per-feature class, pre-event baseline, or free-text narrative?****Response only** (P1, P2, P8, P9). *If the ETL later aggregates a per-feature class into per-class counts, those counts re-enter at step 2 as Impacts.*
78+
4. **Provenance, methodology, or a lifecycle/status flag?****Response only**, carried under the relevant `monty:response_detail` field (P5, P6).
79+
80+
---
81+
82+
## 3. ETL splitting guidance — when and how to split a source record
83+
84+
When a source product matches **P3** or **P4** (it carries realised-effect figures), the ETL
85+
transformer splits the incoming record into one Response item plus one or more Impact items.
86+
87+
### 3.1 Algorithm
88+
89+
For each incoming source product:
90+
91+
1. **Emit one Response item.** Carry the product type, lifecycle, provenance, geometry, and assets
92+
per [Response Best Practices](response-best-practices.md). Do **not** put any realised-effect
93+
figure under `monty:response_detail`.
94+
2. **For each numeric thematic** in the source (P4: one per thematic class; P3: the single figure),
95+
**emit one Impact item** carrying that single figure in `monty:impact_detail`.
96+
3. On every Impact item, add the **`derived_from` → Response** link block (§3.3).
97+
4. Set the **same `monty:corr_id`** on the Response item and on every paired Impact item.
98+
99+
### 3.2 ID and `corr_id` conventions (idempotent re-pairing)
100+
101+
So that re-running the ETL re-pairs the same items instead of creating duplicates:
102+
103+
- **Shared `monty:corr_id`** on the Response and all its Impacts is the durable join key. Reuse the
104+
existing Monty `corr_id` format `YYYYMMDDTHHMMSSZ-CCC-HAZ-EPISODE-PROVIDER`.
105+
- **Deterministic item ids.** Derive Impact ids from the Response `source_id` + thematic, e.g.
106+
`<source_id>-<thematic>-<impact_type>` (`EMSR-DEMO-001-buildings-destroyed`). Re-runs produce the
107+
same id → the catalog upserts rather than duplicating.
108+
- Keep `monty:country_codes` and `monty:hazard_codes` consistent across the paired items.
109+
110+
### 3.3 Exact link block to emit on each Impact item
111+
112+
The canonical provenance edge runs **Impact → Response** (see
113+
[Response Best Practices §6](response-best-practices.md#6-linkage-summary)). On every Impact item:
114+
115+
```jsonc
116+
{
117+
"rel": "derived_from",
118+
"href": "response-EMSR-DEMO-001-GRA.json",
119+
"type": "application/json",
120+
"roles": ["response"]
121+
}
122+
```
123+
124+
The reverse edge (Response → `rel: related` with `roles: ["impact"]` to each Impact) is an
125+
**optional back-reference** only; the `derived_from` link above plus the shared `corr_id` are the
126+
authoritative linkage.
127+
128+
### 3.4 Multi-thematic products (P4)
129+
130+
A CEMS GRA-style statistics table with figures for population, buildings, and roads becomes **one
131+
Response item and three Impact items** — never a single Impact item bundling all thematics, and never
132+
the figures stuffed into `monty:response_detail`. Each Impact item carries exactly one
133+
`monty:impact_detail` object. See the worked fixture under
134+
[`examples/_response-impact-pairing/`](../../examples/_response-impact-pairing/), which exercises this
135+
pattern (one `eo-gra` Response → two thematic Impacts).
136+
137+
---
138+
139+
## 4. Query perspective — re-pairing the two halves
140+
141+
A STAC API consumer reconstructs the paired record using the shared `monty:corr_id` and the
142+
`derived_from` link. Examples use `cql2-json`, consistent with the
143+
[STAC API correlation examples](stac-api/correlation_examples.md).
144+
145+
### 4.1 Return both halves of a paired record
146+
147+
All items (Response + Impacts) sharing a `monty:corr_id`:
148+
149+
```json
150+
{
151+
"filter-lang": "cql2-json",
152+
"filter": {
153+
"op": "=",
154+
"args": [
155+
{"property": "monty:corr_id"},
156+
"20260615T000000Z-DEMO-FL-001-DEMO"
157+
]
158+
}
159+
}
160+
```
161+
162+
### 4.2 Given a Response, find its downstream Impacts
163+
164+
```json
165+
{
166+
"filter-lang": "cql2-json",
167+
"filter": {
168+
"op": "and",
169+
"args": [
170+
{
171+
"op": "=",
172+
"args": [{"property": "monty:corr_id"}, "20260615T000000Z-DEMO-FL-001-DEMO"]
173+
},
174+
{
175+
"op": "in",
176+
"args": ["impact", {"property": "roles"}]
177+
}
178+
]
179+
}
180+
}
181+
```
182+
183+
### 4.3 Given an Impact, find its source Response
184+
185+
Two equivalent strategies:
186+
187+
- **Follow the link (authoritative):** read the Impact item's `links`, take the entry with
188+
`rel: derived_from` and `roles: ["response"]`, and dereference its `href`.
189+
- **Filter by `corr_id` + role:** same query as §4.2 with the `roles` arg set to `"response"`:
190+
191+
```json
192+
{
193+
"filter-lang": "cql2-json",
194+
"filter": {
195+
"op": "and",
196+
"args": [
197+
{
198+
"op": "=",
199+
"args": [{"property": "monty:corr_id"}, "20260615T000000Z-DEMO-FL-001-DEMO"]
200+
},
201+
{
202+
"op": "in",
203+
"args": ["response", {"property": "roles"}]
204+
}
205+
]
206+
}
207+
}
208+
```
209+
210+
---
211+
212+
## See also
213+
214+
- [Response Best Practices](response-best-practices.md) — extension layering, field carriage per source, anti-patterns, and the linkage summary.
215+
- [Response Taxonomy](response-taxonomy.md) — response type codes and the Sendai crosswalk.
216+
- [STAC API correlation examples](stac-api/correlation_examples.md) — broader CQL2 correlation patterns.
217+
- [`examples/_response-impact-pairing/`](../../examples/_response-impact-pairing/) — synthetic Response+Impact fixture exercising pattern P4.

0 commit comments

Comments
 (0)