Skip to content

docs: ADR-053, temporal distribution of imprecise UCDP events (#533) - #539

Open
Polichinel wants to merge 1 commit into
developmentfrom
docs/533-temporal-adr
Open

Polichinel wants to merge 1 commit into
developmentfrom
docs/533-temporal-adr

Conversation

@Polichinel

Copy link
Copy Markdown
Contributor

What

Adds ADR-053 — Temporal Distribution of Imprecise UCDP Events (Known Temporal Imprecision), the twin of ADR-049 on the other axis, and indexes it in docs/ADRs/README.md.

Status: Proposed. Documentation only — no source file is touched, and nothing about what the pipeline invokes changes.

Why

ADR-049 governs what to do when UCDP does not know where an event happened. Nothing governed what to do when it does not know when. The temporal axis had a mechanism (temporal_distribution.py), a held-out evaluation (scripts/evaluate_temporal_allocation.py), registered strategies and a named profile (kgi_kti_activity) — and no decision record. ADR-023 pins the parity invariants without deciding which strategy is correct, and ADR-049 states explicitly that the temporal half "remains an open question and is not settled by this ADR."

ADR-000 requires code and ADRs to agree. There was nothing to agree with.

The two measurements that drive it

Both taken against builds from the same consolidated store.

1. date_end_only produces a calendar artifact.

Share of state-based fatalities by calendar month, whole store:

Dec Oct annual spread
production_parity 12.27% 13.33% 6.99 pp
kgi_kti_activity 6.62% 9.02% 4.68 pp

An even year is 8.33%. Events with date_prec >= 4 are 26.7% of state-based mass, and their date_end months are not uniform — 28.09% in October, 26.09% in December. Of the December enders 85.8% start in January (Jan–Dec annual spans). Of the October enders 88.8% start in August, dominated by a single event (463137, 121,848 fatalities, Aug–Oct 2022).

So December is systematic and October is one event large enough to bend the whole-history distribution by itself. Both are endpoint-placement artifacts; only one is seasonal.

This matters more than the spatial artifact because models learn seasonality. A spatial spike is one wrong cell. A calendar spike is a recurring twelve-month signal present in every year of training data.

2. The spatial fix alone makes it worse.

kgi_distributed — ADR-049's recommendation, spatial axis only — moves December from 12.27% to 15.60% and widens the spread from 6.99 pp to 9.38 pp. exclude_where_prec=() readmits 43,933 events carrying 785,626 fatalities (+22.6%) that are overwhelmingly annual summary events, spreads them correctly across space, then stacks every one into the same month.

Shipping ADR-049's recommendation without a temporal decision would have deepened the seasonality defect while closing the spatial one.

Prior art, recorded rather than claimed as novelty

The legacy UCDP ingestor already spread summary events across their span (fix_summary_events), and ceil_split reproduces it hunk for hunk — confirmed in the dossier's legacy-loader forensics. This ADR does not introduce temporal distribution to VIEWS; it restores it, and fixes three defects plus the reason it stopped running:

Legacy / ceil_split ADR-053
Trigger best >= span — magnitude proxy, date_prec never consulted date_prec >= 4 and span > 1
Rounding ceil(v/m) — does not conserve largest-remainder apportionment, exact
Weights flat across the span observed monthly activity, dyad-first cascade
Reached at all? no — annual date_end_only override preempts it override removed

The rounding defect is measured, not inferred: ceil_split reports 96 deaths across 74 events that appear in no version of the source store. Both apportioning strategies report zero.

Why activity over uniform

Held out, each event removed from its own indicator, 20,000 events with known true month:

strategy mean hit share mean distance
date_end_only (parity) 0.0703 5.564 months
uniform_span 0.0825 4.883 months
activity_weighted 0.1525 3.305 months

The ADR states plainly that the gain is in the mean, not the median — median hit share is 0.00 for all three — so this helps the typical fatality, not the typical event.

It also argues that uniform's flatter year (2.33 pp) is a different distortion rather than a better one: it smears a year-long war into twelve identical months, erasing genuine seasonality along with the fake kind.

What the ADR deliberately does not hide

  • The load-bearing assumption, named as the thing most likely to be wrong: a month with zero observed activity gets zero allocation, and absence of well-observed reporting is not absence of fighting. 1,760 KTI events have at least one zero-activity month in their span.
  • Activity is not uniformly better. It leaves one more month above a 70% single-cell share than uniform (5 vs 4), and its worst cell-month over all history is higher (39,487 vs 24,979). Both follow from the same mechanism that makes it accurate on the held-out evaluation.
  • Cost: row count 372,303 → 2,654,111 (7.1×), build time 86.6 s → 425.6 s, ADR-041 content digests invalidated, VIEWSER parity broken by construction.
  • §9 records a defect it does not fix — the per-row temporal provenance annotations are computed and dropped at the write, the same boundary defect as ADR-049's spatial annotations. Fourth recorded instance of the pattern; left for Summary/aggregate events are concentrated by measurement, not by reality — and one of them is ~63% of our primary evaluation score #533 to close once, deliberately.

A new observer it proposes

§Validation adds a distributional guard: December's share of state-based fatalities must stay below 9.0%. No per-event assertion can see this defect. ADR-049's 2026-11-04 amendment records what happens without such a guard — cell 148759 grew from 273,076 to 274,358 and nothing went red, "a decision with no observer," concern C-365's mechanism.

What it does not decide

  • Does not change what the pipeline invokes. build_viewpoint.py still defaults to production_parity and refresh_pipeline.sh still passes it.
  • Does not settle source_distribution_map — named in §7 as under separate discussion with Håvard.
  • Does not retrain or revalidate any model. 28 views-models requirements files read data this would change; notifying them is a precondition of any switch.
  • Does not close C-37 (codebook semantics stated, not yet asserted in code).

Validation

  • ruff check . — clean
  • tests/test_docs_citations.py, test_falsification_doc_consistency.py, test_falsification_docs_current.py, test_falsification_impl_doc_consistency.py, test_adr049_profile_contract.py — 44 passed
  • Every file the ADR cites exists on development, except notebooks/summary_event_allocation.ipynb, which is in docs: a notebook for summary-event allocation, and two findings it surfaced (#533) #538 and also targets development.

Correction made during drafting

An earlier draft attributed October's spike to Jan–Oct spans. Measured, that is wrong — 88.8% of October enders start in August, and the peak is essentially one event. Corrected before commit, and the measurement is in the ADR rather than the inference.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01EfeuULbPTqrx4qtNki5dy9

The temporal axis had a mechanism, a measured evaluation, a registered
strategy and a named profile, and no governing decision. ADR-023 pins the
parity invariants without deciding which strategy is correct; ADR-049 says
in as many words that the temporal half "remains an open question and is
not settled by this ADR". This closes that gap.

Decides activity-weighted temporal distribution, triggered on UCDP's own
date_prec >= 4 declaration rather than on ceil_split's magnitude proxy,
through the conserving apportionment rounder.

Two measurements drive it, both taken against builds from the same store:

- date_end_only produces a calendar artifact. December carries 12.27% of
  all state-based fatalities ever recorded and October 13.33%, against
  8.33% for an even year. Events with date_prec >= 4 are 26.7% of
  state-based mass and their date_end months are not uniform: 28.09% land
  in October, 26.09% in December. Of the December enders 85.8% start in
  January (Jan-Dec annual spans); of the October enders 88.8% start in
  August, dominated by one event (463137, 121,848 fatalities, Aug-Oct
  2022). December is systematic, October is one event.

- The spatial fix alone makes it worse. kgi_distributed moves December
  from 12.27% to 15.60% and widens the annual spread from 6.99pp to
  9.38pp, because exclude_where_prec=() readmits 43,933 events carrying
  785,626 fatalities that are overwhelmingly annual summary events, then
  stacks every one on its end month. Shipping ADR-049's recommendation
  without a temporal decision would have deepened the seasonality defect
  while closing the spatial one.

Prior art is recorded rather than claimed as novelty: the legacy UCDP
ingestor already spread summary events across their span, and ceil_split
reproduces it exactly. This ADR changes the trigger (declared precision,
not best >= span), the rounding (exact, not ceil, which reports 96 deaths
across 74 events that appear in no source version), the weights (observed
activity, not flat), and removes the annual date_end_only override that
preempted the whole mechanism for every finalised release.

Status is Proposed. It does not change what the pipeline invokes, does not
settle source_distribution_map, and does not close C-37.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EfeuULbPTqrx4qtNki5dy9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant