Skip to content

Commit a0f4469

Browse files
committed
Auto-publish Trade V4 fiscal handoff
1 parent 18a8332 commit a0f4469

4 files changed

Lines changed: 159 additions & 50 deletions

File tree

site/admin/index.html

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
})();
1919
</script>
2020
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='10' fill='%2314171a'/%3E%3Cpath d='M8 8h32L8 40z' fill='%23f0c33b'/%3E%3Cpath d='M56 56H24l32-32z' fill='%230d65d9'/%3E%3C/svg%3E">
21-
<link rel="stylesheet" href="../css/base.css?v=20260604-pre-v4-budget-balance">
22-
<link rel="stylesheet" href="../css/trade.css?v=20260604-pre-v4-budget-balance">
23-
<link rel="stylesheet" href="../css/ledger.css?v=20260604-pre-v4-budget-balance">
24-
<link rel="stylesheet" href="../css/simulation.css?v=20260604-pre-v4-budget-balance">
25-
<link rel="stylesheet" href="../css/records.css?v=20260604-pre-v4-budget-balance">
26-
<link rel="stylesheet" href="../css/responsive.css?v=20260604-pre-v4-budget-balance">
21+
<link rel="stylesheet" href="../css/base.css?v=20260604-budget-handoff-publish">
22+
<link rel="stylesheet" href="../css/trade.css?v=20260604-budget-handoff-publish">
23+
<link rel="stylesheet" href="../css/ledger.css?v=20260604-budget-handoff-publish">
24+
<link rel="stylesheet" href="../css/simulation.css?v=20260604-budget-handoff-publish">
25+
<link rel="stylesheet" href="../css/records.css?v=20260604-budget-handoff-publish">
26+
<link rel="stylesheet" href="../css/responsive.css?v=20260604-budget-handoff-publish">
2727
</head>
2828
<body data-app-mode="admin">
2929
<header class="topbar">
@@ -63,24 +63,24 @@ <h1>Global Ledger</h1>
6363
<span id="sourceNote"></span>
6464
</footer>
6565

66-
<script src="../data.js?v=20260604-pre-v4-budget-balance"></script>
67-
<script src="../js/engine/fiscal.js?v=20260604-pre-v4-budget-balance"></script>
68-
<script src="../js/engine/tradePolicy.js?v=20260604-pre-v4-budget-balance"></script>
69-
<script src="../js/engine/trade.js?v=20260604-pre-v4-budget-balance"></script>
70-
<script src="../engine.js?v=20260604-pre-v4-budget-balance"></script>
71-
<script src="../js/app/config.js?v=20260604-pre-v4-budget-balance"></script>
72-
<script src="../js/app/format.js?v=20260604-pre-v4-budget-balance"></script>
73-
<script src="../js/app/sync.js?v=20260604-pre-v4-budget-balance"></script>
74-
<script src="../js/app/statusTables.js?v=20260604-pre-v4-budget-balance"></script>
75-
<script src="../js/app/recordsParser.js?v=20260604-pre-v4-budget-balance"></script>
76-
<script src="../js/app/records.js?v=20260604-pre-v4-budget-balance"></script>
77-
<script src="../js/app/tradeMapShapes.js?v=20260604-pre-v4-budget-balance"></script>
78-
<script src="../js/app/tradeZones.js?v=20260604-pre-v4-budget-balance"></script>
79-
<script src="../js/app/tradeRouteMesh.js?v=20260604-pre-v4-budget-balance"></script>
80-
<script src="../js/app/tradeLaneSkeleton.js?v=20260604-pre-v4-budget-balance"></script>
81-
<script src="../js/app/tradeMap.js?v=20260604-pre-v4-budget-balance"></script>
82-
<script src="../js/app/tradeView.js?v=20260604-pre-v4-budget-balance"></script>
83-
<script src="../js/app/editorView.js?v=20260604-pre-v4-budget-balance"></script>
84-
<script src="../app.js?v=20260604-pre-v4-budget-balance"></script>
66+
<script src="../data.js?v=20260604-budget-handoff-publish"></script>
67+
<script src="../js/engine/fiscal.js?v=20260604-budget-handoff-publish"></script>
68+
<script src="../js/engine/tradePolicy.js?v=20260604-budget-handoff-publish"></script>
69+
<script src="../js/engine/trade.js?v=20260604-budget-handoff-publish"></script>
70+
<script src="../engine.js?v=20260604-budget-handoff-publish"></script>
71+
<script src="../js/app/config.js?v=20260604-budget-handoff-publish"></script>
72+
<script src="../js/app/format.js?v=20260604-budget-handoff-publish"></script>
73+
<script src="../js/app/sync.js?v=20260604-budget-handoff-publish"></script>
74+
<script src="../js/app/statusTables.js?v=20260604-budget-handoff-publish"></script>
75+
<script src="../js/app/recordsParser.js?v=20260604-budget-handoff-publish"></script>
76+
<script src="../js/app/records.js?v=20260604-budget-handoff-publish"></script>
77+
<script src="../js/app/tradeMapShapes.js?v=20260604-budget-handoff-publish"></script>
78+
<script src="../js/app/tradeZones.js?v=20260604-budget-handoff-publish"></script>
79+
<script src="../js/app/tradeRouteMesh.js?v=20260604-budget-handoff-publish"></script>
80+
<script src="../js/app/tradeLaneSkeleton.js?v=20260604-budget-handoff-publish"></script>
81+
<script src="../js/app/tradeMap.js?v=20260604-budget-handoff-publish"></script>
82+
<script src="../js/app/tradeView.js?v=20260604-budget-handoff-publish"></script>
83+
<script src="../js/app/editorView.js?v=20260604-budget-handoff-publish"></script>
84+
<script src="../app.js?v=20260604-budget-handoff-publish"></script>
8585
</body>
8686
</html>

site/index.html

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
})();
2929
</script>
3030
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='10' fill='%2314171a'/%3E%3Cpath d='M8 8h32L8 40z' fill='%23f0c33b'/%3E%3Cpath d='M56 56H24l32-32z' fill='%230d65d9'/%3E%3C/svg%3E">
31-
<link rel="stylesheet" href="css/base.css?v=20260604-pre-v4-budget-balance">
32-
<link rel="stylesheet" href="css/trade.css?v=20260604-pre-v4-budget-balance">
33-
<link rel="stylesheet" href="css/ledger.css?v=20260604-pre-v4-budget-balance">
34-
<link rel="stylesheet" href="css/simulation.css?v=20260604-pre-v4-budget-balance">
35-
<link rel="stylesheet" href="css/records.css?v=20260604-pre-v4-budget-balance">
36-
<link rel="stylesheet" href="css/responsive.css?v=20260604-pre-v4-budget-balance">
31+
<link rel="stylesheet" href="css/base.css?v=20260604-budget-handoff-publish">
32+
<link rel="stylesheet" href="css/trade.css?v=20260604-budget-handoff-publish">
33+
<link rel="stylesheet" href="css/ledger.css?v=20260604-budget-handoff-publish">
34+
<link rel="stylesheet" href="css/simulation.css?v=20260604-budget-handoff-publish">
35+
<link rel="stylesheet" href="css/records.css?v=20260604-budget-handoff-publish">
36+
<link rel="stylesheet" href="css/responsive.css?v=20260604-budget-handoff-publish">
3737
</head>
3838
<body data-app-mode="public">
3939
<header class="topbar">
@@ -75,24 +75,24 @@ <h1>Global Ledger</h1>
7575
</span>
7676
</footer>
7777

78-
<script src="data.js?v=20260604-pre-v4-budget-balance"></script>
79-
<script src="js/engine/fiscal.js?v=20260604-pre-v4-budget-balance"></script>
80-
<script src="js/engine/tradePolicy.js?v=20260604-pre-v4-budget-balance"></script>
81-
<script src="js/engine/trade.js?v=20260604-pre-v4-budget-balance"></script>
82-
<script src="engine.js?v=20260604-pre-v4-budget-balance"></script>
83-
<script src="js/app/config.js?v=20260604-pre-v4-budget-balance"></script>
84-
<script src="js/app/format.js?v=20260604-pre-v4-budget-balance"></script>
85-
<script src="js/app/sync.js?v=20260604-pre-v4-budget-balance"></script>
86-
<script src="js/app/statusTables.js?v=20260604-pre-v4-budget-balance"></script>
87-
<script src="js/app/recordsParser.js?v=20260604-pre-v4-budget-balance"></script>
88-
<script src="js/app/records.js?v=20260604-pre-v4-budget-balance"></script>
89-
<script src="js/app/tradeMapShapes.js?v=20260604-pre-v4-budget-balance"></script>
90-
<script src="js/app/tradeZones.js?v=20260604-pre-v4-budget-balance"></script>
91-
<script src="js/app/tradeRouteMesh.js?v=20260604-pre-v4-budget-balance"></script>
92-
<script src="js/app/tradeLaneSkeleton.js?v=20260604-pre-v4-budget-balance"></script>
93-
<script src="js/app/tradeMap.js?v=20260604-pre-v4-budget-balance"></script>
94-
<script src="js/app/tradeView.js?v=20260604-pre-v4-budget-balance"></script>
95-
<script src="js/app/editorView.js?v=20260604-pre-v4-budget-balance"></script>
96-
<script src="app.js?v=20260604-pre-v4-budget-balance"></script>
78+
<script src="data.js?v=20260604-budget-handoff-publish"></script>
79+
<script src="js/engine/fiscal.js?v=20260604-budget-handoff-publish"></script>
80+
<script src="js/engine/tradePolicy.js?v=20260604-budget-handoff-publish"></script>
81+
<script src="js/engine/trade.js?v=20260604-budget-handoff-publish"></script>
82+
<script src="engine.js?v=20260604-budget-handoff-publish"></script>
83+
<script src="js/app/config.js?v=20260604-budget-handoff-publish"></script>
84+
<script src="js/app/format.js?v=20260604-budget-handoff-publish"></script>
85+
<script src="js/app/sync.js?v=20260604-budget-handoff-publish"></script>
86+
<script src="js/app/statusTables.js?v=20260604-budget-handoff-publish"></script>
87+
<script src="js/app/recordsParser.js?v=20260604-budget-handoff-publish"></script>
88+
<script src="js/app/records.js?v=20260604-budget-handoff-publish"></script>
89+
<script src="js/app/tradeMapShapes.js?v=20260604-budget-handoff-publish"></script>
90+
<script src="js/app/tradeZones.js?v=20260604-budget-handoff-publish"></script>
91+
<script src="js/app/tradeRouteMesh.js?v=20260604-budget-handoff-publish"></script>
92+
<script src="js/app/tradeLaneSkeleton.js?v=20260604-budget-handoff-publish"></script>
93+
<script src="js/app/tradeMap.js?v=20260604-budget-handoff-publish"></script>
94+
<script src="js/app/tradeView.js?v=20260604-budget-handoff-publish"></script>
95+
<script src="js/app/editorView.js?v=20260604-budget-handoff-publish"></script>
96+
<script src="app.js?v=20260604-budget-handoff-publish"></script>
9797
</body>
9898
</html>

site/js/app/sync.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@
8888
const nextRevision = Number(payload.revision || 0);
8989
if (nextRevision && nextRevision === sharedSync.revision) return false;
9090
const nextData = Engine.normalizeState(Engine.clone(payload.data));
91+
const sourceFiscalVersion = nextData.meta?.tradeV4FiscalBalanceVersion || "";
9192
TradeMap.ensureGeography?.(nextData);
9293
Engine.recalculateAll(nextData);
94+
const fiscalMigrationApplied = isAdmin && nextData.meta?.tradeV4FiscalBalanceVersion && nextData.meta.tradeV4FiscalBalanceVersion !== sourceFiscalVersion;
9395
clearPendingChanges?.();
9496
setData(nextData);
9597
sharedSync.revision = nextRevision || sharedSync.revision;
@@ -100,6 +102,7 @@
100102
updateSourceNote();
101103
state.notice = "";
102104
render();
105+
if (fiscalMigrationApplied) scheduleSharedPublish("Published Trade V4 budget balance handoff.");
103106
return true;
104107
}
105108

tools/sync-fiscal-publish.test.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
const assert = require("node:assert/strict");
2+
const fs = require("node:fs");
3+
const path = require("node:path");
4+
const test = require("node:test");
5+
const vm = require("node:vm");
6+
7+
const ROOT = path.resolve(__dirname, "..");
8+
9+
function loadSyncController({ fetchImpl, setTimeoutImpl }) {
10+
const context = {
11+
console,
12+
clearTimeout: () => {},
13+
document: {
14+
activeElement: null,
15+
addEventListener: () => {},
16+
hidden: false
17+
},
18+
fetch: fetchImpl,
19+
setInterval: () => 1,
20+
setTimeout: setTimeoutImpl
21+
};
22+
context.window = context;
23+
vm.createContext(context);
24+
vm.runInContext(fs.readFileSync(path.join(ROOT, "site/js/app/sync.js"), "utf8"), context, { filename: "site/js/app/sync.js" });
25+
return context.window.AGGS_APP_MODULES.createSyncController;
26+
}
27+
28+
function jsonResponse(payload, status = 200) {
29+
return {
30+
ok: status >= 200 && status < 300,
31+
status,
32+
text: async () => JSON.stringify(payload)
33+
};
34+
}
35+
36+
test("admin sync publishes live state after one-time fiscal migration", async () => {
37+
const published = [];
38+
let data = null;
39+
let pendingTimer = null;
40+
const createSyncController = loadSyncController({
41+
fetchImpl: async (url, options = {}) => {
42+
if (options.method === "PUT") {
43+
published.push(JSON.parse(options.body));
44+
return jsonResponse({ ok: true, revision: 754, updatedAt: "2026-06-04T23:00:00.000Z" });
45+
}
46+
if (String(url).includes("/snapshots")) return jsonResponse({ ok: true, snapshots: [] });
47+
return jsonResponse({
48+
ok: true,
49+
revision: 753,
50+
updatedAt: "2026-06-04T22:00:00.000Z",
51+
data: {
52+
meta: { tradeFormulaVersion: "trade2028" },
53+
national: {
54+
solara: { budgetCapacity: 95104, budgetExpenditure: 89143, budgetBalance: 5807 }
55+
},
56+
tradeNetwork: {}
57+
}
58+
});
59+
},
60+
setTimeoutImpl: (callback) => {
61+
pendingTimer = callback();
62+
return 1;
63+
}
64+
});
65+
66+
const sharedSync = {
67+
enabled: true,
68+
endpoint: "/admin/api/state",
69+
metaEndpoint: "/admin/api/state/meta",
70+
revision: 0,
71+
status: "connecting",
72+
snapshots: []
73+
};
74+
const controller = createSyncController({
75+
getData: () => data,
76+
setData: (nextData) => { data = nextData; },
77+
baseData: {},
78+
Engine: {
79+
clone: (value) => JSON.parse(JSON.stringify(value)),
80+
normalizeState: (value) => value,
81+
recalculateAll: (nextData) => {
82+
nextData.meta.tradeV4FiscalBalanceVersion = "pre-v4-balance-20260604";
83+
nextData.national.solara.budgetExpenditure = 90074;
84+
nextData.national.solara.budgetBalance = 2236;
85+
},
86+
save: () => {}
87+
},
88+
TradeMap: { ensureGeography: () => {} },
89+
sharedSync,
90+
state: {},
91+
isAdmin: true,
92+
updateSourceNote: () => {},
93+
render: () => {},
94+
ensureSelectedNation: () => {},
95+
populateNationSelect: () => {},
96+
clearPendingChanges: () => {}
97+
});
98+
99+
await controller.fetchSharedState();
100+
await pendingTimer;
101+
102+
assert.equal(published.length, 1);
103+
assert.equal(published[0].revision, 753);
104+
assert.equal(published[0].data.meta.tradeV4FiscalBalanceVersion, "pre-v4-balance-20260604");
105+
assert.equal(published[0].data.national.solara.budgetBalance, 2236);
106+
});

0 commit comments

Comments
 (0)