Skip to content

Add format-selection decision framework page and case studies - #205

Merged
emmanuelmathot merged 54 commits into
feat/decision-frameworkfrom
emathot/decision-framework-page
Sep 30, 2026
Merged

emmanuelmathot merged 54 commits into
feat/decision-frameworkfrom
emathot/decision-framework-page

Conversation

@emmanuelmathot

@emmanuelmathot emmanuelmathot commented Aug 3, 2026 •

Copy link
Copy Markdown
Collaborator

First contribution toward the decision framework (#183). Opened against the feat/decision-framework feature


The spine

flowchart TD
    A([Data to publish or migrate]) --> B{Existing data?}

    B -- No --> C{Archival-format<br/>mandate?}
    C -- Yes --> C1[Optimize the mandated format<br/>+ publish a cloud-optimized copy]
    C -- No --> T{{What shape are the data?}}

    B -- Yes --> D{Can you<br/>reprocess?}
    D -- Yes --> T
    D -- No --> P{Can you repackage<br/>the delivered files?}
    P -- Yes --> P1[Format free,<br/>content fixed as delivered]
    P1 --> T
    P -- No --> E{Virtualizable?}
    E -- Yes --> E1[VirtualiZarr / Kerchunk / Icechunk<br/>limited, better than nothing]
    E -- No --> E2[Not yet possible<br/>say what is missing]

    T --> T1(2D raster<br/>COG, Zarr, PMTiles)
    T --> T2(N-D cubes<br/>GeoZarr, COG, CO-HDF5)
    T --> T3(Point cloud<br/>COPC, GeoParquet)
    T --> T4(Vector / tabular<br/>GeoParquet, FlatGeobuf, PMTiles)

    T1 --> R([Refine: intent, object-size lever,<br/>maturity, interpretation])
    T2 --> R
    T3 --> R
    T4 --> R

    classDef entry   fill:#e7eefb,stroke:#3f6db3,stroke-width:2px,color:#152744
    classDef ask     fill:#fff6dc,stroke:#ad7c00,stroke-width:2px,color:#4a3600
    classDef hinge   fill:#dfeffa,stroke:#2a7fb8,stroke-width:3px,color:#0e3247
    classDef family  fill:#e4f4ea,stroke:#39875f,stroke-width:2px,color:#12301e
    classDef partial fill:#fdeadb,stroke:#c06520,stroke-width:2px,color:#48260a
    classDef refine  fill:#ece6f7,stroke:#7355a6,stroke-width:2px,color:#291d44

    class A entry
    class B,C,D,E,P ask
    class T hinge
    class T1,T2,T3,T4 family
    class C1,P1,E1,E2 partial
    class R refine
Loading

Refs #183

Adds a two-page decision framework that takes a reader from "I have
geospatial data to publish or migrate" to a short list of cloud-optimized
format candidates and the layout choices that make each one work.

decision-framework/index.qmd
  - Preconditions P1 (partial/parallel reads over HTTP) and P2 (no opaque
    bundles), written on top of the guide's existing cloud-optimized
    format pattern and glossary rather than as a parallel definition.
  - The spine: a control fork (existing data, reprocessable) then a
    topology gate that opens a candidate set, with a Mermaid flowchart.
  - Refinements: intent, the object-size / grouping lever, ecosystem
    maturity, and interpretation encoding.
  - Cross-cutting notes: metadata and cataloging (STAC recommended, not
    prescribed), within-format depth, and dead ends framed as "unlock
    this".
  - A short optional section on making the choice quantitative, linking
    out rather than reproducing the full spec.

decision-framework/case-studies.qmd
  - Six products run through the spine, five backed by measurements,
    leading with the measured outcome and the recurring lesson that
    cloud-optimized is rarely smaller and the gap is usually the codec.

Also registers both pages in _quarto.yml (placement flagged for review),
adds them to the Welcome table of contents, and credits CNES and the
Copernicus / NASA source missions in _thankyous.qmd.

Refs #183

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
emmanuelmathot and others added 2 commits August 3, 2026 17:31
The optional scoring section named two measured criteria (size and
performance) but had no outbound link, so a reader had no way to
reproduce a number. Point both pages at the open-source harness the
case-study measurements actually came from.

- Framework page: a "where the measured numbers come from" paragraph
  after the criteria table, naming what the harness measures (object-size
  distribution and tier fitness, write, range-request-aware read, tile
  display), that datasets are described in config files rather than baked
  in, and linking the docs and configuration guide.
- Case studies: provenance in the measurement caveat, plus a note in the
  acknowledgements that the harness is maintained outside this guide so
  its methodological choices stay debatable on their own terms (ADR 0002).

Refs #183

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The recurring-lesson callout paired the "rarely smaller" headline with a
measurement-hygiene warning. Replace it with the mechanism that actually
explains the gap: overviews, levels of detail and spatial indexes are
structures the source never carried, so they add volume, and that volume
is the feature being bought. Anchored to the two cases that show it
(the COG carrying overviews, the GeoParquet row-group index).

The like-for-like-on-content caveat is dropped from the summary only. It
stays inline in cases 3 and 5, where it is concrete and actionable.

Also aligns the same paragraph on the framework page.

Refs #183

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@emmanuelmathot

Copy link
Copy Markdown
Collaborator Author

This first cut does not include the "quantitative harness" enabled by https://github.com/developmentseed/cng-formats-benchmark but mention the possible extension.

@gadomski
gadomski self-requested a review August 3, 2026 15:57
@wildintellect
wildintellect self-requested a review August 3, 2026 16:39
@emmanuelmathot
emmanuelmathot marked this pull request as draft August 4, 2026 06:45
emmanuelmathot and others added 3 commits August 4, 2026 08:47
… split

The maturity refinement was a three-row table with no definitions, which
made it the weakest of the four axes. Replace it with the substance from
the strategic-report state-of-the-art work:

- An explicit L1-L5 scale, so the judgement is stated rather than
  impressionistic, and a per-format table giving specification status,
  tooling and level. This also separates FlatGeobuf (L3) from GeoParquet
  and COPC (L4), which the old table lumped together.
- The distinction that is easiest to get wrong: Zarr v3 the substrate is
  dependable (core spec final in 2024, sharding stable in zarr-python 3.x
  and readable by GDAL and xarray), while GeoZarr the convention layer is
  still being ratified in an OGC SWG. The L2 belongs to the conventions,
  not to Zarr.
- "Maturity is a warning, not a veto", which reconciles the topology gate
  offering GeoZarr as the primary N-D cube candidate with that candidate
  sitting at L2: there is no mature alternative at that topology, so a low
  level means plan for change, pin versions, and expect the tooling to
  improve.

Points at geozarr.org for current status rather than quoting a
ratification date that will age badly.

Refs #183

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ies and updating case study references for improved context and accuracy.

@gadomski gadomski left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a run-through of the first half of index.qmd; I'll try to come back for the rest later when I get another free time window. I think the high level flowchart and framework feels about correct, but we need to be sure to explain (or link to definition) of terms before we use them — as an "insider" I still wondered what some acronyms or phrases meant.

Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
emmanuelmathot and others added 7 commits August 5, 2026 08:13
Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com>
Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com>
Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com>
…mat selection and enhancing the explanation of topology and control stages.
…ifying definitions for improved understanding and context.
…d explanations for reprocessing and virtualization processes.
@emmanuelmathot

Copy link
Copy Markdown
Collaborator Author

Thank you @gadomski for the review. I tried to integrate and improve according to your comments and suggestions. I left some opened, feel free to close them or comment further.
I re-request a review because I made a lot of changes. Thanks.

@emmanuelmathot
emmanuelmathot requested a review from gadomski August 5, 2026 08:17
…ion encoding and enhancing the explanation of the two-stage process for format selection. Update theme configuration in Quarto settings for improved clarity.
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
Comment thread decision-framework/index.qmd Resolved
emmanuelmathot and others added 2 commits August 21, 2026 09:18
Co-authored-by: Alex I. Mandel <alex@developmentseed.org>
Comment thread decision-framework/index.qmd Outdated

The lever works in both directions, and which direction you need depends on product size:

- **Products that are already large.** Group *within* one product. Size the shard, the tile, or the row group so it clears the policy.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Zarr I think most of this holds true. However for some of the other formats, there might be a 3rd item in this list, which is to actually split an existing file into separate files because there would be too many chunks/shards in one file the index would be huge. This might be a for later discussion.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about how we somewhat manually design geoparquet partitions (across files), Zarr already does this inherently. There is a a practical limit though, you wouldn't want a single COG for the whole world at 1m resolution, even if it is chunked nicely, that file would be hard to work with on a management side.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be missing something but here and elsewhere it seems to be suggested that an internal COG tile could be its own object. I don't think that is the case? I modified wording above "how bytes are grouped into data chunks and chunks grouped into objects" but when we have a COG that is already too large wouldn't the solution have to be to actually break the COG into smaller regions or separate bands?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx. You are right, and this is an error. For COG, COPC, and single-file GeoParquet, the file is the object. Internal tiles and row groups are byte ranges inside that one object. They do not change the object size at all. Zarr is the only format where the two coincide, because there a shard is an object.

The page also contradicted itself. The "size and shape are two different questions" paragraph below already made this distinction correctly. The table will have two columns, "what sets the object size" and "what sets the read granularity inside the object", and a sentence will state that only Zarr connects them. I will also correct the "products that are already large" bullet, which told you to size the tile or the row group.

@wildintellect wildintellect left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly happy with this as the first version. Will let the others chime in if there are still any blockers for them.

@abarciauskas-bgse abarciauskas-bgse self-assigned this Aug 31, 2026
Comment thread decision-framework/index.qmd Outdated

Tune the lever to your [object store's](../glossary.qmd#object-storage-cloud-storage) **object-size policy** (S3, Azure Blob Storage, Google Cloud Storage, or an on-premises equivalent). Object stores degrade when they must index very many tiny objects, so most reward a larger average object.

One worked example: the [CNES Datalake](https://cnes.fr/en/projects/centre-de-calcul) sorts data into [tiers](../glossary.qmd#tiered-storage) by mean object size, with about 32 MB or more for warm disk and about 100 MB or more for cold tape. This serves the general point: find out whether your storage has an object-size policy, and size the lever against it. Some providers publish a threshold, some only charge you as if your objects were larger, and some have no policy at all.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some only charge you as if your objects were larger

can you explain this a bit more @emmanuelmathot ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, too vague to be useful. I meant a minimum billable object size. Several S3 storage classes charge for small objects as if they were larger. Standard-IA, One Zone-IA, and Glacier Instant Retrieval each charge a minimum of 128 KB for one object. A 10 KB object then costs the same as a 128 KB object. Glacier Flexible Retrieval and Deep Archive add 40 KB of metadata for each object.

The write does not fail, so you do not see this cost until the invoice. The text now gives this example and links to the AWS storage class table.

Will precise in the following paragraph

Comment thread decision-framework/index.qmd Outdated
This axis separates the candidates more than people expect:

- **Zarr v3 makes the information first-class.** With the `scale_offset` codec, a declared fill value, Zarr conventions for projection, CRS and multiscale, the transform is a first-class array property. Every reader recovers the same value.
- **COG, GeoParquet, and COPC standardize the CRS** but leave the value transform to side metadata. Readers honor side metadata unevenly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what "side metadata" means here. I think what you are saying is that these encodings are baked into the Zarr standard but not these other standards. An example may help.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

external files, aka sidecar files, somecog.xml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we sidecar metadata files or add this term the terminology section?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The term is jargon. One small correction on "sidecar files": for a COG with scaled integers, the scale and the offset are usually inside the TIFF, in a GDAL metadata tag. They are not always in a separate file. The important difference is not the location. It is whether the data model of the format makes the reader apply the value.

The term appears only twice, so I removed it instead of adding it to the terminology table. The bullet will give the example you ask for.


A multiscale pyramid does not have to come from one source resolution downsampled by a fixed factor. The common case is **overviews**, also called [pyramids](../glossary.qmd#overviews): downsampled copies of the same band, generated at a uniform decimation so a reader can zoom out without reading and resampling full-resolution chunks. COG overviews, COPC's octree levels of detail, and most GeoZarr pyramids work this way, and it is already covered above under [ecosystem maturity](#maturity) and the [grouping lever](#grouping-lever).

The same structure can also host bands that were never derived from each other and were simply captured at different native resolutions, as long as the steps between levels are chosen to land on each native resolution rather than a fixed factor. Sentinel-2 is the reference case, worked through in [case 1](case-studies.qmd) of the case studies: the 10 m, 20 m, and 60 m bands from the same sensor fold into one pyramid by aligning the decimation to each step (10 m to 20 m halves the resolution, 20 m to 60 m divides it by three), and the pyramid can continue past 60 m into ordinary overviews using the same mechanism. The [Zarr multiscales convention](https://github.com/zarr-conventions/multiscales) is built for exactly this: scale factors between levels are not required to be uniform, and its own reference examples use this Sentinel-2 layout.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this section a bit confusing (why are we talking about bands derived from one another?), perhaps we can discuss it and take a shot at making it more clear

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh we can probably clarify that, this is probably adding too much detail about how a band is captured on some satellites. The key point is that some products have bands at different resolutions.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section answers @wildintellect's prevvious comment. An earlier version said mixed resolution was out of scope. We corrected that: the two cases combine into one pyramid when the step between levels is not a fixed factor. Sentinel-2 is the example, and the Zarr multiscales convention uses that same layout as its own reference example.

fair poiny though. Let's decide what we want here:

  1. Keep the content, and fix the confusing sentence.
  2. Keep a short version here, and move the Sentinel-2 detail to the Zarr page or to the case study.

Adding this point to the call. Leaving this open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pete: "scale factors between levels are not required to be uniform", however if you need to combine bands or variables they need to be the same resolution to avoid resampling

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: verify that COGs allow for arbitrary overview resolutions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Frame the branch that way, and route the reader back to the options: reprocess if you can get the chain changed, virtualize if a reader exists, or convert on ingest. Say what is missing, so somebody can unlock it.

## Optional: make it quantitative

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can simplify this section with just a few sentences and pointing readers to the cng-formats-benchmark repo.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. The section is probably too long here. I will reduce it for this release. However, I would like to defer a future work for a potential dedicated page to describe how to make a quantitative assessement using the benchmark and how to integrate in the decision framework. Followup issue opened here

Comment thread decision-framework/index.qmd Outdated
shape. A *family*, not a format. Two or three candidates normally come through,
and the refinements below rank them.

The hand-off is worth stating outright, because the diagram can be read as if

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what "handoff" is being referred to here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The framework runs in two stages, and the topology question marks the hand-off between them.

This made more sense to me before, will think about how to be more clear.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I introduced "hand-off" to replace "hinge", because I thought it was the clearer word but seems not. I will describe the transition literally.

Comment thread decision-framework/case-studies.qmd Outdated

The temporal benefit appears only once many scenes are concatenated along time into a single cube. That is the regime measured in [case 2](#case-time-cube), and it is a collection-level layout decision, not something a scene-size store can give you. Read the per-scene store as a building block and the cube as the product.

The associated masking product, which is written as many small GeoTIFFs, is a clear anti-pattern to fix at the source.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels out of context and why its a clear anti-pattern is not really explained, so we should probably just remove this sentence.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right that the sentence was not very clear. I would prefer to fix it than to delete it, because the finding is real and it connects to the object-size lever.

Comment thread decision-framework/case-studies.qmd Outdated

Note that this compares GeoParquet against the **source**, not against FlatGeobuf. Against its actual rival in the candidate set, GeoParquet is the smaller of the two, because FlatGeobuf cannot compress at all.

Each pass sits below the warm-tier floor. The lever is therefore **aggregation across passes**: concatenate first, then size the row groups. A one-for-one transcode of each pass would leave the object-size problem in place. The gain over the shapefile is the bounding-box row-group index, not the size.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit hard to digest without knowing what passes are and more about the data, but maybe that is unavoidable in order to provide a succinct case study. Are there multiple passes per shapefile or is it one per shapefile? Does each pass constitute one Geoparquet row or many?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. One pass is one overflight, and the mission delivers one shapefile per pass. But LakeSP Prior reports the whole prior lake database on every pass, not only the lakes that pass observed, so about 78 % of the rows describe a lake with no observation and carry a NULL geometry. Many rows per pass, and most of them empty. The GeoParquet output keeps the same rows. That fact also drives the aggregation lever: concatenating passes stacks these row sets, and the row groups are sized after that.

I had this wrong in the text and will correct it


This case is the counterpart to cases 1 and 2. There, one large band already clears the tier, so you group *within* a product. Here the products are small, so you group *above* a single product.

## 6. Existing archive to a virtual store

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add an actual example from NASA here, we have many now.

2. **What the benchmark measurements showed.**

The examples use public Copernicus and NASA missions. The [CNES Datalake](https://cnes.fr/en/projects/centre-de-calcul) appears as one worked example of a [tiered object store](../glossary.qmd#tiered-storage). Its two thresholds appear throughout the cases below: a **warm tier** with a mean-object-size floor of about 32 MB on disk, and a **cold tier** of about 100 MB on tape. It is not the only kind of object store, and its tier numbers are not a general rule.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do these thresholds work -- is the mean object size per bucket or some other grouping unit? And you don't have a choice, if you have mean objects of 32 MB or smaller, all those objects are in warm storage and if you have mean object size of greater than 32 MB its in cold storage? Or just greater than 100MB? Then what happens inbetween 32 and 100 MB?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I realise our wording cause the confusion. "Sorts data into tiers" makes the process sound automatic and it is not the case.

The Datalake is an in-house S3 service with a lifecycle policy that moves data between disk and tape. Each tier has a recommended minimum mean object size: about 32 MB for disk, and about 100 MB for tape. The check is mechanical. The system divides the total volume by the object count, then compares the result to the floor for the tier.

  • A larger mean object size makes your data eligible for more tiers. It never moves your data to a colder tier.
  • Between 32 MB and 100 MB, the data is suitable for disk but not for tape.
    Below 32 MB is the failure case. The penalty is not the tier. Too many small objects fill the object index table before the disk capacity is full. The Datalake then cannot supply its pooled volume to any project, even when much disk space remains.
  • That last point is the real motivation, and it is much stronger than "tiers", so I will rewrite the paragraph around it. In CNES, floors are advisory, not enforced.


The correct direction depends on the size of your product:

- **Products that are already large.** For Zarr, set the shard size so that the mean object size clears the policy. For a single-file format, the file is already one object. To make the objects smaller, split the file by region, by band, or by time step.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't solidly defined products. This only makes sense when there is an existing "product" we are converting from, which we've already stated isn't the case. I think this reads fine without these bullets at all, but if we decide to keep them we should make clear these statements only make sense in the scenario of an existing product we are converting.

@emmanuelmathot
emmanuelmathot merged commit 7c30419 into feat/decision-framework Sep 30, 2026
1 check passed
@emmanuelmathot
emmanuelmathot deleted the emathot/decision-framework-page branch September 30, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decision framework

4 participants