Skip to content

Next expansion — gap analysis

Date: 2026-07-10 Companion to: related-work synthesis Decision supported: scope of the next planning cycle (Regnskabsdata / CVR_Events / people entity-resolution).

Each gap names the evidence status, the decision it blocks, and the cheapest honest way to close it. "Close at" says where in the flow it belongs: grill (decide with Chris now), PRD (record as scoped decision), issue (close with data during implementation), spike (small throwaway probe before or inside an issue).

Theme 1 — Regnskabsdata

ID Gap Evidence status Blocks Close by Close at
G1 ES version contradiction. Root of distribution.virk.dk reports ES 6.8.23 (live, 2026-07-10); docs/data-sources/cvr-elasticsearch.md and ADR-0005 say "ES 1.7 dialect" per Erhvervsstyrelsen's guide. Unverified whether the authed cvr-permanent index accepts modern DSL (search_after, sliced scroll) or sits behind a restricting proxy (the observed _cat/indices?v rejection points at something non-standard). Contradictory — live probe vs. official guide Adapter design for both ingesters; whether regnskab_es can reuse cvr_es query machinery or is a second dialect Authed probe from Cloud Run (or one-off local test): search_after + sliced-scroll smoke against cvr-permanent and offentliggoerelser; then correct cvr-elasticsearch.md and, if confirmed, amend ADR-0005's consequence issue (fits E1 #19's fixture work; inherit finding in Regnskab PRD)
G2 Index population. ~~Value set of offentliggoerelsestype unknown~~ CLOSED (live, 2026-07-10): all 6,410,977 records match offentliggoerelsestype: regnskab, zero non-regnskab. The index is annual reports only; no population filter needed. Closed — live counts — — done
G3 Corrections model. omgoerelse arrives as a new publication; prephase doc 04 simultaneously proposes unique (cvr, period_start, period_end). Supersede-in-place vs. append breaks that constraint one way or the other. Live count: 19,727 corrections (~0.3%) — rare but real. Internal contradiction in prephase doc 04; volume now known financial_reports PK/uniqueness, idempotency rule, "latest report per period" read semantics Decide at grill: recommend append-with-is_correction + a latest view (keeps ledger idempotency trivial); verify real omgoerelse pairs during implementation grill → issue
G4 XBRL extraction approach. DK taxonomies are yearly-versioned (DK GAAP + IFRS); unknown: how many taxonomy versions are live in the corpus, whether the ~10 promoted nøgletal are stable element names across versions, Arelle vs. hand-rolled extractor cost. Missing — no filing parsed yet The financial_metrics contract and the biggest effort unknown in the epic Spike: fetch ~20 XBRL docs across years/company sizes, table the element names for revenue/equity/result/assets per taxonomy version spike (inside first Regnskab metrics issue)
G5 Durable record key. CLOSED (live, 2026-07-10): indlaesningsId is missing on 3,440,462 of 6,410,977 records (54%) — unusable as the idempotency key. Use the ES _id (urn:ofk:oid:…, present on every hit) as load_id; prephase doc 04's key choice is superseded. Closed — live counts — — done
G6 Document-tier operational facts. Partially closed (live): every record has ≥1 document; 4,028,669 (63%) carry an XML (XBRL) document — the rest are PDF/paper-only, so financial_metrics null-rate ≈ 37% of reports (concentrated in older/exempt filings). Still open: regnskaber.virk.dk fetch behavior (plain HTTP, auth-free assumed), rate tolerance, sizes, URL stability. Half-closed Lazy-fetch design, politeness budget Probe a handful of dokumentUrls during the tracer bullet issue
G7 Delta cursor semantics. sidstOpdateret exists (live) and upstream updates ~10 min (prephase); unknown whether it moves on correction/re-publication and whether it is monotonic enough for the watermark rule. Thin Whether the CVR watermark discipline transfers unchanged Range-query counts over recent windows during the tracer bullet issue
G8 Framework fit of the lazy path. core/ (as planned in E0–E1) has no on-demand fetch machinery, no details_cached_at convention, no per-document politeness. What is the minimal core/ extension that doesn't contradict "ingesters own dataflow, adapters own the conversation"? Design question, not evidence Module boundaries in the Regnskab PRD Decide at grill; the answer is the template for every later detail-heavy source (Tinglysning, EMO) grill → PRD

Theme 2 — CVR_Events

ID Gap Evidence status Blocks Close by Close at
G9 Trigger condition is not measurable. ADR-0005 defers adoption "gated on evidence" and names ownership-change monitoring as the likely trigger, but no threshold exists (detection-latency requirement? observed delta lag?). Missing by design Ever deciding to adopt; also prevents accidental scope creep now Amend ADR-0005's open question into a recorded trigger contract (e.g. "product scenario requiring Y") grill (this cycle's actual CVR_Events deliverable)
G10 Actual freshness of the chosen surface. Unknown how quickly changes appear in distribution.virk.dk's sidstOpdateret relative to reality — i.e. what latency the daily delta actually delivers and whether deltas are ever missed. Missing — measurable for free once E1 runs Honest baseline for the G9 trigger; sizing the reconciliation drift question (ADR-0005) Ledger + reconciliation diff stats already planned in E4/#24–#25; add "report observed delta lag" to acceptance criteria issue (E4; no new work)
G11 Event surface mechanics unexplored. Entity-based events model (late-2025): schema, cursor/replay, delivery guarantees, whether person-related events are visible without the OAuth+MitID grant — all untested; no docs/data-sources/ entry. Missing Nothing today (gate closed); becomes the first task if G9 ever fires A data-source access test when the trigger fires — deliberately not now deferred (recorded in ADR amendment)

Theme 3 — People entity-resolution

ID Gap Evidence status Blocks Close by Close at
G12 No input source exists. The class-B people source (SCRAPER/02, website team pages) is Phase-3 enrichment work, not in this repo; ER built now has no caller and no data. Fact (absence) Justifying any ER implementation this cycle Keep the ADR-0006 gate; produce a design contract only grill
G13 Alias-layer shape undecided. SCRAPER/02 sketches match keys, per-field provenance, and "keep both, ranked" — but no table design exists (person_mentions/people_aliases: standalone vs. FK-to-people, merge/unmerge audit trail, confidence model), and ADR-0006 forbids widening the enhedsNummer rule. Prephase sketch only A stable identity contract the future scraper can build against; avoids the scraper improvising one later Short design ADR this cycle (schema + conflict rule + provenance vocabulary), no implementation grill → ADR
G14 Lawful basis for scraped people data not decided. ADR-0006 covers registry data (legitimate interest, published-for-transparency); scraped team pages are personal data with an Art. 14 information duty and different proportionality (SCRAPER/02 legal section). Missing decision, prephase analysis exists Any future storage of scraped people; must precede the scraper, not follow it Record as an explicit precondition in the ER design ADR (decision itself can wait for Phase 3 kickoff) ADR (as precondition)
G15 Cross-company person identity. Company-scoped matching (the SCRAPER/02 key) doesn't answer "same human at two companies" without a registry key. Unknown whether any product scenario needs it — minimization posture discourages it. Underexplored, deliberately Whether the alias layer needs a person-level cluster ID or stays mention-level State in the ADR: mention-level now; cluster ID is a new ADR with its own lawful-basis analysis ADR (scoping statement)

Cross-cutting

ID Gap Blocks Close at
G16 Serial-execution collision. ADR-0003 (serial implementation) + CVR issues #16–#26 are unstarted; Regnskabsdata implementation cannot begin before E1's core/ exists. Planning depth is fine; issue publication timing needs a decision (publish blocked-on-#19 now vs. after E1 lands). /to-issues timing for Regnskabsdata grill
G17 Storage sizing. ADR-0005 notes the Supabase compute add-on becomes live at CVR backfill; +6.4M financial_reports rows plus lazy JSONB xbrl_facts was not in that sizing. Probably fine (metadata rows are skinny) but unquantified. Cost expectations in the PRD PRD (estimate from G2/G6 numbers)

Opportunity statements (what closing these buys)

  • Closing G1 upgrades both adapters (possible search_after instead of scroll state) and de-risks the single most repeated claim in the platform's source docs.
  • G2+G5 were closed the same day with free queries (results above): the PRD's population, idempotency-key, and XBRL-coverage sections are now fact, not assumption.
  • G9+G13 are the cheap deliverables that let this cycle honestly say it "handled" CVR_Events and entity-resolution without violating the gates the ADRs put up — each is one document, not one epic.

Handoff

Proceed to /grill-with-docs carrying: the scope recommendation from the synthesis and the grill-tagged gaps (G3, G8, G9, G12, G13, G16). The G2/G5/G6 spikes are already closed above.