Every equation change in LEEDS_MODEL, and whether it is stock-flow consistent

Thirty-three changes across nine named edits — what each one does, why it was made, and which of them repaired an accounting violation rather than moving a level. The body was written at twenty changes and seven edits on 2026-09-22; §1 and §2 carry dated addenda bringing it to the counts above.

Author

LEEDS_MODEL — JUST2CE

Published

September 25, 2026

Written: 2026-09-22 · Companion page: model/equations/model-equations.html

Who this is for. Jose and Marco, who do not have the repository. Nothing below requires running the model or opening a file: every claim carries its measurement in the text.

Read from: the equation change log qmd/equations/CHANGELOG.csv (20 rows when this was written; 33 rows as rendered); the equation catalogue qmd/equations/equations.csv (106 equations × 9 columns when written; 107 × 13 as rendered — 1,390 cells, one short of 107 × 13 because equation 6.9, the money wage, has no submitted_marco cell); the edit registry data/edits.csv (9 edits) and the arm registry data/arms.csv (6 runnable arms plus the current_revised alias); the model source model/code/MVP_model_2026.R (1,168 lines when written; 1,519 as rendered) and the pure price functions model/code/price_block_2026.R; the acceptance check R/price_block_check.R.


1. The one idea you need first: an edit, not a version

Until this week the model’s changes were described by version — a word that turned out to mean two unrelated things and could not carry a third. The description is now built on a single primitive.

An edit is a named set of equation changes measured from submitted_marco. An arm is a set of edits.

submitted_marco is the equation as Marco wrote it in Revised model description.docx (2026-03-31). It is a document: there is nothing to run. Everything else in the model is described as a change away from it.

There are seven edits.

edit what it changes equations it touches can it be run on its own?
coded what the submitted code actually did, against what the docx says 26 equations it is the code baseline
tar_author tariff on the foreign market price 5.4 yes — tar_spec
tar_customs tariff on the foreign pre-VAT price 5.4 yes — tar_spec
rec_secondary recycling summed over the secondary-material sectors 12.3 yes — rec_spec
waste_treatment waste referent = the treatment sectors 11.1, 11.2 yes — waste_spec
price_valuation basic vs buyer prices, destination VAT, duty on customs value 8.3–8.7, 9.1, 9.2, 9.6, 5.3, 5.4, 2.4 no
sfc_flow_of_funds the order in which eight equations are evaluated none — see §6 no

And six arms compose them:

arm = which edits what it is for
submitted coded reproduces the 2026-04-03 submission bit for bit; must never change
stale_author_variant coded + tar_author a comparison, not a candidate
customs coded + tar_customs the control that isolates the tariff channel
adjudicated customs + rec_secondary + waste_treatment + eq_adjudicated the eleven adjudicated equations on coded pricing
consistent_pricing adjudicated + price_valuation + sfc_flow_of_funds the design document, built
inflation consistent_pricing + wage_growth the money wage un-pinned
Extended on 2026-09-23 — this report describes the state of 2026-09-22

Two edits and two arms were added the following day, and the two tables above do not carry them. eq_adjudicated (switch leeds.eq_spec = "adjudicated") applies the eleven docx↔︎code differences that §2 adjudicates to Marco and that needed a code change — 1.1, 1.2, 3.2, 3.5, 6.6, 6.7, 7.1–7.4 and 12.13 — of which seven are numerically inert at the shipped parameters (3.2 because \(i^{d}_{p0} = 0\), 7.1–7.4 because all 48 behavioural \(\lambda\) are 0, 6.7 because all three \(\gamma_{imm}\) are 0, and 1.2 because \(xr\) is pinned at 1), leaving 1.1, 3.5, 6.6 and 12.13 as the four that move results. ⚠️ This sentence is wrong for 7.3 and 7.4 and is corrected in the 2026-09-25 addendum below; it is left standing because it is what this report said on 2026-09-23. 12.13 joined this edit later the same day: until then the energy-reserve sign correction described in §4.3 had been applied unconditionally, which changed submitted — the one arm whose contract is to reproduce the submission bit for bit, error included. wage_growth (switch leeds.wage_spec = "growth") un-pins the money wage and introduces a new equation, 6.9, which has no submitted_marco cell because the docx has none. The arms that compose them are current_revised and current_revised_inflation. The registries (data/edits.csv, data/arms.csv) and the companion page are current; the counts in this section — seven edits, five arms — are as of 2026-09-22 and are now nine and seven. Those two arm names were retired on 2026-09-23: they are now adjudicated and inflation, and current_revised became an alias that resolves to whichever arm is the frontier. The reasoning is in data/arms.csv’s header block.

Extended again on 2026-09-25 — the remaining 2026-09-22 arm names were retired, and the table above is updated to the settled vocabulary. author_variant → stale_author_variant (the stale sector indices are now part of the name); revised → folded into adjudicated (reached by loading adjudicated and resetting eq_spec = "coded"); revised_designed → consistent_pricing; and current_revised now points at consistent_pricing, the current frontier. There are six runnable arms — submitted, stale_author_variant, customs, adjudicated, consistent_pricing, inflation — down from the seven the 2026-09-23 note above counted, because revised folded into adjudicated. Throughout the measurements below, the arm formerly called revised is the coded base: adjudicated with eq_spec = "coded".

1.1 Why nothing is called “consistent” any more

This matters more than a naming quibble, because the old name concealed the failure described in §6.

In stock-flow-consistent modelling, “consistent” names three independent properties:

  1. Numerical convergence — the Gauss–Seidel sweep reaches a fixed point.
  2. Stock-flow consistency — the accounting identities close: every financial asset is somebody’s liability, and the world flow of funds sums to zero.
  3. Valuation consistency — prices, VAT and duties are valued coherently against one another.

The block we had been calling “the consistent price block” delivers only the third. And on 2026-09-22 it was measured doing all three things at once, in the same run: it converged (mean relative change across all variables fell to 2.8 × 10⁻¹⁵), it was valuation-consistent, and it violated stock-flow consistency by 21.308748 units per period. A single word had been asserting all three while only one was true.

The switch inside the code is still spelled price_spec = "consistent". The arm is not: it is consistent_pricing, and the two properties the block bundles are now two separately named edits, price_valuation and sfc_flow_of_funds.


2. The summary you can act on

Twenty changes. One of them repaired an accounting violation. The rest move levels, correct reporting, or record a discrepancy still awaiting your decision.

The tally below is the change log as it stood on 2026-09-22, when this section was written. It has since grown to 33 rows. The thirteen added are: two more price_valuation rows (armington.unwired, price.passthrough_half); ten eq_adjudicated rows — the eleven equations of that edit less energy.ken_sign, which was already logged on 2026-09-20 and so is counted in the twenty below; and one wage_growth row. No verdict in the table below changed.

verdict how many which
Repairs a stock-flow violation 1 sfc.gdef_resolve
Per-region misstatement, world sum intact 1 sfc.ff_resolve
SFC-neutral — moves levels only 5 energy.ken_sign, waste.flow_stock, matter.rec_spec, tar.base, price.consistent_block
Reporting or bookkeeping only 4 bop.tar_settlement, anchors.adjudication_2026-09-21, arm.price_spec_unread, catalogue.diff_type_multi
Adjudicated — no code change needed 4 xr.or_reserves, xr.supply_bills_delta, cab.cb_bond, ce.gammaA_switch
Deliberately not implemented 2 matter.cen_o2_absent, matter.kh_commented
Implemented since being recorded 1 price.pass_through
Open — needs a decision from Marco 2 tar.volume, imp.nominal

The two index corrections are not SFC repairs. rec_secondary and waste_treatment change the level of reported material and waste series. They do not touch an accounting identity, and presenting them as consistency fixes would overstate them.


3. The tariff base — three answers to one question

Equation 5.4 is the model’s most contested. It resolves into five distinct forms, more than any other equation in the catalogue.

form expression measured against submitted
Marco’s docx \(x_r^f \; imp^z \sum_j \tau_j^z \eta_j^z\) —
submitted_coded \(x_r^f \; imp_{tot}^z \sum_j \tau_j^z \eta_j^z\) the base
tar_author \(x_r^f \; imp_{tot}^z \sum_j \dfrac{p_j^f \tau_j^z \eta_j^z}{1+\tau_j^z}\) −3.881% (Z1) / −5.319% (Z2)
tar_customs \(x_r^f \; imp_{tot}^z \sum_j \dfrac{p_{t,j}^f \tau_j^z \eta_j^z}{1+\tau_j^z}\) −16.680% (Z1) / −17.839% (Z2)
price_valuation \(\sum_s \tau_s^z \, CV_s^z \, m_s^z\) see §5

tar_author uses the foreign market price \(p^f\), which carries the exporting region’s VAT — so it is \((1+vat^f)\) too high. tar_customs uses the pre-VAT price \(p_t^f\): the destination principle, under which exports are zero-rated and the importer levies its tariff on the customs value. That is the one the revision adopts.

SFC verdict: neutral. A different tariff base changes government revenue and therefore the deficit, but every unit is still somebody’s receipt. No identity moves.

Still owed: the model default. leeds.tar_spec at MVP_model_2026.R:428 is still submitted, while the revision reports customs. Every arm pins the value explicitly, so no arm run is affected — only runs made without an arm. Change log row tar.base, status planned.

3.1 The open question: which volume? — tar.volume

This needs Marco, and it is the largest unresolved number in the model.

Marco’s equation (5.4) multiplies by \(imp^z\), which his own equation (9.1) defines as the aggregate import function. The code multiplies by \(imp_{tot} = imp + M^{TOT}_{int}\), which adds the MRIO intermediate flows.

Measured on the cached submitted baseline at t = 99:

\(imp\) \(M^{TOT}_{int}\) \(imp_{tot}\) ratio
Z1 60.7612 125.1717 185.9330 3.060×
Z2 103.0532 108.9516 212.0048 2.057×

So tariff revenue is +206.0% (Z1) / +105.7% (Z2) above what equation (5.4) specifies: 16.2162 coded against 5.2993 on Marco’s base for Z1; 26.1627 against 12.7174 for Z2.

That gap is roughly 12.6× the size of the intended submitted → customs base change. The correction we are making is an order of magnitude smaller than a discrepancy we have not resolved.

Marco’s line 46 calls \(imp^z\) “real gross import”, which reads like the total — but his defining equation is (9.1), which is not. Economically a tariff applies at every border crossing, which favours \(imp_{tot}\); the paper reports the docx’s formulation, which says \(imp\). We cannot settle this from the code.

3.2 The same drift in nominal imports — imp.nominal

Identical issue, propagated. Marco’s (9.2) values imports at \(p_M \cdot imp\); the code computes \(pim \cdot imp_{tot}\). The code is internally split on this: rex reads the partner’s imp (matching Marco’s 9.3), while nimp reads tot_imp. Whatever is decided for tar.volume should be applied here in the same motion.


4. The two index corrections — real, and not accounting repairs

Both are the same kind of error: a line written for a stylised 5-sector model and never re-indexed when the model grew to 54 sectors.

4.1 Recycling — rec_secondary

Marco’s (12.3) is \(rec = \rho_{dis}\,dis\). The code adds a secondary-material term. Under the old default that term was \(\mu_{mat,5}\,x_5\) — sector 5 of 5 was waste in the stylised model, but sector 5 of 54 is Fossil-Fuel Extraction.

Measured at shock 13, t = 99, EU: that single term was 424.497 of a total \(Z1\_rec\) of 424.945 — 99.89%. Recycling was almost entirely an artefact of a stale index.

The default now sums over the eight genuine secondary-material sectors. All three older arms pin rec_spec = sector5 explicitly, so no cached arm run changed; only runs made without an arm move, and only inside \(rec \rightarrow mat \rightarrow k_{mat}\).

Still owed from Marco: his (12.3) carries no additive term at all. Either the docx absorbs the secondary-material term or the term goes.

4.2 Waste — waste_treatment, and a genuine bug alongside it

The code splits Marco’s single waste stock into a flow and a stock, subtracts purchases of waste-treatment services, and zeroes the treatment sectors’ own waste. Adjudicated in three parts: the split and the subtraction are deliberate extensions and stand.

The third part was a bug. The stock accumulated from the flow’s lag rather than its own: measured on the cached baseline, \(was_j(t) = wa_j(t-1) + wa_j(t)\) exactly (maximum error 8.9 × 10⁻¹⁶) — a two-period moving sum of flows, not a stock at all.

Fixed. Verified against a same-morning pre-fix run: 108 rows moved, all of them was/was_j, with maximum absolute difference zero everywhere else. The new identity \(was_j(t) = was_j(t-1) + wa_j(t)\) holds to 2.8 × 10⁻¹⁴. \(Z1\_was\) at t = 99 moves from 550.39 to 25,282.30.

SFC verdict: neutral, reporting-only. wa, was, wa_j and was_j are read only by each other — verified by R/check_arm_isolation.R. A 46-fold level change that touches no identity.

A note for the catalogue. waste_treatment changes no algebra: it reselects which rows of the \(A\) matrix are the treatment referent, and the written equation names the referent without naming which rows it is. Its column on the companion page is therefore identical to its base. The effect is entirely real and appears only in the computed series.

4.3 The energy-reserve sign — energy.ken_sign

A straightforward sign error, and the code contradicted both Marco and itself. Marco’s (12.13) has \(\Delta k_{en} = +conv_{en} - en_N^z - en_N^f\); the code had \(-conv_{en}\) — while the material-side line immediately above it correctly used \(+conv_{mat}\).

Fixed to \(+conv_{en}\). Verified: only the ken row changed, with \(\text{after} - \text{before} = 2\,\text{cumsum}(conv_{en})\) to 2 × 10⁻⁶.

Correction of 2026-09-23 — the fix is now conditional, and submitted keeps the error. As first applied (2026-09-21), the correction was unconditional: every arm got \(+conv_{en}\), including submitted, whose contract is to reproduce the 2026-04-03 submission bit for bit, sign error and all. It was moved behind leeds.eq_spec on 2026-09-23, so the coded arms (submitted, stale_author_variant, customs) carry the shipped \(-conv_{en}\) and the adjudicated arms (adjudicated, consistent_pricing, inflation) carry \(+conv_{en}\). Measured: the restoration moves 99 of 671,300 cells, all of them the ken row, because \(k_{en}\) is written once and read nowhere else in the engine; the cumulative effect at t = 99 is −275,764.641013 against \(-2\sum_{t=2}^{99} conv_{en} = {}\)−275,764.641014. Confirmed against full_mrio_baseline_2026.RDS (2026-03-28), the only run on disk predating every 2026-09 change: its ken at t = 2 is −20,451,012.809593, exactly the restored engine’s.

But the level stays negative, and this is worth your attention: \(k_{en}\) at t = 99 moves from −4,279,160,703.29 to −4,278,884,938.64. Depletion \(\sum en_N \approx 4.3 \times 10^7\) per period dwarfs \(conv_{en} \approx 1.2 \times 10^3\). The reserve collapse is scale-driven, not sign-driven — fixing the sign does not make the energy stock behave.


5. The valuation block — price_valuation

Specified in the design document, built this month. Eleven equations change form.

what changes before after
the price \(p\) VAT-inclusive VAT-free, at basic values
household deflator \(p_A\) \(p^\top \beta\) own VAT on both origins, on the duty-inclusive price
firm/government deflators same as household at basic prices — no VAT
the tariff base market or pre-VAT price the customs value \(CV = CIF/(1+\tau)\)
VAT base all purchases household purchases only, at the buyer’s rate
imports an estimated log function read off the MRIO — the function is retired
the cab settlement line added skipped — the customs valuation already carries it

Two consequences worth flagging to you directly.

The estimated import function is gone. Under this edit, imports stop being behavioural and become an accounting quantity: the cross-border block of final demand plus the intermediate block of \(A\). The parameters \(m_0, m_1, m_2\) are no longer read. Separately: those parameters have no recorded provenance in the repository, which is its own open question.

The tariff pass-through share \(\varphi\) now exists. The design specifies \(CIF = xr\,p^f(1+\varphi\tau)\) with \(\varphi \in [0,1]\) and a default of 0, i.e. exporter incidence. It was recorded on 2026-09-19 as absent from the code; it is now implemented and the default is 0 in both regions, so every run to date sits at exporter incidence. The vocabulary mismatch behind it is still open: the code’s tariff mechanism is the three-way tar_spec switch, which the design document does not name at all. The two describe the same object in different words.

SFC verdict: neutral in itself. Revaluation moves who is credited what, not whether the books close. Which is precisely what made the next section hard to find.


6. The one real accounting violation — sfc_flow_of_funds

This is the only change so far that repaired a stock-flow violation, and the reason it is worth reading in full is that every obvious diagnosis of it was wrong.

6.1 What was observed

Under price_spec = "consistent" the run aborted: the consistency error sat at 227 against a threshold of 0.01.

6.2 What it was not

Not a convergence failure. Reading the Gauss–Seidel trace at t = 2: the iteration converges. Mean relative change across all variables falls to 2.8 × 10⁻¹⁵, and the error is flat at 227.031369 from iteration 66 through 100. The solver finds a fixed point. The fixed point violates the identity.

Not a balance-sheet failure. Every stock-flow identity holds to machine precision: \(\Delta b_s = gdef\), \(\Delta v = yd - c\,p_A\), firm loan accumulation, household portfolio adding-up, and the bank balance sheet are all exactly zero. The failure is in income accounting, not in a balance sheet.

Not a missing cross-sector flow, and not a recalibration. Both were proposed and both were wrong.

6.3 What it was

An ordering defect. The valuation block overrides values after the original lines, so that the untouched path stays bit-for-bit identical. That leaves any equation sitting between an original assignment and its override reading the original value for good — Gauss–Seidel cannot repair it, because the original assignment re-executes ahead of the reader on every sweep.

gdef reads vat_rev and tar_rev, assigned at lines 406 and 430. The block does not overwrite them until line 909 — 460 lines later.

Measured at t = 2, at the converged state:

Z1 Z2
vat_rev as gdef sees it 30.805189 108.149197
vat_rev as yn sees it 35.498963 124.758756
tar_rev as gdef sees it 5.550883 7.487177
tar_rev as yn sees it 5.555253 7.488221
total −4.698144 −16.610604

The government was credited one VAT figure while the national accounts were debited another, simultaneously, in the same period, at the same converged state.

World total: −21.308748, matching the measured bill-market residual to 2.0 × 10⁻¹³. 99.98% of the leak is VAT (−21.3034 of −21.3087); the tariff part is −0.0054.

6.4 The second instance, and why it is milder

The same class of defect, found by the same trace. f_f and div_h1 read yn, which the block overwrites later. The dividend that actually enters yd is computed from the previous sweep’s stored yn — hence the block’s — while stored f_f, div_h1, div and r_e were built on the original.

The wedge is −6.765387 (Z1) / +6.765387 (Z2) at t = 99 — exactly the net tariff settlement, 12.65 − 19.41.

It is antisymmetric, so it cancels in the world sum and does not break the bill market. This one is a per-region misstatement, not an accounting violation: it misstated the current account by +1.071251 (Z1) / −1.071251 (Z2) at t = 99 — reproduced from the cross-border ownership shares to 2.8 × 10⁻⁷ at t = 10, 50 and 99 — and carried into the capitalist income-tax base and the return on shares.

6.5 The fix, and why the edit shows no equation change

Eight equations were re-solved inside the block: gdef, b_s, debt_gdp, f_f, f_f_u, lf, r_e, div_h1. Each line is its original, verbatim — nothing changed but where it sits, and therefore which values it can see.

This is why sfc_flow_of_funds has an empty “equations touched” entry on the companion page, and why its column is identical to its base. It changed evaluation order, not algebra. That is a finding, not an omission — and it is exactly the kind of defect an equation catalogue cannot show you, which is why this report exists.

Four acceptance gates now pass: R/price_block_check.R (B1–B6) exits 0; the original path is bit-identical to the previous commit across all 671,300 cells; and the fast engine is bit-identical under both price specifications.

6.6 The trap this sprang, and what it costs

The fast engine had been stale since 2026-09-21 and was silently running the original price path under price_spec = "consistent". Nothing caught it, because no acceptance test exercised the fast engine under a non-default price specification.

Consequence for anyone running the model: the generated engine must be rebuilt after any edit to the model source. It is now verified bit-identical on both specifications, at 3.77× and 5.51× the original speed.


7. Four discrepancies that need no code change

Recorded, measured, adjudicated, closed. Listed so you can see they were examined rather than missed.

xr.or_reserves — the code’s central-bank identities carry an official-reserves term \(OR \cdot p_{or}\) that Marco’s (10.2)/(10.3) omit. Numerically inert: \(Z1\_or = Z2\_or = 0\) at t = 1, 50 and 99, the update lines are commented out, and the solver pins \(\Delta or = 0\). The code reduces exactly to Marco’s. Dormant scaffolding for a reserve-accumulation closure that is switched off. If reserves are ever activated, the docx must gain the terms.

xr.supply_bills_delta — Marco writes \(\Delta B_{s,z}^f\) on the left-hand side; the code computes the level. The right-hand sides are identical and Marco’s own prose reads “the supply … is defined as”, which is a level. The \(\Delta\) is a typo in the docx.

cab.cb_bond — Marco writes the central-bank bond interest term symmetrically for both areas; the code has only one cross-border central-bank position (Z1 holds Z2’s world money), so Z2’s entry is the mirror. Intentional one-sided holding. The docx should note the asymmetry.

ce.gammaA_switch — the partial-adjustment form is identical; the code scales the speed by a scenario switch that is zero before the shock and lags \(\gamma_A\) one period. Scenario gating and standard timing, not formula drift.


8. Two equations deliberately not implemented

Both are exactly derivable after a run from series every run already carries. Implementing them in the engine would add rows to the initial-state workbook, re-dimension every simulation and break comparability with every cached run — for series nothing reads.

cen and o2 (12.16, 12.17). \(cen = emis/car\) and \(o_2 = emis - cen\), where \(car = 44/12\) converts Gt C to Gt CO₂. Terminal indicators with no consumers anywhere in the code. If the revision reports them, derive them at the table layer.

\(\Delta k_h\) (12.6). Commented out; kh does not exist in the simulation array at all, and the docx supplies no initial value. Exactly recoverable as \(k_h(t) = k_h(1) + \text{cumsum}(x_{mat} - dis)\). Marco keeps (12.6) as the definition; any reported \(k_h\) is derived, not simulated.


9. What we need from you

Regenerated 2026-09-22 after the maintainer settled two of the original six. The tariff-base questions (§3.1, §3.2) are closed: the base is tot_imp — all imports, components and intermediates included, because a tariff applies at every border crossing. That decision means the code was already right and the docx is the side to amend, which changes what we are asking you for.

Marco — four amendments to the model description

  1. Equations (5.4) and (9.2) should multiply by total imports, not imp. Your (5.4) and (9.2) use \(imp^z\), defined by your own (9.1) as the aggregate import function. The code uses \(imp_{tot} = imp + M^{TOT}_{int}\), which adds the MRIO intermediate flows — measured 3.060× (Z1) and 2.057× (Z2) larger at t = 99. Adopted as correct. Your line 46 calls \(imp^z\) “real gross import”, which reads like the total, so this may be a wording slip rather than a disagreement — but (9.1) defines it narrowly and the two need to agree.
  2. Equation (12.3) carries no additive recycling term; the code has one. Either the docx absorbs the secondary-material sum or the term goes. We cannot decide this for you: it is a modelling claim about where secondary material comes from.
  3. Equation (9.6) should state the central-bank asymmetry. You write the bond-interest term symmetrically for both areas; the code has one cross-border central-bank position (Z1 holds Z2’s world money) and Z2’s entry is its mirror. Adjudicated as intentional — please state it.
  4. Equation (10.4)’s \(\Delta\) appears to be a typo. You write \(\Delta B_{s,z}^f\) on the left; the code computes the level, the right-hand sides are identical, and your own prose says “the supply … is defined as”, which is a level. Confirm and we will close the row.

Marco — one question only you can answer

  1. ✅ ANSWERED 2026-09-25 — by the workbook, not by Marco. See the addendum below; the question as put here no longer needs him. (Original text kept:) What were \(\lambda_{15}\), \(\lambda_{16}\), \(\lambda_{25}\), \(\lambda_{26}\) and \(\lambda_{31}\ldots\lambda_{46}\) meant to multiply? The initial-state workbook carries 58 lambda values, of which 48 are zero and 32 are read by no equation at all. The four coded portfolio equations use suffixes 1–4 (Z1’s bill rate, Z2’s bill rate plus the exchange-rate gain, the deposit rate, disposable income). Suffixes 5 and 6 are carried, unread, and no file in the repository records what they were for. A full Tobin matrix over the four assets would want the two share returns there, but that is our conjecture. Without your answer the portfolio cannot be written out — it is currently fixed shares of wealth with no interest-rate or income sensitivity whatever.

Jose — one modelling decision

  1. price_valuation and sfc_flow_of_funds are two edits in specification but one switch in code. Both sit inside if (price_spec == "consistent"), so you cannot run the valuation block without the flow-of-funds repair, nor test them apart. Separating them is a small change and is worth doing only if we want to report them as distinct contributions.

  2. The Armington origin split is specified, written, unit-tested and never called. Decided at \(\varepsilon = 0.626\), not yet wired. Until it is, the tariff has a revenue channel and no substitution channel: duties fall on every border crossing, half of them passed through to the importing buyer, and nobody substitutes away from the dutied origin. Worth knowing before any tariff result is interpreted.

10. Where everything lives

artefact what it holds
model/equations/model-equations.html the companion page — five tabs, equations typeset, disagreement between columns computed rather than stored
qmd/equations/CHANGELOG.csv the 20 rows this report narrates, with source on every one
qmd/equations/equations.csv 106 equations × 9 columns
data/edits.csv the edit registry
data/arms.csv the arm registry — what each arm actually runs
tools/build_equation_tables.sh rebuilds the page; the invocation is recorded so it cannot be lost again

One discipline that produced most of the above. No row is written without a source — a file, a line and a measurement. Where a number is quoted here, it was read off a run, not inferred. Where something is unresolved, it is listed in §9 rather than resolved by assumption.

Extended on 2026-09-25 — the portfolio parameters are labelled in the workbook, and two of them were being read backwards

Two things changed since the 2026-09-23 addendum, and both come from one file nobody in this project had opened: the description column of data/initial_state_2026.xlsx, sheet aggregate.pars. It names every lambda in prose.

The answer to question 5

The question above asks what \(\lambda_{15}\), \(\lambda_{16}\), \(\lambda_{25}\), \(\lambda_{26}\) and \(\lambda_{31}\ldots\lambda_{46}\) were meant to multiply, and says “that is our conjecture”. The conjecture was right, and the workbook says so outright. All eight of the suffix-5 and suffix-6 coefficients carry these descriptions:

description, verbatim
\(\lambda_{15}, \lambda_{25}, \lambda_{35}, \lambda_{45}\) Sensitivity of Portfolio Choices to Return Rate on Z1 Shares
\(\lambda_{16}, \lambda_{26}, \lambda_{36}, \lambda_{46}\) Sensitivity of Portfolio Choices to Return Rate on Z2 Shares

So suffix 5 is the return on Z1-issued equity and suffix 6 the return on Z2-issued equity, in all four demand equations — the full Tobin matrix over the four assets, exactly as conjectured. Marco does not need to answer question 5. What remains for him is narrower and is stated at the end of this addendum.

Note also that the count in question 5 has moved: it said 32 of the 58 values are read by no equation at all. Measured 2026-09-25 against the current engine, all 29 lambda parameters (58 values) are now read — the eq_adjudicated edit introduced the suffix-5 and suffix-6 terms into all four equations. They remain zero; being read and being active are different things.

The correction: (7.3) and (7.4) were not inert

What this report claimed, in the 2026-09-23 addendum above: seven of the eleven adjudicated equations are numerically inert, “7.1–7.4 because all 48 behavioural \(\lambda\) are 0”, leaving 1.1, 3.5, 6.6 and 12.13 as the four that move results.

What was measured, 2026-09-24, over twenty baseline runs (output/calibration/eq_isolation_20260924.csv, produced by R/run_eq_isolation.R): at \(t = 99\), against the coded base’s \(Z1\_b\_s\) of \(+751.503\), equation 1.1 alone gives \(751.142\), 3.5 alone \(751.502\), 6.6 alone moves it by \(4\times10^{-9}\), and (7.3)/(7.4) alone give \(-72{,}163.207\), with calib_debt_survival() falling from its \(-100\) floor to \(-9\). That one site carried 99.991% of the \(-72{,}921\) move from the coded base to adjudicated.

Why the stated basis did not cover it. “All 48 behavioural \(\lambda\) are 0” is true, and it does cover 7.1 and 7.2, whose adjudicated form only adds terms in \(\lambda_{15}, \lambda_{16}, \lambda_{25}, \lambda_{26}\). It does not cover 7.3 and 7.4, whose difference from the coded form is in \(\lambda_{30}\) and \(\lambda_{40}\) — the constant wealth shares, which are not behavioural coefficients and are not zero.

The cause: the workbook mixes two indexing conventions, and says so

description, verbatim Z1 Z2 indexing
\(\lambda_{10}\) Fixed Proportion of Domestic Government Bills to Total Wealth 0.10 0.10 own/foreign
\(\lambda_{20}\) Fixed Proportion of Foreign Government Bills to Total Wealth 0.03 0.01 own/foreign
\(\lambda_{30}\) Fixed Proportion of Z1 Shares to Total Wealth 0.25 0.01 issuer
\(\lambda_{40}\) Fixed Proportion of Z2 Shares to Total Wealth 0.02 0.18 issuer

Bills are labelled own/foreign; equity is labelled by issuer. The coded equations implement both faithfully, and under the issuer reading the shipped values are coherent: each region holds mostly its own equity, the same home-bias direction its bill block shows, RoW at 0.18 own against 0.01 foreign. Marco writes (7.3)/(7.4) in own/foreign terms throughout, so for Z2 only his form lands \(\lambda_{30}\) — 0.01, “Z1 Shares” — on the own-share row. RoW then holds 1% of its own equity and 18% of the EU’s: a permanent capital inflow into the EU from period one, whose counterpart with \(xr\) pinned at 1 is an EU external surplus that the budget identity accumulates into government assets. For Z1 the two readings coincide, which is why only one region diverges.

The fix, and what was not done

No data changed. data/initial_state_2026.xlsx is untouched. Inside the adjudicated branch of (7.3)/(7.4), and for Z2 only, the \(\lambda_{3x}\) and \(\lambda_{4x}\) families swap rows, so Marco’s algebra and his sign pattern are preserved and the parameters are read as the workbook labels them.

⚠️ Swapping the two workbook values instead would have been wrong, and that was measured before it was rejected. The coded branch reads the same two parameters, so a bare swap moves the divergence into the coded arms — the coded base with the values swapped gives \(Z1\_b\_s = -72{,}163.207\) and survival \(-9\) — and it would break submitted’s contract to reproduce the 2026-04-03 submission bit for bit.

The coded arms are unaffected by the fix, and this needs no simulation to establish: for them the edited branch is folded away entirely, and the regenerated files under model/code/arms/ for submitted, stale_author_variant and customs differ only in the source md5 stamp and in provenance comment line numbers.

What is left for Marco

Not question 5, and not an adjudication between two defensible equations. One confirmation:

Your (7.3)/(7.4) are written own/foreign. The workbook labels \(\lambda_{30}\) and \(\lambda_{40}\) by issuer — “Z1 Shares”, “Z2 Shares” — and its values only make sense that way. We have kept your algebra and signs and read the parameters as the workbook labels them, which for RoW means the \(\lambda_{4x}\) family on the own-share row. Confirm that is what you intended, or tell us the parameters should be relabelled instead.