Skip to content

docs: R&D dossier for summary-event allocation (#533) - #535

Merged
Polichinel merged 2 commits into
developmentfrom
docs/533-summary-event-allocation-dossier
Nov 4, 2026
Merged

Polichinel merged 2 commits into
developmentfrom
docs/533-summary-event-allocation-dossier

Conversation

@Polichinel

Copy link
Copy Markdown
Contributor

Scaffolds the research program for how UCDP aggregate events are allocated in space and time.
Follows the catalog in reports/investigation_summary_event_concentration/ (#534) and responds to
#533 / #484.

No code, configuration or data changed. This is a staging area; on promote its design
graduates to a proposed ADR.

What's here

Ten documents in reports/2026-11-04_summary_event_allocation_dossier/: the README spine, an
840-paper library survey with a 19-item acquisition list, the option space, the harness audit, a
gated roadmap, a deliberately-empty analysis plan, a glossary, an append-only experiment log, and a
study of the legacy views3 GedLoader.

The harness audit: ~85% already exists

Found, not built: an OCP strategy registry (datafactory_provenance.registry.Registry) so a new
allocation strategy is registered rather than patched into the builder · profiles as the
default-off flag mechanism · the ADR-050 byte-identical fixture gates · a synthetic known-answer
path
for cheap readout · sixteen post-mortems, so negatives have a home.

The one real gap: nothing in this repo evaluates allocation quality — no locked baseline, no
metric protocol, no ground truth — because allocation was never treated as a choice. That is the
build, and it is constructible: 343,865 events are well-located and well-dated, carrying
68.6% of all mass, so a coarsen-and-recover test has a known answer and needs no new data.

⚠️ Three divergences — governance, not research

All routed to roadmap P0 rather than resolved here.

1. ADR-049 is accepted and dormant. Dated 2026-06-28, it decided spatial distribution "on by
default"
, named cell 148759 and event 463131, and set a golden test that cell 148759 should drop
from 273,076 to its proportional share. The profile uses passthrough:

ADR-049 said measured 2026-11-04
cell 148759, all history should drop from 273,076 274,358 — it grew
cell 150919, all history 79,841 80,064

Nothing is red because no test asserts it. That is C-365's lesson recurring, and 03 §C4 makes
writing that test a pre-flight blocker — expected red until the decision lands.

2. ADR-049's worked example misdescribes the code. It states event 463131 "is spread across 12
months by temporal distribution (correct)"
. It produces exactly one row; the
annual → date_end_only override catches it. So the spatial half is decided but dormant and the
temporal half is undecided and was believed decided.

3. production_parity does not reproduce the legacy country-month path. The legacy loader reads
different tables for its two aggregations — self.pg_ged (filtered) for priogrid-month,
self.ged (unfiltered) for country-month — so where_prec 4/6 deaths were present at country
level. Our grid_to_country_month() takes the grid as input, after the viewpoint filter, so
they are in neither: ~48,124 events, ~864,869 fatalities, 19.16% of all mass. Structural
rather than configurable, and an argument for making ADR-049 live, since distributed events would
reach country-month on their own. (Approximate — deduplicated by event id rather than replaying
full survivorship; the order is right, the number is provisional.)

A correction the legacy notebook forced

I had filed ceil_split's 0.91% inflation as an ADR-040 Invariant 1 violation to fix. It is
faithful parity
: the legacy code applies np.ceil to best, high and low alike. Fixing it
is a parity break, not a bug fix, and belongs behind the same profile switch as everything else.
The legacy loader also truncates summary events spreading past the ingestion month — a second
inherited leak, pulling the other way.

Two more legacy findings that strengthen the design:

The parity freeze is not the obstacle it looked like

ADR-049 §6 already established that parity is "achievable via configuration". So
production_parity stays frozen and byte-identical while a separate recommended profile carries the
changes, and the question becomes which profile the pipeline invokes — one line, reversible,
auditable — rather than whether to mutate a frozen contract.

Library: survey and acquisition list

840 papers, 3,807 claims. Strong on evaluation under domination by a few observations (31
scoring-rule papers incl. Lerch2017 on the forecaster's dilemma, Gneiting2011's threshold
decomposition, Bolin2023 SCRPS), point processes (22, incl. Brehmer2023 on scoring a point
process's intensity measure), and UCDP measurement error (Vesco2026: underreporting is non-linear
in event size, so the large aggregates are the most accurately-counted values we hold — which is
why option O4, dropping, should be rejected).

Verified absent, checked against all 840 titles rather than inferred from a failed search:
change-of-support/MAUP (0), temporal disaggregation/benchmarking (0), areal interpolation
(~0), coarsened-at-random/partial identification (3, all Tobit — a different problem).

09_library_acquisitions.md lists 19 items in 3 tiers with verified citations; three of the seven
Tier 1 items are open access. Notable: Tobler (1979) on pycnophylactic (mass-preserving)
interpolation is ADR-040 Invariant 1 as an existing method, and the R packages pycno and
TSdisaggregation can serve as oracles for the evaluation harness.

Caveats carried in the documents

  1. Figures rest on a local viewpoint build dated 2026-06-01, data through 2026-04; re-measure
    against the deployed build before citing as current.
  2. The coarsen-and-recover design has two declared confounds: the proxy population, and — from
    Weidmann (2015) — that UCDP-GED locational accuracy degrades sharply below district scale,
    so our "ground truth" carries its own error at ~55 km cells and the oracle is not a true ceiling.
  3. No causal claim about any model's performance.

Verification

Full suite: 2,433 passed · 16 skipped · 17 xfailed · 0 failed · 0 errors.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EfeuULbPTqrx4qtNki5dy9

Polichinel and others added 2 commits November 4, 2026 09:01
Scaffolds the research program for how UCDP aggregate events are
allocated in space and time, following the catalog in
reports/investigation_summary_event_concentration/.

Ten documents: the README spine, an 840-paper library survey with a
19-item acquisition list, the option space, the harness audit, a gated
roadmap, a deliberately-empty analysis plan, a glossary, an append-only
experiment log, and a study of the legacy views3 GedLoader.

The harness audit found ~85% of the standing harness already in place:
an OCP strategy registry so a new allocation strategy is registered
rather than patched in, profiles as the default-off flag mechanism, the
byte-identical fixture gates, a synthetic known-answer path for cheap
readout, and sixteen post-mortems. The one real gap is that nothing in
this repo evaluates allocation QUALITY — no locked baseline, no metric
protocol, no ground truth — because allocation was never treated as a
choice. That is the build, and it is constructible: 343,865 events are
well-located and well-dated, carrying 68.6% of all mass, so a
coarsen-and-recover test has a known answer and needs no new data.

Three divergences found, all governance rather than research, and all
routed to P0 rather than resolved here:

ADR-049 was accepted 2026-06-28 and is not live. It decided spatial
distribution "on by default", named cell 148759 and event 463131, and
set a golden test that cell 148759 should drop from 273,076. The profile
uses passthrough and the cell now holds 274,358 — it grew. Nothing is
red because no test asserts it, which is C-365's lesson recurring.

ADR-049's own worked example misdescribes the code. It states event
463131 "is spread across 12 months by temporal distribution (correct)".
It produces exactly one row; the annual->date_end_only override catches
it. So the spatial half is decided but dormant, and the temporal half is
undecided and was believed decided.

production_parity does not reproduce the legacy country-month path. The
legacy loader filtered where_prec 4/6 out of priogrid-month but read the
unfiltered table for country-month, so those deaths were present at
country level. Our country-month derives from the grid, after the
viewpoint filter, so they are in neither — approximately 48,124 events
and 864,869 fatalities, 19.16% of all mass. Structural, not
configurable, and an argument for making ADR-049 live, since distributed
events would reach country-month on their own.

The legacy notebook also corrects something I had filed as a bug.
ceil_split's 0.91% inflation is faithful parity: the legacy code applies
np.ceil to best, high and low alike. Fixing it is a parity break, not a
bug fix, and belongs behind the same profile switch as everything else.
The legacy loader also computed an is_summary flag and dropped it at the
aggregation boundary, and named every column _nokgi — "no Known
Geographical Imprecision" — so views-hydranet's request for a precision
flag is a restoration of a legacy concept, not a new demand.

The parity freeze is not the obstacle it looked like. ADR-049 section 6
already established that parity is achievable via configuration, so
production_parity can stay frozen and byte-identical while a separate
recommended profile carries the changes. The question becomes which
profile the pipeline invokes.

No code, configuration or data changed. Weighted scoring rules and
intensity-measure targets are recorded as comparators and explicitly
out of scope, per the boundary ADR-049's Deferred section already drew.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EfeuULbPTqrx4qtNki5dy9
Simon added all seven Tier 1 acquisitions plus Croicu2026, which was
not on my list and is the most important of the eight. Library is now
848 papers. Four of them changed the design, so this records what did
not survive the reading.

Croicu2026 is our problem by its own name, from inside UCDP/ViEWS.
Multiple Spatial Imputation fits actor-dyad-monthly Gaussian Processes
over the cloud of precisely located events in a 24-month window, then
samples m locations per KGI event inside its administrative polygon,
emitting m complete datasets. Measured: RMSLE down 34.92% against list
deletion and 36.30% against centroid imputation, winning 100% of 2,400
simulations, with mean point error 519km to 275km. Even m=1 beats
centroid everywhere, and gains plateau past m=20. Actor-dyad
decomposition is both essential to the method and what makes the GP
tractable, about 27,000x cheaper than one global model.

That contradicts two of ADR-049's deferrals. It deferred
actor-informed distribution partly as premature; Croicu makes it
load-bearing. It placed sampling treatments downstream; Croicu treats
MSI as data preparation consuming this layer's own input. Added as
option O7 with the boundary question raised as decision D5 rather than
answered here, since it reaches beyond this repo.

Heitjan1991 changes the argument against the status quo from degree to
kind. Under coarsening at random the mechanism may be ignored; when
data are not CAR, ignoring it "leads to severely biased inference".
Croicu measures that KGI is non-random, correlating with remoteness,
reporting quality and common predictors of violence. So passthrough and
date_end_only ignore a mechanism that provably cannot be ignored, and
O0 is a specification error rather than a conservative default. What
stays open is which remedy, not whether one is needed.

Denton1971 rejects our intended O2 by name. The identity-matrix penalty
distributes each year's discrepancy equally among sub-annual periods,
which is even_split, and Denton calls that specification "inadequate
for practical use" because it introduces spurious year-boundary
discontinuities. O2 is now the first-difference penalty, with Chow-Lin
as the covariate-informed variant where an indicator series exists.
ChowLin1971 also notes that temporal additivity emerges from best
linear unbiasedness rather than being imposed, so a conserving
allocation need not be a constrained one.

Weidmann2015 both validates and bounds the exercise. Spatial error is
of the order of an administrative district; 80% of matched events fall
within 50km, 63.4% in the correct district. The paper recommends not
operating below 50x50km cells, and our cells are 0.5 degrees. So the
grid choice is vindicated and allocation precision is capped at about
one cell. It also reverses a premise: counts are substantially more
accurate than locations, and remoteness degrades location without
degrading casualty accuracy. With Vesco2026 that gives a second and
more direct argument against dropping, since dropping discards the
reliable number because the coordinate is unreliable.

Gotway2002 supplies the map. Ecological inference, MAUP, block kriging,
pycnophylactic interpolation and spatial misalignment are all special
cases of the change of support problem, so ours is not sui generis.
ADR-049's proportional weighting is the bottom rung of a known overlay
ladder, with Tobler1979's mass-preserving surface as the next rung,
added as O1b. Tobler's Dirichlet boundary condition at water maps onto
our existing land mask, and his non-negativity clipping is the same
problem any fatality allocation faces. Gotway also states the mandate
for our harness directly: the assumptions made when combining
incompatible spatial data often surreptitiously determine cross-scale
relationships, and diagnostic tools to assess them are needed.

Two numbers now disagree and neither should be cited until they do not.
Croicu reports KGI at 31.1% of 293,634 GED observations; we measure
where_prec >= 4 at 15.5% of 440,461. Different denominators and
possibly different definitions. Registered as decision D6 in P0.

Also recorded: Croicu's effect sizes are not our expected gain, because
his comparators are centroid imputation and list deletion, not ADR-049's
proportional weighting. Quoting 34.92% as ours would be the
borrowed-number error this dossier exists to avoid.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EfeuULbPTqrx4qtNki5dy9
@Polichinel
Polichinel merged commit 7f28406 into development Nov 4, 2026
6 checks passed
@Polichinel
Polichinel deleted the docs/533-summary-event-allocation-dossier branch November 4, 2026 09:04
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