fix: separate historical source methodologies
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-15 07:57:59 +02:00
parent c7faea430f
commit 5b1156e989
4 changed files with 50 additions and 7 deletions
+3
View File
@@ -30,6 +30,9 @@
- Added deterministic estimate disclosure for population answers and instructed
the local model to copy governed values literally instead of inventing
averages, rates or derived trends.
- Clarified the source inventory so current GRB layers are shown as a different
methodology beside, rather than as part of, the comparable 2013-2025 land-use
time series.
## Sprint 201 Semantic area-selection metrics (2026-07-15)
@@ -328,3 +328,5 @@ def test_frontend_exposes_source_inventory_timeline_and_ai_window() -> None:
assert "nextAssistantMessageId" in assistant_hook
assert "crypto.randomUUID" not in assistant_hook
assert "Officiële bronnen die hierna kunnen worden ingeladen" in catalog
assert "vergelijkbare meetmomenten" in catalog
assert "andere bronmethode" in catalog
+4
View File
@@ -8422,6 +8422,10 @@ Validation evidence:
limitation whenever an answer discusses estimated population data, while the
model prompt forbids independent averages, rates and derived trends. Focused
tests and full readiness passed with 619 backend tests.
- Browser review identified that the source summary visually combined current
GRB geometry with historical 10 m land-use observations. The UI now reports
comparable observations per temporal-series key and labels singleton/current
datasets as a separate source method. Full readiness remained green.
Known limitations:
- Water volume remains unavailable until a governed depth/bathymetry source is
@@ -66,12 +66,42 @@ function datasetTheme(dataset: DatasetCreateResponse): string | null {
return null
}
function timelineSummary(
temporalCount: number,
temporalSeriesCount: number,
otherMethodCount: number,
firstYear: number | null,
lastYear: number | null,
): string {
if (temporalCount < 2 || !firstYear || !lastYear) {
return 'Alleen de huidige toestand is vergelijkbaar beschikbaar.'
}
const seriesNote = temporalSeriesCount > 1
? ` in ${temporalSeriesCount} afzonderlijke reeksen`
: ''
const methodNote = otherMethodCount > 0
? `plus ${otherMethodCount} andere bronmethode${otherMethodCount === 1 ? '' : 'n'}`
: null
return [
`${temporalCount} vergelijkbare meetmomenten${seriesNote}`,
`${firstYear}-${lastYear}`,
methodNote,
].filter(Boolean).join(' · ')
}
export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.Element {
const ready = datasets.filter((dataset) => dataset.status === 'ready')
const themes = Object.keys(THEME_LABELS).map((theme) => {
const matches = ready.filter((dataset) => datasetTheme(dataset) === theme)
const temporal = matches.filter((dataset) => dataset.temporal_series_key && dataset.observed_at)
const years = temporal.map((dataset) => new Date(dataset.observed_at as string).getUTCFullYear())
const seriesGroups = new Map<string, DatasetCreateResponse[]>()
temporal.forEach((dataset) => {
const seriesKey = dataset.temporal_series_key as string
seriesGroups.set(seriesKey, [...(seriesGroups.get(seriesKey) ?? []), dataset])
})
const comparableSeries = [...seriesGroups.values()].filter((series) => series.length >= 2)
const comparableObservations = comparableSeries.flat()
const years = comparableObservations.map((dataset) => new Date(dataset.observed_at as string).getUTCFullYear())
const latest = [...matches].sort(
(left, right) => new Date(right.observed_at ?? right.imported_at ?? 0).getTime() - new Date(left.observed_at ?? left.imported_at ?? 0).getTime(),
)[0]
@@ -79,7 +109,9 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
theme,
label: THEME_LABELS[theme],
datasetCount: matches.length,
temporalCount: temporal.length,
temporalCount: comparableObservations.length,
temporalSeriesCount: comparableSeries.length,
otherMethodCount: matches.length - comparableObservations.length,
firstYear: years.length ? Math.min(...years) : null,
lastYear: years.length ? Math.max(...years) : null,
source: latest?.source_name ?? latest?.source ?? null,
@@ -105,11 +137,13 @@ export function SourceCatalogPanel({ datasets }: SourceCatalogPanelProps): JSX.E
<span>{theme.source ? theme.source.replaceAll('_', ' ') : 'Nog niet ingeladen'}</span>
</div>
<b>{theme.datasetCount > 0 ? 'Beschikbaar' : 'Ontbreekt'}</b>
<p>
{theme.temporalCount >= 2 && theme.firstYear && theme.lastYear
? `${theme.temporalCount} officiële meetmomenten · ${theme.firstYear}-${theme.lastYear}`
: 'Alleen de huidige toestand is vergelijkbaar beschikbaar.'}
</p>
<p>{timelineSummary(
theme.temporalCount,
theme.temporalSeriesCount,
theme.otherMethodCount,
theme.firstYear,
theme.lastYear,
)}</p>
</article>
))}
</div>