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 |
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_afterinstead 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.