War Atlas · Method series · Paper 02
A diagnostic of the acknowledgement field across eighteen proxy conflicts, and of the count already published over it
Abstract. A proxy conflict is a war in which an outside state arms, funds or directs one of the combatants without being the principal belligerent itself. One spreadsheet on this machine records eighteen such conflicts, and gives each one a single cell saying whether the outside sponsor admitted what it was doing. This paper measures that column and nothing else. Across eighteen rows the column holds eight different forms of words: avowed and denied_then_admitted cover six rows each, and the remaining six rows are each phrased in a way used nowhere else in the column. A control settles what that means. The column immediately to its left, written by the same compiler in the same file, divides the same eighteen rows into five clean terms with no drift at all — so the spread is a property of the idea being recorded, not of a careless hand. The workbook already publishes a three-way breakdown over the loose column; that breakdown counts one row twice, misses another entirely, and still sums to eighteen because the two errors cancel exactly. The paper's claim is that this column, as it stands, cannot carry a count. It makes no claim whatever about how often sponsors in history admitted sponsorship, and the distribution reported below is a distribution of strings in a spreadsheet, not of behaviour in the world.
Datasets of armed conflict are plentiful and they agree on roughly what they record: dates, belligerents, battle deaths, sometimes a conflict type. Almost none of them carry a field for whether an external sponsor admitted the sponsorship. That absence is not an oversight so much as a difficulty, because admission is not a fact about a war — it is a fact about a state's public posture, and public postures change, differ between the sponsors in one war, and are argued about for decades afterwards.
The workbook examined here does carry such a field. That is what makes it worth a paper: an original column is worth more scrutiny than a duplicated one, because there is no second dataset to check it against. It also means the first honest question is not what does the column say but can this column hold a count at all. A column can be full, tidy-looking, and still incapable of being tallied, and the way that failure normally surfaces is that someone tallies it anyway.
The strongest argument against this paper should be stated before any number appears, because it is a good argument and it is not fully answerable. It runs: you have diagnosed the wrong defect. You are counting strings, and the problem is not the strings — it is the grain. The grain, or unit of analysis, is the thing one row stands for. Here one row stands for a conflict, but sponsorship is done by sponsors, and a conflict can have four of them pursuing different aims and admitting at different times. On that reading the eight formulations are not vocabulary drift at all; they are the visible pressure of one cell being asked to describe two sponsors at once. If that is right, then imposing a tidy vocabulary at the conflict grain would produce a clean, countable and permanently wrong column — a worse outcome than the untidy one, because tidy columns stop attracting scrutiny. That criticism is taken up in §5, where it is conceded rather than rebutted.
The acknowledgement column of proxy_conflicts is not, in its present state, fit to carry a count: any tally over it requires a bucketing rule that is not written down in the workbook, and the tally the workbook already publishes mis-assigns rows.
Class 1 — corpus diagnostic. The object of study is a column in a named file, not the past. Every quantity below is a count of cells, recomputable in one line. It is explicitly not class 2, because no comparison is made between named historical cases; and explicitly not class 3, because eighteen conflicts gathered with no admission rule — a written statement, fixed in advance, of what qualifies a case for inclusion — cannot support any claim about proxy conflicts as a population. Class 1 is not a lesser result here. It is the only class the evidence permits, and a diagnostic of a corpus is entitled to take that corpus as given, because the corpus is the specimen.
The column is declared unfit to carry a count if either condition holds.
The column is fit if neither fires. One further test governs the interpretation, and it is a positive control — a case where the instrument is known in advance what it should say. If a comparison column, written by the same compiler in the same sheet, also fires R1, then the finding is about the compiler's habits and the paper is worthless as a statement about the concept. Only if the control column passes cleanly can the spread be attributed to the idea being recorded.
Both acknowledgement and the control column form are nominal: their values are names with no order and no spacing between them, so counting members of a category is the only arithmetic licensed. No mean, no median, no rank. This is enforced structurally elsewhere in the estate — the measure table in ops/truth.db declares scale_type against a closed list and refuses a mean over an ordinal by CHECK constraint — and it is stated here so that no reader treats the six-and-six below as a midpoint of anything. began and ended are years, and are interval-scaled; their difference is reported as a span in years and nothing further is done with it.
The order of work was not ideal and is recorded rather than hidden. The distribution of the column was measured on 2026-09-09 and logged as entry P5 in ops/domain_logic.py before the decision rule above was written down. The rule was written to be decidable by someone who had not seen the data, but it was not written first. This is carried as threat 1 in §6.
Two admission rules are in play, and keeping them apart is what allows this paper to exist at all.
The unit of analysis here is a cell, not a war.
acknowledgement, of the sheet proxy_conflicts in propaganda-and-proxy-states.xlsx.conflict_key in column A.Eighteen cells satisfy the rule. Eighteen are admitted. Nothing was dropped, and there is no discretion in the criteria, which is the whole reason a class 1 diagnostic can be stated at full confidence when the corpus underneath it cannot.
There is no written statement anywhere on this machine of why these eighteen conflicts and not others. The set is visibly bounded by the Cold War — every onset falls between 1946 and 1979, with eight of the eighteen beginning in the 1970s alone — and conflicts of exactly the same form after 1990 are simply absent. The atlas database keeps a table of admission rules, nine of them, and none covers this set. Its own legacy entry describes precisely this condition for a different body of rows: “Admitted by no stated rule… measured the frame after the fact and ruled the set a convenience sample, permanently.” The word that matters there is permanently: a frame measured after the data were collected caps every claim drawn from it at description, and no amount of extra data lifts the cap.
The honest sentence is that the eighteen were assembled by interest. That bounds what a successor paper may claim — no class 3 population claim is available until an admission rule is written — and it does not bound this one, because this paper's claim is about the column, and the column is fully present.
Every quantity used anywhere in this paper appears in Table 1 with the operation that produced it and a confidence. Certain (structural) means the number is a count of cells in a file that anyone can reopen; it is not a claim about history and cannot be wrong unless the file changes. Where the same file carries two different numbers for one quantity, a band is published rather than a point.
| Quantity | Value | Basis | Confidence |
|---|---|---|---|
Data rows in proxy_conflicts | 18 | Rows with a non-empty conflict_key, read directly | certain (structural) |
| Columns in the sheet | 7 | Header row read directly | certain (structural) |
Empty acknowledgement cells | 0 of 18 | Null and whitespace-only test over the admitted cells | certain (structural) |
| Distinct values, exactly as typed | 8 | Frequency count over raw cell values | certain (structural) |
| Distinct values after trimming spaces and lowercasing | 8 | Same count over normalised values. This is the negative-space check: it asks whether near-duplicates exist that would explain the spread away, and finds none | certain (structural) |
Rows reading denied_then_admitted | 6 | Exact string match | certain (structural) |
Rows reading avowed, exactly and alone | 6 | Exact string match | certain (structural) |
| Rows whose value occurs once in the column | 6 | Values of frequency 1, counted | certain (structural) |
| “Avowed”, as the file itself counts it | 6–8 | Band, not a point. Lower bound: 6 rows match the string exactly. Upper bound: 8 rows match the prefix pattern the workbook's own summary uses. One file, one quantity, two published figures | certain at both endpoints |
Distinct values in the control column form | 5 | Frequency count over raw cell values | certain (structural) |
Block sizes of form over the same 18 rows | 6, 4, 3, 3, 2 | Frequency count; the five blocks are disjoint and exhaust the eighteen rows | certain (structural) |
Breakdown published on the summary sheet | 8 / 6 / 4 | Cached values at summary!B16:B18, read with formulas resolved | certain (structural) |
| Cells counted exactly once by those three formulas | 16 of 18 | Spreadsheet wildcard semantics reimplemented in Python and run per row; the reimplementation reproduces the workbook's three cached totals exactly, 3 of 3 | high (reconstruction, validated against cached totals; not executed in Excel) |
| Cells counted twice | 1 — congo-crisis | As above | high (reconstruction) |
| Cells counted zero times | 1 — mozambique | As above | high (reconstruction) |
| Onset years spanned | 1946–1979 | Minimum and maximum of began | certain (structural) |
| End years spanned | 1949–2002 | Minimum and maximum of ended | certain (structural) |
| Onsets by decade | 1 / 6 / 3 / 8 | 1940s, 1950s, 1960s, 1970s, from began | certain (structural) |
Span from began to ended | 0–27 years | Arithmetic on two stated year columns. It is the span the file asserts, not a measured duration of fighting | certain (structural) |
Rows whose parties_and_sponsors text contains a semicolon | 10 of 18 | Substring test. A structural proxy for “two sponsored sides are named”, not a semantic count of sponsors | moderate — must be quoted as a semicolon count, never as a sponsor count |
| Rows carrying a date of admission | 0 — no such column | Column list of the sheet | certain (absence) |
Rows carrying an evidential_status | 0 — no such column | Column list. The sibling sheet propaganda_campaigns does carry the field, 16 of 16 populated | certain (absence) |
| Rows carrying a named source, region or theatre | 0 — no such columns | Column list | certain (absence) |
| Admission rules in the atlas covering these 18 conflicts | 0 of 9 | select rule_id from admission_rule in war-atlas/db/atlas.db, read-only | certain (absence) |
| Total rows across the workbook's seven data sheets | 111 | Live COUNTA formulas on the summary sheet, cached values | certain (structural) |
form insurgency_sponsorship 6 · covert_regime_change 4 · direct_intervention 3 · client_conventional 3 · bloc_suppression 2
acknowledgement denied_then_admitted 6 · avowed 6 · then, one row each: “avowed on the Western side”, “partly documented, still contested”, “partly avowed”, “partly documented”, “avowed by 1980s”, “documented”
form is the positive control: five terms, five disjoint blocks, nothing left over, no qualifier fused into any term. acknowledgement is the column under test: two blocks of six, then six slivers of one. No line is fitted and no axis is drawn, because both variables are nominal — categories with no order — so there is nothing for a line to run along. A class 1 diagnostic would forbid a fitted line in any case.| conflict_key | years | form | acknowledgement, verbatim | counted |
|---|---|---|---|---|
| greece-civil | 1946–1949 | insurgency_sponsorship | avowed on the Western side | 1 |
| korea | 1950–1953 | direct_intervention | denied_then_admitted | 1 |
| iran-coup | 1953–1953 | covert_regime_change | denied_then_admitted | 1 |
| guatemala-1954 | 1954–1954 | covert_regime_change | denied_then_admitted | 1 |
| congo-crisis | 1960–1965 | covert_regime_change | partly documented, still contested | 2 |
| yemen-1962 | 1962–1970 | client_conventional | avowed | 1 |
| vietnam | 1955–1975 | direct_intervention | partly avowed | 1 |
| laos-cambodia | 1959–1975 | insurgency_sponsorship | denied_then_admitted | 1 |
| chile-1973 | 1970–1973 | covert_regime_change | partly documented | 1 |
| angola-war | 1975–2002 | insurgency_sponsorship | avowed by 1980s | 1 |
| mozambique | 1977–1992 | insurgency_sponsorship | documented | 0 |
| ogaden | 1977–1978 | client_conventional | avowed | 1 |
| afghanistan-79 | 1979–1989 | direct_intervention | denied_then_admitted | 1 |
| nicaragua-contra | 1979–1990 | insurgency_sponsorship | denied_then_admitted | 1 |
| el-salvador | 1979–1992 | insurgency_sponsorship | avowed | 1 |
| chad-libya | 1978–1987 | client_conventional | avowed | 1 |
| hungary-56 | 1956–1956 | bloc_suppression | avowed | 1 |
| czechoslovakia-68 | 1968–1968 | bloc_suppression | avowed | 1 |
The workbook does not leave the column untallied. Its summary sheet publishes a three-way breakdown of acknowledgement — avowed 8, denied_then_admitted 6, contested-or-partial 4 — and those three cells are not typed numbers but live formulas. Read with the formulas rather than their results, they are:
B16 =COUNTIF(proxy_conflicts!$G$2:$G$400,"avowed*") B17 =COUNTIF(proxy_conflicts!$G$2:$G$400,"denied_then_admitted") B18 =COUNTIF(proxy_conflicts!$G$2:$G$400,"*contested*") + COUNTIF(proxy_conflicts!$G$2:$G$400,"partly*")
In a spreadsheet's matching language, * stands for any run of characters, so "avowed*" matches any value beginning with the word and "*contested*" matches any value containing it anywhere. Running those three patterns over the eighteen cells one row at a time gives Figure 2.
congo-crisis, reading “partly documented, still contested”, matches both halves of the third formula and is counted twice. mozambique, reading “documented”, begins with no matched prefix and contains no matched word, and is counted not at all. The row totals are 8, 6 and 4, and they sum to 18 — the correct number of rows — because the surplus of one and the shortfall of one cancel. No fitted line and no trend: the horizontal axis is a list of cells in file order, which carries no magnitude and no sequence to fit.The decision rule fires on both conditions, and the column is declared unfit to carry a count.
R1 fires. Three of the eight values are a base term with a qualifier fused onto it rather than terms in their own right: “avowed on the Western side”, “avowed by 1980s” and “partly avowed” are all modifications of avowed, and “partly documented, still contested” is a modification of “partly documented”. The spread is not an artefact of typing: normalising spaces and case leaves all eight distinct, so there are no near-duplicates to explain it away.
R2 fires. The tally the workbook already publishes assigns one row to two buckets and one row to none. That is the condition R2 names, and it was found by asking the question a positive tally never asks — not do the buckets sum correctly, which they do, but is every row counted exactly once, which they are not. The two errors are equal and opposite, so the total audits clean at eighteen. A check that would have caught this had to be a check on coverage, not on the sum.
The control passes. The form column, in the same sheet, by the same hand, on the same eighteen rows, holds five terms that partition the set exactly: no fused qualifiers, no one-off phrasings, nothing left over. The compiler therefore demonstrably could produce a controlled vocabulary, and did, six inches to the left. That is what promotes this from a complaint about tidiness into a statement about the concept: acknowledgement resisted the treatment that form accepted.
The tempting summary of this data is that sponsors avowed their sponsorship in a third of these conflicts and denied it before admitting it in another third. That summary is not available, and this paper does not make it. The six and the six are counts of two strings in a spreadsheet column. They are not a distribution of acknowledgement in the world, and they cannot be converted into one, because the rule for assigning a war to a bucket has never been written and the six remaining rows show exactly what happens where no rule exists: the compiler wrote a sentence instead.
Nor does the finding say the column should not exist. It is an original field — no conflict dataset in general use carries one — and originality is precisely why the vocabulary problem had to be solved from scratch and was not.
greece-civil is the row that fits worst, and it fits worst in a way that undermines this paper's own framing. Its cell reads “avowed on the Western side”. That is not a value of an acknowledgement variable at all. It is the compiler stating, in the cell, which sponsor the cell is about — a confession that one cell was asked to describe the postures of two sponsors and could only describe one of them. angola-war is the second such row, in the other direction: “avowed by 1980s” is a date of admission written into a field with no date, for a war in which four external sponsors were involved and one cell had to cover them all. vietnam's “partly avowed” is the same admission in fewer words.
The concession follows and it should be made plainly. Three of the eight formulations — the three just named — are better explained by the grain objection in §1 than by this paper's headline. They are not vocabulary drift; they are a unit-of-analysis error leaking into free text at exactly the three rows where one cell had to cover two sponsors or two dates. A hostile reader is entitled to say that the diagnosis names the symptom and not the disease, and that if someone acts on this paper by imposing a four-term vocabulary at the conflict grain, they will produce a clean, countable, permanently wrong column, and this paper will have caused it.
That is right, and it sharpens rather than replaces the result. Fixing the vocabulary without fixing the grain is the worse of the two available errors, because a tidy column stops being questioned while an untidy one keeps announcing its own defect. The registered claim is therefore stated as a refusal — the column cannot carry a count — and not as a repair.
Eighteen rows is too few to diagnose anything. The control answers this: form achieves a clean partition at the same eighteen rows in the same sheet, so the number of rows is not what separates the two columns.
This is a complaint about a spreadsheet, not a finding. The summary-sheet defect answers this. An uncontrolled column already produced a published breakdown that double-counts one row, omits another, and passes its own arithmetic check. That is a reproducible failure with the offending formulas quoted, not an aesthetic objection.
ops/domain_logic.py earlier the same day, before the decision rule in §2 was drafted. The rule was written to be decidable by a stranger, and both its conditions turn on facts a stranger can check independently, but it was not preregistered in the strict sense and the paper does not claim it was.parties_and_sponsors is free prose and enumerating it would require a judgement on every row. The only structural handle is that ten of eighteen rows contain a semicolon, which is a fact about punctuation. Eighteen is the denominator of the corpus, not of the phenomenon, and no estimate of the latter is offered.evidential_status column, so every historical characterisation in the sheet is unverified recall. This does not touch the paper's claims, which are about a column, but it means no value in Table 2 may be quoted as a historical fact about any war. The sheet's own workbook makes this worse rather than better: its read-me insists that evidential status “should never be dropped when a row is quoted elsewhere”, and its assessment rule 8 says to record it in the row — and the sheet this paper examines carries no such column.ops/domain_logic.py. A reader who opened the file alone would find nothing warning them off.Each condition is checkable by someone who has the file and none of my working.
summary formulas in which every row is counted exactly once. The R2 finding would be wrong and the double count would be mine.form also carries fused qualifiers or one-off phrasings. The finding would then be about the compiler and not about the concept, and the paper's central inference collapses.Should any of these obtain, the registered claim is retired to not_supported, with a dated record naming what replaced it rather than a quiet rewording. The reinstatement condition is a re-read of the column under an unchanged decision rule that fires R1 or R2 again.
The prior register entry P5 carries its own falsifier: two rows in one bucket whose dates of admission differ. That condition cannot be checked from this workbook, because there is no date-of-admission column and none of the six denied_then_admitted rows records when the admission happened. Supplying those six dates from memory would be inventing figures and is refused. The consequence must be stated in the strong form: a registered falsifier the data cannot evaluate has not passed. It is untested, and an untested falsifier is a claim about a claim.
This is also the largest true gap in the column. “Denied then admitted” describes a dated change of state compressed into a timeless label, so the bucket silently equates a denial admitted after two years with one admitted after forty. Six rows sit in it and it cannot be ordered.
A four-term vocabulary would cover the eight formulations: avowed from the outset; denied_then_admitted; partial, where one sponsor avowed and another did not; and contested, where the historical record itself is unsettled. The row-by-row mapping is shown below as a worked proposal only.
No count is derived from this mapping, and no total is given for it, deliberately. Applying a vocabulary invented after seeing the data is the exact defect this paper diagnoses, and publishing a tidy tally from it would be that defect committed inside the paper about it — which is, in miniature, what the summary sheet already did. The mapping is offered so that a successor can argue with it before anything is counted, and it is unregistered.
| Value as found | Proposed term | Why the assignment is arguable |
|---|---|---|
| avowed | avowed | Unambiguous only if the row has one sponsor, which is not recorded |
| denied_then_admitted | denied_then_admitted | Loses the date of the change, which no column carries |
| avowed on the Western side | partial | Names one side explicitly; the other side's posture is simply absent, not partial |
| partly avowed | partial | “Partly” may mean partly by one sponsor or partly over time |
| avowed by 1980s | denied_then_admitted | Reads as a dated transition, but the pre-transition state is not stated |
| partly documented | contested | Describes the historian's evidence, not the sponsor's posture — a different variable |
| partly documented, still contested | contested | Same category error, plus the phrase the double count came from |
| documented | — | Unassignable. “Documented” says the historian has evidence; it says nothing about whether the sponsor admitted anything. Any assignment would be a guess |
Three of the eight values describe the state of the historical record rather than the conduct of the sponsor. That is a second variable living in the same column, and it is a further reason the repair cannot be a rename. A successor should split the column before it renames anything, and split the row before it splits the column.
All eighteen rows live in ~/ops/reference/propaganda-and-proxy-states.xlsx, sheet proxy_conflicts, columns A–G. The published breakdown lives at summary!B16:B18 of the same file and must be read with formulas unresolved to see the defect; read with results only, it looks correct. The absence of an admission rule is checkable in ~/war-atlas/db/atlas.db, table admission_rule, nine rows, opened read-only. The prior measurement is entry P5 in ~/ops/domain_logic.py, and that entry links forward to this paper.
The headline distribution recomputes in one line:
python -c "import openpyxl,collections;w=openpyxl.load_workbook( r'~/ops\reference\propaganda-and-proxy-states.xlsx', data_only=True)['proxy_conflicts'];r=[x for x in w.iter_rows( values_only=True)][1:];print(len([x for x in r if x[0]]), collections.Counter(x[6] for x in r if x[0]))"
Status: counts verified; corpus unsourced — figures pending source verification. The two halves of that line mean different things and both are load-bearing. Every quantity in Table 1 is a count of cells in a named file and has been recomputed; those are not pending anything. What is pending is the corpus: not one of the eighteen rows names a source, the sheet carries no evidential-status field, and the workbook is a scaffold assembled from recall whose own read-me omits to say so. Before this paper is cited, that state must travel with it.
Two restrictions follow and they are not stylistic. First, no acknowledgement value in Table 2 may be quoted as a historical fact about any war; the table is a listing of what a file says, and the file has no sources. Second, the six-and-six may not be quoted as a distribution of acknowledgement, only as a distribution of strings. If a later document quotes either without its qualifier, the defect is in that document, and this paragraph is the record that the qualifier was attached at the point of publication.
War Atlas method series, paper 02 · dylanvir.com/war/papers · Registered 2026-09-09 · Measure class 1, corpus diagnostic · This paper makes no population claim about proxy conflicts and no historical claim about any conflict named in it. A class 3 successor requires an admission rule for the eighteen-conflict set, which does not exist; a class 2 successor requires the sponsor-conflict pair to be enumerable, which it is not.