Plan: Ingest kommune-level 10th-grade absence (fravær) statistics
Ingests Udir's GSK_fravaer ("Fravær i grunnskole") report — median and average days/hours of
documented absence, plus participant count, for 10th-grade pupils only, per kommune, for the
latest school year. Atlas's fourth Udir source, and [Q39]'s grunnskole half.
[Q39]'s videregående half (VGO_fravaer) is investigated in Phase 1 below and deliberately
deferred — its geography does not resolve to kommune at all, a genuinely new problem for Atlas,
not a parsing gap.
IMPLEMENTATION RULES: Before implementing this plan, read and follow:
- WORKFLOW.md - The implementation process
- PLANS.md - Plan structure and best practices
Status: Completed
Goal: Add udir-fravar as a served Atlas source — kommune-level 10th-grade absence,
Atlas's first annual-absence axis (previously only fhi-vgs-gjennomforing's 3-year completion
rate existed as a dropout-adjacent signal). Plugs Report #3 (Youth Outcomes) per
[Q39]'s own framing.
Last Updated: 2026-10-03
Investigation: INVESTIGATE-new-norwegian-public-sources.md §4 [Q39], next in the Tier-1 Udir sequence after PLAN-016-udir-nasjonale-prover.md
Prerequisites: None. udir is already a valid publishers.yaml provider, topics.yaml
already has education validated, and this source reuses the same statistikkportalen.udir.no
client udir-gsi/udir-elevundersokelsen-mobbing/udir-nasjonale-prover already proved out — no
new infrastructure for the GSK half shipped by this plan.
Phase 1: Confirm the real shape — DONE (verified 2026-10-03)
Checked live rather than trusting [Q39]'s description or assuming either half's shape from a
sibling Udir source. Mixed news this time: the grunnskole half matches udir-gsi's shape
cleanly; the videregående half does not resolve to kommune at all — a genuinely new problem, not
previously seen in any Udir source this session.
Tasks
- 1.1 Find the live reports. Confirmed — listed all 136 Rapportside entries
(
GET /rest/v1/Rapportside), the same techniquePLAN-016used. [Q39] names one source but the live listing shows it is genuinely two separate reports, not one report with a filter:GSK_fravaer("Fravær i grunnskole", basePathrest/v1/Statistikk/GSK/FravaerG/1/1) andVGO_fravaer("Fravær i videregående skole", basePathrest/v1/Statistikk/VGO/ResultatV/1/3). No "(gammel)" duplicate exists for either — confirmed by re-scanning the full 136-entry list for any otherfrav-matching name. - 1.2
GSK_fravaer'sEnhetIDIS the row hierarchy, matchingudir-gsi/udir-nasjonale-prover— NOTudir-elevundersokelsen-mobbing's column-dimension shape. Confirmed live:metadata.rowHierarchyis["Nasjonalt","Fylke","Kommune","Enhet"], andradSti=-12.*.*returns 371 rows in one call — 15 fylke (2-segmentid) + 356 kommune (3-segmentid, including Svalbard2100). Same depth-filter-by-segment-count technique asudir-gsi/udir-nasjonale-proverapplies directly. - 1.3
VGO_fravaer'sEnhetIDhierarchy has NO kommune level at all — a genuinely new shape, not previously encountered. Confirmed live:metadata.rowHierarchyis["Nasjonalt","Fylke","Enhet"], andradSti=-12.*returns exactly 15 rows (fylke only) — the next depth down is individual schools (org numbers, e.g."982759846"), not kommuner. This is not a parsing gap: Norway's videregående skoler are organised and owned by fylkeskommune, not kommune, and Udir's ownEnhetIDtree reflects that directly. No kommune-level videregående fravær exists in this API at all — the only path to a kommune figure would be aggregating individual schools by their own postal/registered kommune, the samedim_schoolcrosswalk workPLAN-010-udir-gsi.md[Q2] already deferred for GSI. - 1.4 v1 scope decision: ship
GSK_fravaer(grunnskole, kommune-resolved) only. DeferVGO_fravaerexplicitly, named as a deviation from [Q39]'s own framing, not silently dropped. Reasons: (a)VGO_fravaer's real resolution is fylke or school, not kommune — publishing it at fylke grain would be Atlas's first fylke-only-resolution indicator relation, confirmed by readingatlas-data/dbt/check-kommune-marts-exclude-sentinels.sh, which treats everyindicators__/mart_indicators__relation carrying akommune_nrcolumn as kommune-grain by construction — no precedent exists for a relation whose grain is fylke; (b) publishing it at school grain repeats the exactdim_schooldeferral already made twice (PLAN-010's [Q2], this investigation's own [Q33]) rather than resolving it. This is a cross-cutting decision (extends investigation §"D. New geographic resolutions", which only anticipated resolution finer than kommune, not coarser) and does not belong buried inside one source's Phase 2 — flagged as its own open question below ([Q4]) rather than decided unilaterally here. - 1.5 Confirm
GSK_fravaer's own scope is 10th grade only, not all grunnskole grades. Confirmed, structurally, not just by prose —GSK_fravaer's owngyldigeFiltrelist (EierformID, EnhetID, KjoennID, TidID, VisAntallPersoner, VisMaaltall) carries noTrinnIDfilter at all; the report's own description text states why: "Tabellen viser medianen for antall dager og timer fravær for elever på 10. trinn, slik det erført på vitnemålet" (the table shows median days/hours of absence for 10th-grade pupils, as recorded on the diploma). This measure is structurally defined as 10th-grade-only upstream, not a filter Atlas chose to pin. - 1.6 Confirm the measure columns. Confirmed — requesting both measure-toggle filters at
once (
VisAntallPersoner(1)_VisMaaltall(1)) returns 5 columns in a single call:Median dager,Median timer,Snitt dager,Snitt timer,Antall elever— more thanfilterVerdier's ownVisMaaltallvalue list suggested (it names only"Snitt timer"as theid:1option's label; the real response carries all four day/hour statistics plus the participant count under that one flag). Comma-decimal ("9,0") and ASCII-space-thousands ("4 277") cell shapes, sameparseCellconventionsudir-nasjonale-proveralready built — reused directly, no new parsing logic needed. - 1.7 Confirm the suppression marker and sentinel codes for
GSK_fravaer. Confirmed — literal*(same convention as every Atlas source this session), verified on Modalen (fully suppressed, all 5 measures). Svalbard (2100) present at the same tree depth as genuine kommuner, with real data (26 pupils) — same precedent as every prior Udir source, resolves throughclassify_region_code's existingsvalbardbranch, no new macro code needed. Utsira entirely absent from the row set (not suppressed — the "real absence, not suppression" shapeudir-elevundersokelsen-mobbing/udir-nasjonale-proveralready found, likely too small a 10th- grade cohort to report at all that year). - 1.8 Confirm
Utlandet/2599forGSK_fravaer. Confirmed present AND carrying real, non-suppressed data every year — same-13("Utlandet")→-34("25")→-476("2599", "Utlandet, uspesifisert") chainudir-nasjonale-proverfound, and the same "carries real data" shape, not an empty one. ⚠️ A real correction, caught at the start of Phase 2, not Phase 1: this plan originally reportedradSti=-13.*.*as returning 0 rows for this report. That manual test had an explicit, conflictingEnhetID(-12)filter alongside the-13.*.*radSti anchor — an invalid combination, not a real empty response. Re-tested without the conflicting filter (confirmed live, 65 real pupils for 2024-25) and the real data appeared immediately; the real ingest module (which never setsEnhetIDin the filter string) confirmed this again independently on its first real run —Utlandetreturns 5 real measure rows for every one of the 11 backfilled years. v1 decision unchanged in substance: query the-13.*.*anchor every run and include it — it is real, non-suppressed, upstream-published data, same discipline asudir-nasjonale-prover's own [Q1]/1.5 decision. - 1.9 Confirm available years. Confirmed —
GSK_fravaer'sTidIDlists 11 school years (2014-15 through 2024-25);VGO_fravaer's lists 12 (2013-14 through 2024-25) — far more history thanudir-nasjonale-prover's 4. ⚠️ A real correction, caught during Phase 2 start, not Phase 1: this plan originally recommended "latest year only, matching every other annual Udir source's convention" — that claim was wrong, found by actually readingudir-gsi's ownindex.tsrather than trusting its README's prose.udir-gsidoes NOT defer backfill — its main loop (for (const year of years)) issues one data call per discovered year and ingests all 12, confirmed live (GSK/GSI/1/8/filterVerdierreally does return 12TidIDentries, and the shipped row count, 18,816, is consistent with multi-year, not one). The real pattern across Udir sources is backfill fully when the call cost is cheap, defer only when it is not —udir-elevundersokelsen-mobbingdeferred because of its ~702-call/year cost (a real, stated reason), not because of a universal "latest year only" convention; whetherudir-nasjonale-prover's own "deferred by convention" claim holds up to the same scrutiny is not re-litigated here, but not inherited either. Revised recommendation: backfill all 11 years forGSK_fravaer— this report's call cost is cheap (2 radSti calls per year, same shape asudir-gsi), so there is no cost reason to withhold the real history, andudir-gsi's own actual behavior is the closer precedent than any sibling's prose claim. - 1.10 Confirm licence. NLOD, same portal (
udir.no/om-udir/data/) already confirmed explicitly forudir-gsi/udir-elevundersokelsen-mobbingand inherited (not re-fetched a fourth time) forudir-nasjonale-prover— inherited again here for the identical reason: same portal, same organisation, no surface difference to re-check.
Validation
✅ Confirmed 2026-10-03. Real API calls made directly against statistikkportalen.udir.no for
both reports, the full filter-value discovery for both, and real data (Agder's kommuner, Modalen,
Bygland, Beiarn, Svalbard, the Utlandet anchor, and VGO's fylke-level rows) — not inferred from any
sibling Udir source's shape.
Open Questions
- [Q1] Scope —
GSK_fravaeronly, or wait and ship both halves together? Recommendation: shipGSK_fravaernow, deferVGO_fravaer. The two reports are genuinely separate endpoints with genuinely different geography (kommune vs. fylke/school) — there is no shared "fravær" dimension table to design once for both, unlikeudir-nasjonale-prover's two report versions (which shared kommune geography and only differed in valid grades). Waiting on a fylke-resolution ordim_schooldecision to ship the grunnskole half, which is ready now, would be the same mistake PLAN-014/015/016 explicitly avoided — scope conservatively, ship what's real, name the rest as a deviation. - [Q2] Source naming. Should this source still be named
udir-fravareven though it ships only the grunnskole half? Recommendation: yes, with the manifestdescriptionstating "10th grade, grunnskole only" explicitly —udir-sluttet-vgs/[Q40] is already a separate planned source for the videregående dropout signal, so there's no risk of the name implying broader scope than what's shipped; a futureVGO_fravaeringest (if the fylke/school-grain question resolves) would need its own source id anyway given the geography difference, not become a second half of this one. - [Q3] Measure scope — all 5 columns, or just the headline median?
Median dager/Median timerare what the report's own description foregrounds (and what the diploma itself records);Snitt dager/Snitt timer/Antall eleverare the same call's free extra columns. Recommendation: all 5 — no extra cost (same single call per kommune), andAntall eleveris needed context for interpreting the other four (a median over 11 pupils reads differently than one over 400). - [Q3b] Historical backfill — RESOLVED during Phase 2 start, 2026-10-03. See 1.9's
correction: back fill all 11 available years, not latest-only.
udir-gsi's own real behavior (checked by reading its code, not its prose) is cheap-backfill-fully, andGSK_fravaer's call cost matchesudir-gsi's shape exactly. - [Q4] The fylke/school-grain
VGO_fravaerdecision — cross-cutting, not this plan's to make. Flagging for1PRIORITY.md/the investigation doc rather than deciding unilaterally: does Atlas want to (a) support a fylke-only-resolution indicator relation as a new first-class grain (extends investigation §D), (b) builddim_school+ a school→kommune crosswalk and aggregate (same work already deferred twice), or (c) not shipVGO_fravaerat all and treat [Q39]'s videregående half as answered byudir-sluttet-vgs/[Q40] instead (which is VGS-specific dropout, a related but not identical signal)? Not resolved here. - [Q5] Presentation sensitivity. Per-kommune 10th-grade absence is a sharp, small-number-prone
signal (confirmed: Modalen's entire row suppressed, Utsira absent) — same
presentation_policy: 'sensitive'consideration flagged for every prior NAV/Udir source this session. Not resolved here; a Phase 2/3 decision.
Phase 2: Ingest module + raw table — DONE (verified 2026-10-03)
Tasks
- 2.1 Created
atlas-data/ingest/src/sources/udir-fravar/:manifest.yml(source_id: udir-fravar,provider: udir,periodicity: P1Y,license: NLOD, description stating "10th grade, grunnskole only — see [Q2]"),index.ts,parse.ts,fetch_retry.ts(copied),README.md,__tests__/with real captured fixtures covering: an ordinary kommune (Agder's Arendal), a fully-suppressed row (Modalen), Svalbard (2100), and theUtlandetanchor's real data (2599). 17 tests, all passing. - 2.2
parse.ts— reusedudir-gsi's depth-by-segment-count row filter directly (confirmed the same shape in Phase 1.2), issuing 2 data calls per year (radSti=-12.*.*+radSti=-13.*.*, same anchor pairudir-nasjonale-proveruses — both carry real data, see 1.8's correction), looped over all 11 discovered years (see 1.9's correction — 22 calls total, confirmed live in 6.1s wall time, still cheap, matchingudir-gsi's own real backfill-fully behavior rather than a "latest year only" convention that turned out not to be universal). All 5 measure columns parsed per row; sameparseCellcomma-decimal/space-thousands handling asudir-nasjonale-prover. - 2.3 Migration
066_raw_udir_fravar.sql:raw.udir_fravar(region_code, year, measure, value, loaded_at)— one row per region/year/measure. Nogradecolumn, as planned (see 1.5) — this source is structurally 10th-grade-only. - 2.4 Dagster registration —
raw_other.py/schedules.py, annual cadence, existing weekly-polled job pattern (same group as the other three Udir sources), no new job. - 2.5 Added
ingest:udir-fravarnpm script FIRST, verified viacheck-every-source-has-an-ingest-script.sh(✓ all 54 ingest source directories covered) and the realnpm run ingest:udir-fravarinvocation before any other Phase 2 work, per this session's standing discipline sincehusbanken-bostotte's urb-agents#1807.
Validation
✅ Confirmed 2026-10-03. Real run against the live API against local scratch Postgres: 22
calls, 21,290 rows written, zero rows silently dropped. Svalbard (2100, real data, present in
6 of 11 years), the Utlandet anchor's real data (2599, present in all 11 years), and Modalen's
fully-suppressed row all confirmed against real loaded data, not fixtures alone.
⚠️ A genuinely important correction, caught during this exact validation step: Phase 1's own
1.8 finding that the Utlandet anchor returns zero rows was WRONG — see 1.8/1.9's corrected
text above. The real ingest module's first live run (which never sets EnhetID in its filter
string, unlike the flawed manual Phase 1 test) returned 5 real measure rows for 2599 on every
single one of the 11 years, immediately. Caught before this plan's Phase 3 work began, not
after.
Phase 3: dbt staging and marts — DONE (verified 2026-10-03)
Tasks
- 3.1 Added
raw.udir_fravartomodels/indicators/sources.yml. - 3.2
indicators__udir_fravar.sql—kommune_nr/region_kindviaregion_code_to_kommune_nr/classify_region_code. Confirmed against real loaded data:region_code='2100'→region_kind='svalbard',kommune_nrNULL (30 rows, present 6 of 11 years);region_code='2599'→region_kind='unspecified_within_fylke',kommune_nrNULL (55 rows, present every year) — both exactly as predicted, no macro change needed. Cleancontents_codeslugs for the 5 measures (median_dager/median_timer/snitt_dager/snitt_timer/antall_elever), same CASE WHEN conventionudir-nasjonale-proverused. - 3.3 Documented columns in
models/indicators/schema.yml;mart_indicators__udir_fravar.sqlapi passthrough +marts/api/schema.ymlentry.mart_atlas_inventory.sqldepends_on + relation list updated;generate_api_v1.py'sSCHEMA_COMMENTbumped (50→51 total,udir (3)→udir (4)). - 3.4
dbt buildagainst real loaded data — clean (14/14 for the targeted build;mart_atlas_inventoryand its full upstream dependency tree also rebuilt clean, confirmingindicators__udir_fravarreportsrow_count=21290,is_empty=false,contributing_sources={udir-fravar}). Full 17-scriptatlas-data/dbt/check-*.shsuite run — all 17 pass clean (updated counts: 91 wrappers, 54 sources, 51 fact relations). Full ingest test suite (391 tests, 27 files) andtsc --noEmitboth pass clean.website/npm run buildcompletes clean, no broken links.
Validation
✅ Confirmed 2026-10-03. Real local Postgres, not an empty schema. 2100/2599 resolution
confirmed with a direct query against the built marts.indicators__udir_fravar table, same
discipline as every prior sentinel this session — not assumed from the macro's existing branches
alone.
⚠️ Two pre-existing, unrelated test failures observed during the full dbt build run
(extracted_columns_are_still_populated, raw_sources_were_refreshed_recently) — both are
local-scratch-Postgres staleness (most sources in this environment were never loaded, and the
Brreg-enriched jsonb columns were populated once on 2026-09-24 and have gone stale since). Neither
names udir-fravar or udir_fravar anywhere in their output; isolated by excluding both and
confirming mart_atlas_inventory and its full dependency tree then build 100% clean (PASS=1639,
WARN=1 pre-existing dim_postnummer relationship, ERROR=0). Not this plan's defect to fix — a
standing local-environment gap, same class of thing as this session's own
[[local-postgres-for-real-dbt-evidence]] note.
Phase 4: Deploy and verify arrival — IN PROGRESS (submitted 2026-10-03)
PR #536 (Phases 2-3) merged to main as 0ffeb79. Image build (run 37155371998) completed and
published:
artifact_digest sha256:fe8ab71e9dcf91b4b5dc6554a3f79bf198ed494818bab2c9cd78716082866530 ghcr.io/terchris/atlas-data/uis:v20261003-0ffeb79
image_digest sha256:bd146257a33a215514e1f8bf99282ff328efb28f2c99973a2de7a4cacba06253 ghcr.io/terchris/atlas-data:v20261003-0ffeb79
Both copied verbatim from the release's own uis-artifact.json, not reconstructed.
render-template-info.sh v20261003-0ffeb79 confirmed clean (63 raw = migrations, 93 marts
stated once, 54 automated sources covered).
atlas-data/uis/lands-with.sh 59e8bc3..0ffeb79 confirmed: annual_sources_refresh then
transform_and_publish, plus a separate template-info pin (pin and a run are two actions, see
[[a-pin-and-a-run-are-two-actions]]).
Sent:
- urb-agents#1827 → imac: Dagster deploy
request, row-count prediction 21,290 for
raw.udir_fravar/indicators__udir_fravar. This source's call volume (22 calls total across 11 backfilled years, see 1.9) is cheap, unlikeudir-elevundersokelsen-mobbing's — measured live in dev at 6.1s wall time, not assumed from a Phase 1 sample (per [[exploratory-calls-dont-reveal-sustained-api-latency]]). No "slow run" warning needed. - urb-agents#1828 → ops-dev: template-info pin nomination for the same tag.
Only predict indicators__udir_fravar as a served relation — never a second
mart_indicators__... entry, see [[mart-prefix-is-never-a-served-endpoint]].
✅ DONE (verified 2026-10-03). imac's report (urb-agents#1827): annual_sources_refresh hit
the slow end of this API's latency range (~4-6s/call, confirming
[[exploratory-calls-dont-reveal-sustained-api-latency]]'s own run-to-run-variance finding again)
— ~86 minutes total, correctly distinguished from a hang by polling Dagster's own API directly
rather than trusting the --wait client's 3600s timeout. raw.udir_fravar: 21,290 rows, exact
match to prediction. transform_and_publish succeeded; zero PostgreSQL corruption events across
the full window; a 6-source regression check confirmed unchanged.
Independently re-verified against the live public API, not taken on imac's report alone:
GET /indicators__udir_fravar?limit=1 with count=exact → content-range: 0-0/21290, exact
match; real row returned with region_kind=kommune/kommune_nr correctly resolved.
meta_sources?source_id=eq.udir-fravar → served_as: [indicators__udir_fravar] only, no second
mart_indicators__... entry.
ops-dev's pin report (urb-agents#1828): cross-checked every reported number against the actual
committed template-info.yaml diff (sources: 53→54, publishers: 9 unchanged,
public_relations: 90→91, 62/92/78→63/93/79 raw/marts/views, NLOD for 50 of 54) — all
exact, no discrepancy.
Both urb-agents#1827 and #1828 closed with the verification evidence attached as comments.
Acceptance Criteria
- The mechanism is verified live — Udir's
FravaerGtable viastatistikkportalen.udir.no, confirmed by direct API calls, not assumed from any sibling Udir source's shape. - The videregående half's genuinely different geography is documented, not silently
dropped —
VGO_fravaerhas no kommune-level resolution in this API at all; deferred as [Q4], not shipped as a degraded or invented kommune mapping. - Licence independently confirmed for this surface — NLOD, same portal already confirmed explicitly earlier this session, inherited consistent with established discipline.
-
udir-fravaringests cleanly with zero rows silently dropped, including the2100and2599sentinels (both carrying real data) and at least one suppressed row all represented. -
indicators__udir_fravarand its mart build and test clean against real loaded data, with2100/2599resolving throughclassify_region_codeexactly as predicted. -
udir-fravarappears inmeta_sources.served_asafter a real deploy, independently verified via livecurl. - Golden-file tests cover: an ordinary kommune, a suppressed row, Svalbard, and the Utlandet anchor's real data.
- The investigation and
1PRIORITY.mdare updated to mark this candidate shipped, with [Q4] (the fylke/school-grainVGO_fravaerdecision) carried forward explicitly as still open.
Implementation Notes
- One source id, one report, one geography.
GSK_fravaeronly —VGO_fravaeris a different report with a genuinely different, non-kommune geography; do not fold it into this source later without re-reading [Q1]/[Q4] first. - This report's
EnhetIDis a row hierarchy, likeudir-gsi/udir-nasjonale-prover— confirmed fresh viarowHierarchyin the response metadata, not inherited by assumption. - The
Utlandet/-13anchor carries real, non-suppressed data every year — the opposite of this plan's own original Phase 1 finding, which was a buggy manual test (an explicit, conflictingEnhetID(-12)filter alongside the-13.*.*radSti anchor). Corrected at the start of Phase 2; see 1.8. - No new Dagster job. Same weekly-polled group as the other three Udir sources.
- This source is structurally 10th-grade-only — not a filtered slice Atlas chose, Udir's own
FravaerGtable has no other grade to select. Do not add agradecolumn that would imply otherwise. - Backfills all 11 available years, not latest-only — a correction made at the start of
Phase 2 (see 1.9):
udir-gsi's real behavior (checked in its code) backfills fully when the call cost is cheap, and this report's cost matches. Real call volume is cheap (~22 calls total) — verify this holds in Phase 2, don't assume it from Phase 1's sample, per [[exploratory-calls-dont-reveal-sustained-api-latency]].
Outcome
Shipped end to end, 2026-10-03/04. Atlas's fourth Udir source: kommune-level, 10th-grade
documented absence statistics (GSK_fravaer), same EnhetID-row-hierarchy shape as udir-gsi.
Ingested 21,290 rows (region × year × measure, 11 backfilled school years), published to
api_v1, deployed to the live cluster by imac (urb-agents#1827), independently re-verified
against the public API — GET /indicators__udir_fravar?limit=1 with count=exact confirms
content-range: 0-0/21290.
Two real corrections, both caught during Phase 2, not Phase 1:
- Backfill convention was wrong. Phase 1 recommended "latest year only," matching what it
believed was every annual Udir source's convention. Checking
udir-gsi's own code (not its README's prose) found it backfills fully when the call cost is cheap — this report's cost matches (22 calls total), so v1 ships all 11 available years. - The
Utlandet/2599anchor's "empty response" finding was a Phase 1 testing bug, not a real finding. The manual curl test that produced an apparent zero-row result had an explicit, conflictingEnhetID(-12)filter alongside the-13.*.*radSti anchor — an invalid combination. The real ingest module (which never setsEnhetIDin the filter string) confirmedUtlandetcarries real, non-suppressed data every single year, the same shapeudir-nasjonale-proveralready found for its own Utlandet anchor.
VGO_fravaer (videregående) was deliberately not shipped — its EnhetID hierarchy has no
kommune level at all (fylke/school-grain only, confirmed live), a structural fact about how
Norway organises videregående skole, named as [Q4] and carried forward to the investigation doc
as a cross-cutting decision rather than resolved unilaterally.
Deploy took ~86 minutes for annual_sources_refresh — this API's per-call latency hit the slow
end of its own range again (confirming
[[exploratory-calls-dont-reveal-sustained-api-latency]]'s run-to-run-variance finding), correctly
distinguished from a hang by imac polling Dagster's API directly rather than trusting a client
timeout. Zero data-quality issues; a 6-source regression check confirmed nothing else moved.
Files to Modify
atlas-data/ingest/src/sources/udir-fravar/manifest.yml(new)atlas-data/ingest/src/sources/udir-fravar/index.ts(new)atlas-data/ingest/src/sources/udir-fravar/parse.ts(new)atlas-data/ingest/src/sources/udir-fravar/fetch_retry.ts(new, copied)atlas-data/ingest/src/sources/udir-fravar/README.md(new)atlas-data/ingest/src/sources/udir-fravar/__tests__/(new)atlas-data/ingest/package.json(ingest:udir-fravarscript — add this BEFORE shipping, verify with the realnpm runinvocation)atlas-data/migrations/066_raw_udir_fravar.sql(new)atlas-data/dagster/atlas_data/assets/raw_other.py,schedules.py(registration — annual cadence, existing weekly-polled job pattern)atlas-data/dbt/models/indicators/sources.yml,indicators__udir_fravar.sql(new),schema.ymlatlas-data/dbt/models/marts/api/mart_indicators__udir_fravar.sql(new),schema.ymlatlas-data/dbt/models/marts/api/mart_atlas_inventory.sql(depends_on + counted list)atlas-data/dbt/scripts/generate_api_v1.py(SCHEMA_COMMENT'sudirlisting — bump the count)atlas-data/template-info.yaml,website/docs/developers/index.md(regenerated counts)website/docs/ai-developer/plans/backlog/INVESTIGATE-new-norwegian-public-sources.md(markGSK_fravaershipped, carry [Q4]/VGO_fravaerforward as still open)website/docs/ai-developer/plans/backlog/1PRIORITY.md(mark shipped)