Trim dashboard bootstrap payload
Some checks failed
deploy / deploy (push) Failing after 37s

Proof: npm test passed 161/161; npm run operator-dashboard:build passed; local dev dashboard API returned HTTP 200 after cleaning stale port-forward processes.

Assumptions: Forgejo main push remains the deployment path; imported 1Click assets should remain catalog-visible but not enter Funds balances unless inventory-enabled.

Still fake: no live funds movement; local dashboard access still depends on a healthy kubectl port-forward because no public dashboard ingress is part of this turn.
This commit is contained in:
philipp 2026-05-12 22:05:22 +02:00
parent af87a08a19
commit 339a1d8c43
2 changed files with 92 additions and 3 deletions

View file

@ -755,11 +755,14 @@ function buildBalanceSummary({ inventorySnapshot, marketPrice, config }) {
const spendable = inventory.spendable || {}; const spendable = inventory.spendable || {};
const pendingInbound = inventory.pending_inbound || {}; const pendingInbound = inventory.pending_inbound || {};
const pendingOutbound = inventory.pending_outbound || {}; const pendingOutbound = inventory.pending_outbound || {};
const balanceAssets = config.trackedAssets?.length
? config.trackedAssets
: [...config.assetRegistry.values()];
return { return {
synced_at: inventory.synced_at || inventorySnapshot?.ingested_at || null, synced_at: inventory.synced_at || inventorySnapshot?.ingested_at || null,
reconciliation_status: inventory.reconciliation_status || null, reconciliation_status: inventory.reconciliation_status || null,
items: [...config.assetRegistry.values()].map((asset) => { items: balanceAssets.map((asset) => {
const spendableUnits = String(spendable[asset.assetId] || '0'); const spendableUnits = String(spendable[asset.assetId] || '0');
const pendingInboundUnits = String(pendingInbound[asset.assetId] || '0'); const pendingInboundUnits = String(pendingInbound[asset.assetId] || '0');
const pendingOutboundUnits = String(pendingOutbound[asset.assetId] || '0'); const pendingOutboundUnits = String(pendingOutbound[asset.assetId] || '0');
@ -1540,8 +1543,8 @@ function buildTradeFunnelSummary(lifecycleRows = []) {
unresolved_submission_count: unresolvedSubmissions.length, unresolved_submission_count: unresolvedSubmissions.length,
no_trade_count: noTradeRows.length, no_trade_count: noTradeRows.length,
successful_trades: successfulTrades, successful_trades: successfulTrades,
unresolved_submissions: unresolvedSubmissions, unresolved_submissions: unresolvedSubmissions.slice(0, 20),
no_trade_rows: noTradeRows, no_trade_rows: [],
counts, counts,
caveat: caveat:
successfulTrades.length > 0 successfulTrades.length > 0

View file

@ -847,6 +847,92 @@ test('bootstrap balances and funding handles distinguish nBTC reserve from legac
assert.equal(bootstrap.funds.funding.handles[0].label, 'btc:mainnet funding handle'); assert.equal(bootstrap.funds.funding.handles[0].label, 'btc:mainnet funding handle');
}); });
test('bootstrap balances exclude imported catalog assets that are not inventory-enabled', () => {
const config = buildDualBtcConfig();
const importedOnlyAsset = {
assetId: 'nep141:sol-imported-token.near',
symbol: 'CATALOG',
label: 'Catalog-only token',
decimals: 6,
chain: 'sol',
supported: true,
enabledForInventory: false,
};
config.assetRegistry = new Map([
...config.assetRegistry,
[importedOnlyAsset.assetId, importedOnlyAsset],
]);
const bootstrap = buildDashboardBootstrap({
config,
inventorySnapshot: {
ingested_at: '2026-05-12T19:45:00.000Z',
payload: {
synced_at: '2026-05-12T19:45:00.000Z',
reconciliation_status: 'ok',
spendable: {
[config.tradingBtc.assetId]: '100000',
[importedOnlyAsset.assetId]: '999999',
},
pending_inbound: {},
pending_outbound: {},
},
},
marketPrice: null,
recentQuotes: [],
submissionPage: { page: 1, page_size: 20, total: 0, total_pages: 1, items: [] },
submissionSummary: { total: 0 },
fundingObservations: [],
recentTradeDecisions: [],
recentExecuteTradeCommands: [],
recentExecutionResults: [],
recentAlertTransitions: [],
serviceSnapshots: [],
});
assert.deepEqual(bootstrap.funds.balances.items.map((item) => item.asset_id), [
'nep141:nbtc.bridge.near',
'nep141:btc.omft.near',
'nep141:eure.omft.near',
]);
assert.equal(
bootstrap.strategy.asset_catalog.items.some((item) => item.asset_id === importedOnlyAsset.assetId),
true,
);
});
test('bootstrap keeps no-trade counts without shipping non-rendered row payloads', () => {
const config = buildConfig();
const bootstrap = buildDashboardBootstrap({
config,
inventorySnapshot: null,
marketPrice: null,
recentQuotes: [],
submissionPage: { page: 1, page_size: 20, total: 0, total_pages: 1, items: [] },
submissionSummary: { total: 0 },
fundingObservations: [],
recentTradeDecisions: [{
observed_at: '2026-05-12T19:40:00.000Z',
payload: {
decision_id: 'decision-rejected',
quote_id: 'quote-rejected',
pair: config.activePair,
decision: 'rejected',
decision_reason: 'below_threshold',
raw_solver_payload: Object.fromEntries(Array.from({ length: 100 }, (_, index) => [`field_${index}`, index])),
},
}],
recentExecuteTradeCommands: [],
recentExecutionResults: [],
recentAlertTransitions: [],
serviceSnapshots: [],
});
const funnel = bootstrap.strategy.strategy_state.trade_funnel;
assert.equal(funnel.no_trade_count, 1);
assert.deepEqual(funnel.no_trade_rows, []);
});
test('bootstrap normalizes actionable decision vocabulary before exposing it to the dashboard', () => { test('bootstrap normalizes actionable decision vocabulary before exposing it to the dashboard', () => {
const config = buildConfig(); const config = buildConfig();
const bootstrap = buildDashboardBootstrap({ const bootstrap = buildDashboardBootstrap({