DRAFT · Updated 20 October 2026

Methodology

How the GINC Simulation Engine is built and run. Its parts, the data each reads and writes, the game model a run plays, the harness that plays it thousands of times, and the improvement loop that makes the model less wrong cycle by cycle.

The engine at a glance

Its components, the data that moves between them, and the improvement loop that closes the circle.

The Simulation Engine's componentsInputs (the framework, the 2026 ratings, the scenario library, the dependency graph and the parameter seed) feed the parameter tables and starting states, which the world loader compiles for one nation. The runner plays experiments through the policies, the scenarios and the engine, and writes the run record. DuckDB and the query pack read the record; the checks, summaries and narrator analyse it, and the site export publishes it. The diagnosis, the AI reviewer and the master recommendation turn the analysis into one package of changes, which a person approves; the approved changes update the parameter seed for the next cycle, and the ledger records it all.InputsWorldHarnessRecord and analysisImprovement loopCapability framework73 groups, 9 domainsIndex ratings 2026197 nations, 0 to 20Scenario library10 shocks, 10 upsidesDependency graphedges, elasticities, lagsParameter seedparams_v0_1.yamlParameter tablesParquet, versionedStarting statesscore, depth, modeWorld loaderone nation, compiledExperimentsYAML batchesRunnerseeds, probes, poolPolicieschoose(state)Scenariosdraw(state)Enginestep(): one yearRun record8 Parquet tablesDuckDB and queriesviews, query packChecks, summariespass, warn, failNarratorreport.md eachSite export/runs/ pagesDiagnosisfindings by layerAI reviewerjudges every layerMaster recommendationone package to runHuman gateapprovals.yamlLedger and historyloop/, ParquetAn approved master recommendation changes the parameter seed for the next cycle
The run path goes left to right; the improvement loop closes it through the human gate (dashed).

What the engine is

The Simulation Engine turns the National Capability Index from a static position into a dynamic one. A nation is a player. It starts from its 2026 ratings across the 73 capability groups of the National Capability Framework, earns credits each year and decides where to invest them. The world answers through the Capability Dependency Graph, decay, shocks and upside events.

The engine has two layers, kept apart on purpose:

  • The game model: a formal, parameterised description of one year of a nation’s capability. It is the intellectual asset, and the thing to be defended and refined with the expert network.
  • The harness: Python that plays the game without people, thousands of times, under controlled experiments. It writes a complete record of every run, checks it and reports on it. The improvement loop reads that record and proposes how the model should change.

Nothing the engine produces is a forecast. Every output is a distribution over runs of a model whose parameters are, in this version, structural judgements. The outputs serve to compare policies, to test the model and to find where it is wrong.

The parts

The diagram above shows the components and how data moves between them. In words, a run goes from left to right:

Part What it does Where it is
Inputs The framework’s 73 groups, the Index’s 2026 ratings for 197 nations, the scenario library, the dependency graph and the parameter seed. data/reference/, data/capability/, data/scenarios/, data/graph/, data/sim/seed/
Parameter tables The seed compiled to versioned Parquet tables, with confidence and provenance on every row. data/sim/*.parquet, built by pipeline/sim/world/tables.py
World loader Reads the tables once and compiles one nation’s world to arrays: costs, decay, vulnerability, credits, its exposures and the graph’s lag kernels. pipeline/sim/world/loader.py
Engine One pure function, step, that advances a state by one year given the moves and the events. No files, no randomness of its own. pipeline/sim/engine/
Policies Who plays the nation: hold, random, greedy marginal, mirror a peer, scripted, and replay for the probes. All share one interface, so a solver can be dropped in. pipeline/sim/policies/
Scenarios What the world does: no events, the stochastic library, scripted events, or a single stress event. pipeline/sim/scenarios/
Experiments and runner YAML experiments in batch directories. The runner plays each across its seeds, runs the counterfactual probes and writes the run record, one process per experiment. pipeline/sim/experiments/
Run record Eight Parquet tables per experiment, nothing summarised away, with the experiment’s lineage and a frozen copy of every table it used. results/<batch>/<experiment>/
DuckDB and the query pack Views over every run and over the loop’s history, and the named SQL queries that every report, check and page uses. loop/sim.duckdb, pipeline/sim/report/queries/
Checks and summaries The automated checks (invariants, plausibility, saturation, degeneracy, dead parameters, calibration, regression, performance) and the experiment and batch summaries. pipeline/sim/report/
Narrator A report for every experiment, written from the queries, every figure citing its query. pipeline/sim/loop/narrate.py
Diagnosis Turns the checks into findings at five layers, each with evidence, a hypothesis, a proposed change, a verification and a risk. pipeline/sim/loop/diagnose.py
AI reviewer A language model reads the batch and judges every layer: parameters too high or too low, mis-shaped mechanics, and the methodology itself. pipeline/sim/loop/ai.py
Master recommendation One package of changes to run next, chosen and ordered from every recommendation of the cycle. pipeline/sim/loop/master.py
Human gate A person approves, edits or rejects the master recommendation. Nothing changes until they do. loop/approvals.yaml
Ledger and history The running state of the loop, and every finding, recommendation, check, summary and narrative of every cycle, as Parquet. loop/
Site export The JSON the runs and improvement loop pages are built from. src/data/sim/

The data

Inputs, owned elsewhere

  • The framework: 73 capability groups in nine domains and three dimensions, synced from nationalcapability.org. Each group’s position in every state vector is fixed by its node_index.
  • The 2026 ratings: every nation’s score on every group, 0 to 20, from the National Capability Index. These are the starting scores. They are inputs, set by the Index’s methodology, and the engine never tunes them.
  • The scenario library: ten downside and ten upside scenarios. Each has a loading by domain, a likelihood band, a duration and the buffer or required groups the scenario methodology sets out. The scenario tests’ own reading of the loadings becomes the shock and upside libraries.
  • The Capability Dependency Graph: the edges between groups, each with a weight, an elasticity bracket and a lag. It is reviewed and versioned separately (Capability graph).

The parameter tables

Every number in the model lives in a table, never in code, so that the loop can change a value without touching the engine and a reviewer can see what changed. The tables are built from one reviewable seed, data/sim/seed/params_v0_1.yaml. The seed sets values by domain class, then overrides them group by group, each with a rationale.

Table One row per Columns
cost_params group and mode base cost per level, convexity, delivery years, whether the mode is open, confidence, provenance, defensible, rationale
decay_params group decay when built, bought and partnered; maintenance per level; confidence; provenance; note
vulnerability_params group base vulnerability, depth sensitivity, mode sensitivity
model_params scalar the frontier, the frontier premium, maintenance share, upstream discount, depth gain and erosion, propagation floor, carry decay, the credit function’s coefficients, chunk sizes, moves per round, shock and upside magnitudes, absorption, credit effects
credit_params nation or global coefficient each nation’s base credits, and the global coefficients of the credit function
shock_library, upside_library event groups hit and their load points, magnitude range, duration, yearly probability, credit effect, buffer or required groups
shock_exposure event and nation the nation’s exposure, from the scenario tests
nation_states nation, year and group score, depth, mode shares, provenance
parameter_versions version what changed, when, and the finding and evidence behind it

Confidence is low or medium throughout version 0.1. Five groups carry defensible = false, because no cost, decay or vulnerability can be argued for them in a single-nation model: Nuclear Deterrence, Early Childhood Development, Diaspora & Global Communities, Monetary & Currency Capability, and Economic Leverage & Dependence. These are questions for the framework review, not values to tune.

The run record

Every experiment writes these tables to results/<batch>/<experiment>/. Each row carries its batch and experiment, so DuckDB reads every run of every batch as one table.

Table One row per What it holds
trajectory run, year, group score, depth, mode shares, and what moved it: levels invested, decay, events, propagation
decisions run, year, move the action, group, chunk, credits, levels, whether it was paid for, the policy’s reason, its value estimate and its three best alternatives
events run, year, event the event, its type, the magnitude drawn, duration, credit effect, and the damage to each group after scaling
propagation run, year, edge points the edge delivered this year and points it still owes: the ledger that traces a change through the graph
credits run, year income and each credit group’s share of it, maintenance needed and paid, spending by mode, carried in and out, lost to shocks
tiers run, year each domain’s tier, the Tier 1 count, the rank and the tier rank
objectives probe, run, year every objective along the path, for the base run and every probe
run_summary probe, run the horizon, concentration of spending, saturation and the largest move without a shock

Beside them sit manifest.parquet (the versions of the model, graph, parameters and shock library, the policy, nation, horizon, seeds, timestamps, git commit and run time), config.yaml, and params/, a frozen copy of every table the experiment ran on. Any run can be reproduced without the live tables.

The game model

One round is one year. The state of a nation is its score on each group (0 to 20), its depth on each (0 to 1, how institutionalised the capability is), its mode shares on each (how much was built, bought or partnered), its credits, and three queues: investments still to arrive, propagating changes still travelling the graph, and events still landing. A round runs in a fixed order.

1. Income

Credits come in at base_credits × (E / E₀)^η × (1 + event credit effects). Here E is a weighted mean of four groups: Fiscal Capacity & Public Finance (0.4), Economic Output & Productivity (0.3), Financial System Depth & Access (0.2) and Investment Attraction & Projection (0.1). E₀ is its 2026 value.

A nation’s base credits are calibrated so that holding its 2026 position costs a share of its income, leaving headroom that rises with its fiscal capacity. This is a structural calibration. The brief asks for fiscal capacity as a share of GDP, which is not yet in the repository.

2. Maintenance

Every level held costs credits each year, more steeply near the frontier. Maintenance is paid first. Whatever share goes unpaid is the share of every group that decays.

3. Moves

A policy makes up to five moves a year. Each is a chunk of income (1, 2 or 5 per cent) spent to build, buy or partner in one group:

  • Build is slow, raises depth and is cheaper where the group’s upstream dependencies are strong.
  • Buy is fast and leaves the capability more exposed to shocks.
  • Partner sits between.

A level costs base cost × convex(score) × upstream discount. Here convex(s) = 1 + c (s/20)² + premium × max(0, s − 17)², so each level is dearer than the last and steeply so at the frontier. A move is priced at the midpoint of the levels it buys, and arrives over one to six years depending on its mode.

4. Decay

The unmaintained share of each group decays at a rate set by how the capability was acquired. Institutions decay slowly, technology quickly and infrastructure in between, and bought capability decays faster than built.

5. Events

Each shock and upside event in the library may begin each year at its yearly probability, taken from the scenario’s likelihood over ten years. An event cannot begin again while it is still running.

A shock’s damage to a group is its load points (3, 1.5 or 0.75 for a high, medium or low load) times:

  • a magnitude drawn for the run,
  • the nation’s exposure,
  • one minus the share its buffer groups absorb, as in the scenario load test,
  • the group’s vulnerability, which falls with depth and rises with the bought share.

The damage is spread over the event’s duration, and a shock that loads the financial domain also cuts income. Upside events are smaller and rarer. They scale with the nation’s readiness on the groups they require, so a breakthrough is likelier where the groundwork is strong.

6. Propagation

Changes from moves and events (not decay, which every group suffers on its own) travel the dependency graph. Each edge passes on its weight times its elasticity times the source’s relative change, spread over the following years by its lag. Elasticities are drawn within their brackets for each run. Scores are clipped to 0 to 20, and a clipped move is not passed on.

7. Objectives

Four objectives are reported, never one:

  • the score sum at the horizon;
  • the tier rank: the Index’s own Pareto tiering within each domain (tolerance a quarter of a point), ordered lexicographically across domains;
  • the resilient score: the mean score sum across runs, so under the distribution of shocks;
  • a goal-weighted score for pathway questions.

The capability index (the mean of the dimensions, each the mean of its domains, each the mean of its groups) and the rank on it are reported beside them.

Phase one plays one nation at a time. Every other nation stays at its 2026 rating, so rank and tiers are measured against a still field.

Policies and scenarios

A policy implements one function, choose(state, params, rng), and returns moves, each with a reason code, a value estimate and its alternatives where it has them:

  • Hold makes no moves.
  • Random draws five.
  • Greedy marginal ranks every open group and mode by value per credit. The value is the level itself plus its long-run effect downstream through the graph, less the maintenance it will need, and only what arrives before the horizon counts. It is the bar any solver must beat.
  • Mirror a peer closes the largest gaps to the nation five places above it.
  • Scripted plays a fixed plan, such as a reconstructed historical policy.
  • Replay plays back a recorded run, for the probes.

A scenario implements draw(state, rng). The stochastic scenario draws a random number for every event every year, so that runs stay aligned across policies.

The harness

Experiments and seeds

An experiment names a nation, a horizon, a policy, a scenario, a number of seeds and how elasticities are drawn. A batch is a directory of experiments; an entry may expand over a list of nations and policies.

Each run’s randomness comes from its seed, split three ways: one stream for the graph’s elasticities, one for the policy, one for the scenario. Two policies run with the same seeds therefore face the same shocks (common random numbers). The same inputs always give the same outputs, which the determinism check tests every cycle.

Counterfactual probes

To say what a decision or a shock was worth, every experiment reruns its first twenty seeds with one thing changed:

  • no shocks;
  • the hold policy;
  • each of the five largest moves removed in turn;
  • each of the three largest shocks removed in turn;
  • the recorded moves replayed unchanged, which must reproduce the run exactly.

The removals replay the remaining moves open-loop, so the difference is the item’s worth with everything else held.

Speed

One step of the engine takes well under a millisecond (about 70 to 130 microseconds with five moves). A thousand runs of thirty years under the greedy policy take a few seconds on one core. A full improvement cycle (25 experiments, 2,000 runs, 3,000 probe runs, the checks, the reports and a sensitivity sweep) takes under three minutes.

Checks, reports and queries

Every batch is checked before anything is written about it. The checks are written to checks.parquet as pass, warn or fail:

Category Checks
Invariants credits conserved in every round; scores within 0 to 20; no missing values; the replay probe reproduces the run; the propagation ledger sums to the propagated change
Plausibility no group moves more than 3 points in a year without a shock; no domain gains more than two tiers in five years; no nation outside the top 10 reaches Tier 1 in all nine domains in 20 years; holding does not drop a frontier nation below Tier 3
Saturation groups driven to 0 or 20 and held there in more than a quarter of runs
Degeneracy spending concentrated in one group; the same move in most rounds; a single-group strategy as the best policy; greedy best on every objective
Dead parameters a parameter whose change of 20 per cent either way moves no objective by more than 0.5 per cent (a scheduled sweep)
Calibration backcast error by nation and domain, calibration and hold-out apart
Regression and performance a median objective that moved more than 5 per cent with no version change; step time against the last batch

Each experiment’s report (report.md) is written from the query pack. It covers the headline, the starting position, what was decided, what the world did, where it ended, why (from the probes), surprises and a comparison with the hold experiment. Every figure in it names its query: trajectory_by_domain, decision_sequence, spend_by_period, close_calls, top_edges, shock_damage, shock_damage_by_group, tier_transitions, objective_distributions, probe_attribution, outlier_runs, baseline_comparison, backcast_comparison and engine_evolution.

The improvement loop

The model will be wrong, and the loop is how it becomes less wrong. It is recursive, not autonomous. Each cycle runs in six steps:

  1. Run the cycle batch: the null run and the baselines for five nations under four policies, always with the same seeds, so a difference between cycles is the model’s and not luck’s.
  2. Record everything above.
  3. Narrate each experiment.
  4. Diagnose. The checks become findings. Each states the evidence, a hypothesis, a proposed change, the expected effect, how to verify it and the risk.
  5. Recommend. Every finding becomes a recommendation at one of five layers, and an AI reviewer adds its own after reading the batch:
    • Parameter: a number in the tables is too high or too low. For example, a decay rate is too high, the credit function too low, the shocks too weak.
    • Mechanic: a function of the model is mis-shaped, such as how decay, credits, shocks, upsides or propagation work.
    • Methodology: the approach itself. For example, whether the Pareto tiering adds anything over the capability index as an objective, whether the policies and experiments are the right ones, and whether the loop’s own sequence is right.
    • Harness and data: the code, checks, experiments and data the harness needs, such as the reconstructed historical states that backcasting requires.
    • Framework: a group or measure the model cannot represent sensibly. It is flagged for the framework review and never tuned.
  6. Master and gate. A master recommendation chooses among all of these and orders them into one package to run next: the changes, the experiment that tests them, the criteria for accepting them, and what is deferred and why. A person reviews it and approves, edits or rejects it in loop/approvals.yaml. The next cycle applies an approved master’s parameter changes as one package, with one version bump recorded against the recommendations it drew on. Changes to mechanics, methodology and the harness go to an agent and a pull request.

The 2026 starting scores are fixed inputs throughout: no recommendation may change them, and none may tune to the hold-out nations.

Everything each cycle produces is kept in loop/ as Parquet: the cycles, findings, recommendations, master recommendations, checks, experiment summaries, narratives, AI insights and parameter judgements. DuckDB reads it beside every run, so the evolution of the engine can be queried as readily as a single run. loop/ledger.md is the running state: read first at the start of a cycle and written last.

The loop stops for a human review when the hold-out backcast error stops improving for three cycles, or when calibration improves while the hold-out worsens. Neither can be judged until historical starting states exist.

How it all runs together

From the repository root, with a Python that has NumPy, pandas, PyArrow, DuckDB and PyYAML:

Command What it does
python -m pipeline.sim params Builds the parameter tables from the seed and reports their counts and the groups without a defensible value.
python -m pipeline.sim sanity The null run against its closed form, the spectral radius, a single edge against its closed form, credit conservation and determinism.
python -m pipeline.sim bench Times a step and a thousand runs.
python -m pipeline.sim run <batch> Runs a batch directory and writes its record.
python -m pipeline.sim report <batch> Checks, summaries and reports for a batch.
python -m pipeline.sim cycle --sensitivity One improvement cycle: apply the approved master recommendation, run, check, narrate, diagnose, review and write the next master recommendation. The AI steps run when DEEPSEEK_API_KEY is set.
python -m pipeline.sim query <name> <batch> <experiment> Runs one query of the pack.
python -m pipeline.sim export Writes the site’s JSON from the results and the loop’s history.

The site is then built as usual (npm run build). Results under results/ are not committed: they are rebuilt exactly from the experiments and seeds. The loop’s history, the parameter tables and the site’s export are committed.

Limitations

  • Structural parameters. Every value is a judgement by domain class, not an estimate. The loop exists to test them, and the expert network to replace them.
  • No backcast yet. Historical starting states for the calibration nations (South Korea, Estonia, the UAE, Singapore and Ireland) and the hold-outs (Chile and Poland) must be reconstructed before the model can be scored against history.
  • One nation at a time. Other nations are frozen at 2026, so the tier rank of a rising nation is measured against a still field.
  • Calibrated credits. Income is calibrated to the cost of a nation’s position, not to its fiscal capacity as a share of GDP. This favours nations that already hold a strong position.
  • Defaults for depth and mode. Neither has data behind it in 2026.
  • A single global graph. The graph has no nation-specific edges, and its own version changes as it is reviewed. Every run records the graph version it used.