ADR-0070: Release-gebundener Preiskanal für T04 und ehrliche Granularität
- Status: Accepted
- Datum: 2026-09-17
- Owner: Andreas (Tech)
- Issue: #575 (Slice 1), Parent #493
- Adressiert Risiken: R-01/R-02 (Datenrechte, Quellenstand), R-06 (Scheingenauigkeit)
- Präzisiert: ADR-0069 §1 (Scope)
- Baut auf: ADR-0043, ADR-0051, Muster Erdbeben-Publisher (#427)
Kontext
ADR-0069 hat T04 Immobilienmarkt-Signal als drittes öffentliches Werkzeug gewählt und als Bedingung gesetzt, dass Slice 1 die Preisquellen zuerst in den Source-Release-Vertrag überführt. Die Faktenlage beim Start:
- Sechs Preisquellen werden per Cron in
price_facts(Migration 0012) idempotent überschrieben. Die Tabelle kennt weder Release noch Run-Key; ein öffentliches Werkzeug könnte daraus keinen unveränderlichen, rechte- und frischegebundenen Stand lesen. statat,eurostat,oenbstehen insource_registryalslegacy_unverifiedmitrights_status = unknown; es gibt keinesource-rights-Dateien, keinen Distribution-Key, keine Consumer-Bindung.- Granularität: Statistik Austria (
OGD_hpi2015_HPI_15_1) hat nur die Zeitreihe als Dimension, Eurostatprc_hpi_qnur Länder, die OeNB nur Österreich und Wien. Es gibt keine Bundesland-Werte. ADR-0069 §1 hatte „im gewählten Bundesland“ versprochen; das war nicht belegt.
Entscheidung
- Scope-Korrektur zu ADR-0069: T04 zeigt Österreich gesamt (Statistik Austria HPI, Eurostat HPI), Wien und Österreich (OeNB Fundamentalpreisindikator) sowie EU-Aggregate als Vergleich. Kein Bundesland-Umschalter. Die Granularitätsgrenze steht in Rechte-Review, Coverage-Evidenz, Hilfetext und Werkzeug.
- Drei Quellen, ein Vertrag:
lib/data-sources/prices-release-contract.mjsdefiniert fürstatat,eurostat,oenbDistribution-Key, Pflicht- Indikatoren, Pflicht-Regionen, Wertebereich und die fünf Pflichtprüfungenrights-attribution,artifact-schema,nationwide-coverage,null-and-range,freshness. Fähigkeitindex_lookup, Kanalpublic, Coverage-Keycountry:AT. Eurostat trägt zusätzlich einen Regionsfilter (releaseRegions: Österreich, EU- und Euroraum-Aggregate): nur diese Zeilen bilden Kopie, Manifest und Evidenz; einzelne andere Länder bleiben in der operativen Tabelle. Beleg aus dem ersten Produktionslauf am 18. September 2026: die Türkei-Reihe (Index 1885 bei Basis 2015) riss die globale Wertgrenze, obwohl Österreich und EU im Rahmen lagen; der erste Kandidatf7f9563f…blieb deshalb korrekt unqualifiziert. - Unveränderliche Release-Kopie statt Arbeitstabelle: Migration 0054
legt
price_release_facts(PK je Release und Grain) und die Sichtprice_facts_currentan, die nur Zeilen des aktiven, qualifiziertenpublic_display-Releases liefert.price_factsbleibt die operative Tabelle der Crons; das Werkzeug liest sie nie. - Gebundener Ingest bezeugt sein Manifest:
ingest()inscripts/lib/prices-db.mjsnimmtreleaseentgegen, setzt Run-Key und Release-ID ab dem Start (source_release_assert_run_source), kopiert die Fakten je Release und schreibt den SHA-256 über die sortierten kanonischen Zeilen inmetadata.manifest_sha256plusquality_summary. Der Publisher übernimmt diesen Hash unverändert. - Ein generischer Publisher:
scripts/publish-prices-release.mjs --source <slug>folgt der erzwungenen Sequenz Kandidat → gebundener Ingest → Evidenz → Qualify → CAS-Activate. Kandidat entsteht mitfreshness_basis = retrieved_at(der Quellenstand ist erst nach dem Ingest bekannt) und wird nach dem Ingest aufsource_vintageplusfresh_until(Periodenende +maxStalenessDays) vervollständigt. Eine fehlgeschlagene Pflichtprüfung lässt Kandidat und Evidenz stehen und qualifiziert nicht. OeNB wird aus einer manuell beschafften ZIP-Datei publiziert (#87). - Matrix und Registry: Die Lanes der drei Quellen tragen die Tool-ID
market-signal; das Matrix-Tupel wird auf drei Werkzeuge erweitert (Version2026-09-17.market-signal-price-lanes-v1). Der Registry-Eintrag erhältcoverageContract: machine, bleibt aberplanned, bis ein Release aktiv ist. Site-Check ignoriert Preis-Lanes weiterhin (er verlangtsite-checkintoolIds). - Rechte-Review bleibt menschlich: Die drei Review-Dateien sind
Entwürfe mit belegten Primärquellen (Statistik Austria OGD-Deskriptor
CC BY 4.0, OeNB-Nutzungsbedingungen 2.2 CC BY 4.0, Eurostat
Copyright-Hinweis). Die Registry-Übernahme per
apply-source-rights-review.mjs --apply --ack <sha>und die Produktionsaktivierung erfolgen erst nach ausdrücklicher Bestätigung.
Alternativen, die erwogen wurden
| Alternative | Pro | Contra | Warum verworfen |
|---|---|---|---|
price_facts um source_release_id erweitern und Crons überschreiben lassen | kleinste Migration | jeder Cron-Lauf entkoppelt Zeilen wieder vom Release; Sicht leert sich unkontrolliert | verletzt ADR-0051 (unveränderlicher Stand) |
| Crons komplett auf release-gebundene Läufe umstellen | ein Pfad | jeder Quartalslauf würde automatisch qualifizieren und aktivieren | Aktivierung muss bewusst bleiben (ADR-0043) |
| Nur Statistik Austria binden, Rest später | schneller | T04 braucht OeNB-Wien und EU-Vergleich für ein sinnvolles Ergebnis | drei Quellen, ein Vertrag ist kaum teurer |
| Bundesland-Werte aus anderer Quelle nachbeschaffen | erfüllt ADR-0069 wörtlich | keine freie Quelle mit Bundesland-HPI bekannt; lizenzpflichtige Transaktionsdaten (ADR-0016) | ehrliche Korrektur statt Scheingenauigkeit |
Konsequenzen
Positiv
- Erste Migration von
legacyUnverified-Quellen in den Release-Vertrag; das Muster ist für die übrigen 35 Katalogquellen wiederverwendbar. - Das Werkzeug kann nie einen Stand zeigen, der nicht qualifiziert, aktiv und frisch ist.
- Granularitätsgrenze ist in jeder Schicht dieselbe.
Negativ / Trade-offs
- Zwei Datenhaltungen für Preise (operativ und released); Speicher vernachlässigbar (< 10.000 Zeilen je Release).
- OeNB-Releases bleiben Handarbeit je Quartal.
- Ein Freshness-Fenster von 200–230 Tagen ist großzügig, weil Statistik Austria und OeNB mit Verzögerung veröffentlichen; ein stale Release blockiert die Matrixzelle, ersetzt sich aber nicht selbst.
Folge-Entscheidungen
- Slice 2 (#575): Werkzeugseite und
GET /api/tools/immobilienmarkt, lesend überprice_facts_current. Pflicht aus dem Eurostat-Review (accessDateInAttribution): die sichtbare Attribution muss das Abrufdatum (retrieved_atdes aktiven Releases) enthalten. - Slice 3: MET-01-Instrumentierung, Release-Evidenz, Tag-90-Bericht.
- Runbook: docs/PRICES-RELEASE-RUNBOOK.md.
Referenzen
lib/data-sources/prices-release-contract.mjs,scripts/lib/prices-release-publisher.mjs,scripts/publish-prices-release.mjs,scripts/lib/prices-db.mjslib/db/migrations/0054_price_release_facts.sqlconfig/source-rights/{statat,eurostat,oenb}.public-display.json,docs/evidence/source-rights-{statat,eurostat,oenb}-2026-09-17.md- Tests:
tests/unit/scripts-lib/prices-release-publisher.test.mjs,tests/unit/scripts-lib/prices-db-release.test.mjs,tests/integration/prices-publisher-postgres.e2e.mjs