@bongos/core 1.24.36 → 1.24.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.bongos-core.json +90 -85
  2. package/docs/copy-inventory.md +58 -57
  3. package/docs/copy-registry.json +163 -166
  4. package/docs/module-api-changelog.md +2 -0
  5. package/docs/onboarding/primer.md +2 -0
  6. package/docs/page-inventory.json +34 -34
  7. package/docs/page-readings.json +684 -680
  8. package/modules/hall-ui/public/ai-studio.html +2 -2
  9. package/modules/hall-ui/public/atlas.html +2 -2
  10. package/modules/hall-ui/public/blockers.html +2 -2
  11. package/modules/hall-ui/public/board-room.html +2 -2
  12. package/modules/hall-ui/public/city.html +2 -2
  13. package/modules/hall-ui/public/collab.html +2 -2
  14. package/modules/hall-ui/public/commands.html +2 -2
  15. package/modules/hall-ui/public/copy-desk.html +2 -2
  16. package/modules/hall-ui/public/craft-mode.js +1 -1
  17. package/modules/hall-ui/public/deploy.html +2 -2
  18. package/modules/hall-ui/public/fleet-costs.js +3 -2
  19. package/modules/hall-ui/public/gate.html +2 -2
  20. package/modules/hall-ui/public/goal-map.html +2 -2
  21. package/modules/hall-ui/public/goals.html +2 -2
  22. package/modules/hall-ui/public/government.html +2 -2
  23. package/modules/hall-ui/public/idea.html +2 -2
  24. package/modules/hall-ui/public/ideas.html +2 -2
  25. package/modules/hall-ui/public/index.html +2 -2
  26. package/modules/hall-ui/public/market-watch-item.html +2 -2
  27. package/modules/hall-ui/public/market-watch-rules.html +2 -2
  28. package/modules/hall-ui/public/market-watch.html +2 -2
  29. package/modules/hall-ui/public/modules.html +2 -2
  30. package/modules/hall-ui/public/money.html +92 -0
  31. package/modules/hall-ui/public/money.js +302 -0
  32. package/modules/hall-ui/public/money.states.json +15 -0
  33. package/modules/hall-ui/public/my-agents.html +2 -2
  34. package/modules/hall-ui/public/palette.js +9 -2
  35. package/modules/hall-ui/public/primer-live.js +18 -105
  36. package/modules/hall-ui/public/primer.html +7 -8
  37. package/modules/hall-ui/public/primer.js +16 -18
  38. package/modules/hall-ui/public/profile.html +2 -2
  39. package/modules/hall-ui/public/project-settings.css +2 -1
  40. package/modules/hall-ui/public/project-settings.html +6 -16
  41. package/modules/hall-ui/public/project-settings.js +2 -142
  42. package/modules/hall-ui/public/roadmap.html +2 -2
  43. package/modules/hall-ui/public/roster.html +2 -2
  44. package/modules/hall-ui/public/sessions.html +2 -2
  45. package/modules/hall-ui/public/settings.html +2 -2
  46. package/modules/hall-ui/public/shell.js +7 -5
  47. package/modules/hall-ui/public/street.html +2 -2
  48. package/modules/hall-ui/public/studio.html +2 -2
  49. package/modules/hall-ui/public/task.html +2 -2
  50. package/modules/hall-ui/public/thinking.html +2 -2
  51. package/modules/hall-ui/public/tweak-editor.html +2 -2
  52. package/modules/hall-ui/public/watch.html +2 -2
  53. package/modules/hall-ui/public/work.html +2 -2
  54. package/package-lock.json +2 -2
  55. package/package.json +1 -1
  56. package/release-notes.json +6 -0
  57. package/src/bongos/auth.js +0 -8
  58. package/src/bongos/serve-internal.js +15 -6
  59. package/src/module-api.js +1 -1
  60. package/tests/auth_resolve_failure.mjs +5 -4
  61. package/tests/fleet_cost_view.mjs +2 -18
  62. package/tests/hall_loading.mjs +1 -1
  63. package/tests/hall_money_page.mjs +272 -0
  64. package/tests/hall_nav.mjs +5 -4
  65. package/tests/hall_oversight_world.mjs +1 -1
  66. package/tests/hall_page_gate_map.mjs +3 -2
  67. package/tests/hall_reading_room.mjs +21 -62
  68. package/tests/nav_permission_atoms.mjs +1 -1
  69. package/modules/hall-ui/public/fleet.html +0 -69
  70. package/modules/hall-ui/public/fleet.js +0 -41
  71. package/modules/hall-ui/public/fleet.states.json +0 -14
@@ -49,19 +49,9 @@
49
49
  server resolved it through, and markup that could drift from that is the
50
50
  thing this page exists to end. -->
51
51
  <div id="ps-rows"><span class="kit-skel"></span></div>
52
- </section>
53
- <!-- Where the money went (task 1003184, criterion money-outflow-visible). Hidden
54
- unless GET /project-account/outflow answers — it is the payout authority's
55
- read (payout.approve, the owner), so every other reader of this page never
56
- sees an empty panel. Written by project-settings.js; every figure is the
57
- server's, summed from records that already exist. -->
58
- <section class="scroll" id="ps-money" aria-labelledby="ps-money-h" hidden>
59
- <h2 class="scroll__h" id="ps-money-h">Where the money went</h2>
60
- <p class="scroll__lede">
61
- What this project has spent, what each builder has been paid, and what it has
62
- promised but not paid yet. A balance only means something after those promises.
63
- </p>
64
- <div id="ps-money-body"></div>
52
+ <!-- task 1005047: the money figures this page drew ("Where the money went",
53
+ task 1003184) moved to the one money page; this page keeps its knobs. -->
54
+ <p class="ps-prov" id="ps-money-link">What this project has spent and paid out, its treasury and its hosting bills are on the <a href="/builders/money">Money</a> page.</p>
65
55
  </section>
66
56
  <!-- Danger zone (task 1004570): where this project's OWNER takes it offline or
67
57
  disconnects it. Both controls live on the platform that hosts the project,
@@ -86,11 +76,11 @@
86
76
 
87
77
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
88
78
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
89
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
90
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
79
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
80
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
91
81
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
92
82
  <script src="/builders/palette.js" defer></script>
93
83
  <script src="/builders/hall-kit.js"></script>
94
- <script src="/builders/project-settings.js?v=2026-11-02-outflow"></script>
84
+ <script src="/builders/project-settings.js?v=2026-11-04-money"></script>
95
85
  </body>
96
86
  </html>
@@ -417,148 +417,8 @@
417
417
  $('ps-danger').hidden = false;
418
418
  }
419
419
 
420
- // ---- where the money went (task 1003184, criterion money-outflow-visible) --------
421
- //
422
- // The owner's outflow view. The server sums every figure (modules/economy/outflow.js);
423
- // this only draws it. It leads with what is left AFTER what is owed, because a balance
424
- // that looks healthy while it owes three payouts is the trap the view exists to show.
425
- // Any outcome but a 200 leaves the section hidden: the read is the payout authority's,
426
- // and everyone else on this page has nothing to see here, not an error.
427
-
428
- const money = (micro) => {
429
- const usd = Number(micro || 0) / 1e6;
430
- const abs = Math.abs(usd).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
431
- return `${usd < 0 ? '−' : ''}$${abs}`;
432
- };
433
- const monthLabel = (ym) => {
434
- const [y, m] = String(ym).split('-').map(Number);
435
- return new Date(Date.UTC(y, m - 1, 1)).toLocaleDateString(undefined, { month: 'short', year: 'numeric', timeZone: 'UTC' });
436
- };
437
- const dayLabel = (iso) => (iso ? new Date(iso).toLocaleDateString() : '');
438
-
439
- function moneyFiguresHtml(out) {
440
- const c = out.committed;
441
- const after = out.balance.after_commitments_micro;
442
- const fig = (label, value, note, tone) => `<div class="ps-money__fig${tone ? ` ps-money__fig--${tone}` : ''}">
443
- <span class="ps-money__label">${escapeHtml(label)}</span>
444
- <strong class="ps-money__value">${escapeHtml(value)}</strong>
445
- <span class="ps-money__note">${escapeHtml(note)}</span>
446
- </div>`;
447
- const owedNote = c.owed_count === 1 ? '1 payment' : `${c.owed_count} payments`;
448
- const apprNote = c.approved_count === 1 ? '1 approval' : `${c.approved_count} approvals`;
449
- return `<div class="ps-money__figs">
450
- ${fig('Balance', money(out.balance.available_micro), c.held_micro ? `after ${money(c.held_micro)} held` : 'on our record')}
451
- ${fig('Owed but not paid', money(c.owed_micro), owedNote, c.owed_micro ? 'warn' : '')}
452
- ${fig('Approved, not started', money(c.approved_micro), apprNote, c.approved_micro ? 'warn' : '')}
453
- ${fig('Left after all of that', money(after), after < 0 ? 'the project owes more than it holds' : 'what is really free', after < 0 ? 'bad' : '')}
454
- </div>${c.other_currency_count ? `<p class="ps-prov">${c.other_currency_count} more in a currency other than US dollars, not added in.</p>` : ''}`;
455
- }
456
-
457
- // The payout's latest try in words. A reason is either the processor's own sentence, shown
458
- // as it is, or one of core's held codes (builder-payouts.js HELD_WORDS). The codes an owner
459
- // can act on get a short phrase here; any other code is left out rather than shown as a slug.
460
- const TRY_WORDS = { held: 'Waiting', failed: 'Did not go through' };
461
- const REASON_WORDS = {
462
- no_processor: 'no payment processor connected yet',
463
- recipient_gated: 'this person cannot be paid right now',
464
- guardian_cosign_missing: 'a guardian has not co-signed yet',
465
- currency_not_supported: 'not in US dollars',
466
- };
467
- function lastTryWords(r) {
468
- if (!r.last_outcome) return 'Not tried yet';
469
- const reason = String(r.last_reason || '');
470
- const why = REASON_WORDS[reason] || (/^[a-z_]+$/.test(reason) ? '' : reason);
471
- return `${TRY_WORDS[r.last_outcome] || r.last_outcome}${why ? ` — ${why}` : ''}`;
472
- }
473
-
474
- function owedHtml(out) {
475
- const rows = out.committed.owed;
476
- if (!rows.length) return '';
477
- return `<h3 class="ps-money__h">Owed but not paid</h3>
478
- <div class="ps-money__wrap"><table class="ledger">
479
- <thead><tr><th>Builder</th><th>Task</th><th>Amount</th><th>Owed since</th><th>Last try</th></tr></thead>
480
- <tbody>${rows.map((r) => `<tr>
481
- <td>${escapeHtml(r.builder_name || `builder ${r.builder_id}`)}</td>
482
- <td><a href="/builders/task/${encodeURIComponent(r.task_id)}">${escapeHtml(r.task_title || `task ${r.task_id}`)}</a></td>
483
- <td>${escapeHtml(money(r.amount_micro))}</td>
484
- <td>${escapeHtml(dayLabel(r.since))}</td>
485
- <td>${escapeHtml(lastTryWords(r))}</td>
486
- </tr>`).join('')}</tbody>
487
- </table></div>`;
488
- }
489
-
490
- function buildersHtml(out) {
491
- const rows = out.by_builder;
492
- if (!rows.length) return '';
493
- return `<h3 class="ps-money__h">By builder</h3>
494
- <div class="ps-money__wrap"><table class="ledger">
495
- <thead><tr><th>Builder</th><th>Spent</th><th>Paid</th><th>Owed</th><th>Approved</th></tr></thead>
496
- <tbody>${rows.map((b) => `<tr>
497
- <td>${escapeHtml(b.builder_name || `builder ${b.builder_id}`)}</td>
498
- <td>${escapeHtml(money(b.spend_micro))}</td>
499
- <td>${escapeHtml(money(b.paid_micro))}</td>
500
- <td>${escapeHtml(money(b.owed_micro))}</td>
501
- <td>${escapeHtml(money(b.approved_micro))}</td>
502
- </tr>`).join('')}</tbody>
503
- </table></div>
504
- <p class="ps-prov">Spent is the recorded cost of each builder’s sessions (API, compute). Paid, owed and approved are payouts.</p>`;
505
- }
506
-
507
- function burnHtml(out) {
508
- const months = out.burn.months;
509
- if (!months.some((m) => m.total_micro > 0)) return '';
510
- const bars = window.OTBKit.barSeriesHtml(
511
- months.map((m) => ({ label: monthLabel(m.month), value: m.total_micro, valueLabel: money(m.total_micro) })),
512
- { list: true, ariaLabel: 'Money out by month', valueWidth: '96px' });
513
- return `<h3 class="ps-money__h">Money out, by month</h3>${bars}
514
- <p class="ps-prov">Each month is recorded spend (${escapeHtml(money(months.reduce((s, m) => s + m.spend_micro, 0)))} over these ${months.length} months) plus payouts and fees out of the project account. Read from the cost log and the account’s own record.</p>`;
515
- }
516
-
517
- // The daily check of our record against the processor's (task 1004912). The newest check
518
- // in words, and on a difference each one with both amounts. The blocker it raises is public
519
- // and carries no numbers, so this is where the owner reads them.
520
- const CHECK_WORDS = {
521
- clean: 'Our records and the payment processor’s agree.',
522
- drift: 'Our records and the payment processor’s differ.',
523
- unchecked: 'Not checked: no payment processor is connected yet, so there is nothing to compare with.',
524
- error: 'Could not check: the payment processor could not be read.',
525
- };
526
- function ledgerCheckHtml(out) {
527
- const c = out && out.checks && out.checks[0];
528
- if (!c) return '';
529
- const words = out.kind_words || {};
530
- const amt = (v) => (v === null || v === undefined ? '—' : money(v));
531
- const rows = (c.findings || []).map((f) => `<tr>
532
- <td>${escapeHtml(words[f.kind] || f.kind)}</td>
533
- <td>${escapeHtml(f.ref || 'the project balance')}</td>
534
- <td>${escapeHtml(amt(f.ours_micro))}</td>
535
- <td>${escapeHtml(amt(f.theirs_micro))}</td>
536
- </tr>`).join('');
537
- return `<h3 class="ps-money__h">Daily check against the payment processor</h3>
538
- <p class="ps-prov">${escapeHtml(dayLabel(c.created_at))}: ${escapeHtml(CHECK_WORDS[c.outcome] || c.outcome)}${c.outcome === 'clean' || c.outcome === 'drift' ? ` ${c.refs_compared} ${c.refs_compared === 1 ? 'movement' : 'movements'} compared.` : ''}</p>
539
- ${rows ? `<div class="ps-money__wrap"><table class="ledger">
540
- <thead><tr><th>Difference</th><th>Reference</th><th>Our record</th><th>Processor</th></tr></thead>
541
- <tbody>${rows}</tbody>
542
- </table></div>` : ''}`;
543
- }
544
-
545
- async function loadMoney() {
546
- let out = null;
547
- try {
548
- const res = await api.request('GET', `${API}/project-account/outflow`);
549
- out = res.ok ? res.data : null;
550
- } catch (_) { out = null; }
551
- if (!out || !out.balance || !out.committed) return;
552
- let checks = null;
553
- try {
554
- const res = await api.request('GET', `${API}/project-account/ledger-checks?limit=1`);
555
- checks = res.ok ? res.data : null;
556
- } catch (_) { checks = null; }
557
- $('ps-money-body').innerHTML = moneyFiguresHtml(out) + ledgerCheckHtml(checks) + owedHtml(out) + buildersHtml(out) + burnHtml(out);
558
- $('ps-money').hidden = false;
559
- }
560
-
420
+ // task 1005047: "Where the money went" (task 1003184) moved to money.js, the one
421
+ // money page; the section's link is in project-settings.html.
561
422
  load();
562
- loadMoney();
563
423
  loadDanger();
564
424
  })();
@@ -43,8 +43,8 @@
43
43
 
44
44
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
45
45
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
46
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
47
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
46
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
47
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
48
48
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
49
49
  <script src="/builders/palette.js" defer></script>
50
50
  <!-- The chart kit and the timeline's series builders (task 1003447). Both MUST
@@ -55,8 +55,8 @@
55
55
  chrome exists before the page script runs. -->
56
56
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
57
57
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
58
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
59
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
58
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
59
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
60
60
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
61
61
  <script src="/builders/palette.js" defer></script>
62
62
  <!-- the branding pack's currency label, loaded BEFORE roster.js. -->
@@ -113,8 +113,8 @@
113
113
  chrome exists before the page script runs. -->
114
114
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
115
115
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
116
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
117
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
116
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
117
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
118
118
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
119
119
  <script src="/builders/palette.js" defer></script>
120
120
  <script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
@@ -393,8 +393,8 @@
393
393
  chrome exists before the page scripts run. -->
394
394
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
395
395
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
396
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
397
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
396
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
397
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
398
398
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
399
399
  <script src="/builders/palette.js" defer></script>
400
400
  <!-- shared component kit (window.OTBKit) — section rail (task 2166). -->
@@ -122,7 +122,7 @@
122
122
  sessions: '<path d="M7 5h8M7 9h8M7 13h8"/><circle cx="3.8" cy="5" r=".7" fill="currentColor"/><circle cx="3.8" cy="9" r=".7" fill="currentColor"/><circle cx="3.8" cy="13" r=".7" fill="currentColor"/>',
123
123
  settings: '<circle cx="9" cy="9" r="2.5"/><path d="M9 2.5v2m0 9v2M2.5 9h2m9 0h2M4.4 4.4l1.4 1.4m6.4 6.4 1.4 1.4m0-9.2-1.4 1.4M5.8 12.2l-1.4 1.4"/>',
124
124
  deploy: '<path d="M9 12V3M5.5 6.5 9 3l3.5 3.5M3 12.5v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-2"/>',
125
- fleet: '<path d="M4 2.5h10v13l-1.7-1-1.6 1-1.7-1-1.7 1-1.6-1-1.7 1v-13Z"/><path d="M6.5 6h5M6.5 9h5M6.5 12h3"/>',
125
+ money: '<path d="M4 2.5h10v13l-1.7-1-1.6 1-1.7-1-1.7 1-1.6-1-1.7 1v-13Z"/><path d="M6.5 6h5M6.5 9h5M6.5 12h3"/>',
126
126
  permissions: '<circle cx="6" cy="12" r="3.5"/><path d="M8.5 9.5 15 3M12.5 5.5l2 2M10.5 7.5l1.5 1.5"/>',
127
127
  // The governor city: a pediment over columns, the Governing mode's own glyph.
128
128
  city: '<path d="M2.5 7 9 3l6.5 4M4 7.5v6.5M7 7.5v6.5M11 7.5v6.5M14 7.5v6.5M2.5 15.5h13"/>',
@@ -367,6 +367,12 @@
367
367
  // The project's own fundamentals (task 1003280, goal 1000072). Mirrors the
368
368
  // server's requireGovernmentPage floor; cosmetic only, as every item here is.
369
369
  { id: 'project-settings', label: 'Project settings', icon: 'settings', href: '/project-settings', gate: 'government', position: true },
370
+ // task 1005047 (owner decision 2026-11-03, idea 1001312): the owner's one money page —
371
+ // spend and payouts (once on Project settings), the treasury (once the Economy page)
372
+ // and the hosting bills (once 'Fleet costs', /fleet, which now redirects to its
373
+ // #hosting section). It mirrors the server's requireGovernmentPage floor; every
374
+ // section on it is hidden unless its own read answers, so the wall stays per route.
375
+ { id: 'money', label: 'Money', icon: 'money', href: '/money', gate: 'government', position: true },
370
376
  // The deploy door (task 1003159, ADR 0293). Gated 'archon' here to mirror the
371
377
  // `core.pin.move` atom its page and both its routes check — cosmetic only, as
372
378
  // every item in this list is; the wall is server-side.
@@ -377,10 +383,6 @@
377
383
  // is the platform hall's page alone, and every other hall takes its update from
378
384
  // Settings → Software update, whose Update button carries the hub hand-off.
379
385
  { id: 'deploy', label: 'Deploy', icon: 'deploy', href: '/deploy', gate: 'archon', module: 'provisioning render-deploy', position: true },
380
- // task 1001949: what each hosted project costs. 'government' mirrors the metic floor
381
- // of `provisioning.fleet.manage`, the atom its page and its one read check (the
382
- // Deploy precedent above); dropped where the provisioning module is not enabled.
383
- { id: 'fleet', label: 'Fleet costs', icon: 'fleet', href: '/fleet', gate: 'government', module: 'provisioning', position: true },
384
386
  // task 1005045: the owner's Market Watch settings (task 1004938) are an owner-only tab on
385
387
  // /market-watch now, so the sidebar says "Market Watch" once (owner decision 2026-11-03).
386
388
  // BV1.R113: the roles-and-permissions tab (ADR 0151). The one genuinely
@@ -230,8 +230,8 @@
230
230
 
231
231
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
232
232
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
233
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
234
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
233
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
234
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
235
235
  <script src="/builders/palette.js" defer></script>
236
236
  <script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
237
237
  <script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
@@ -224,8 +224,8 @@
224
224
 
225
225
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
226
226
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
227
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
228
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
227
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
228
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
229
229
  <script src="/builders/palette.js" defer></script>
230
230
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
231
231
  (task 1003829), which the older reviews still show. -->
@@ -58,8 +58,8 @@
58
58
  time so the chrome exists before the deferred page scripts execute. -->
59
59
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
60
60
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
61
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
62
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
61
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
62
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
63
63
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
64
  <script src="/builders/palette.js" defer></script>
65
65
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N deep-link overlay
@@ -140,8 +140,8 @@
140
140
 
141
141
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
142
142
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
143
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
144
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
143
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
144
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
145
145
  <script src="/builders/palette.js" defer></script>
146
146
 
147
147
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -144,8 +144,8 @@
144
144
 
145
145
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
146
146
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
147
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
148
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
147
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
148
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
149
149
  <script src="/builders/palette.js" defer></script>
150
150
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
151
151
  <script src="/builders/tweak-editor-lib.js?v=2026-09-29-tw27"></script>
@@ -167,8 +167,8 @@
167
167
  chrome exists before the page script runs. -->
168
168
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
169
169
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
170
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
171
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
170
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
171
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
172
172
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
173
173
  <script src="/builders/palette.js" defer></script>
174
174
  <!-- shared component kit (window.OTBKit) — tabs, ledger, pager (task 2166). -->
@@ -173,8 +173,8 @@
173
173
  time so the chrome exists before the deferred page scripts execute. -->
174
174
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
175
175
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
176
- <script src="/builders/craft-mode.js?v=2026-11-04-reading-room"></script>
177
- <script src="/builders/shell.js?v=2026-11-04-reading-room"></script>
176
+ <script src="/builders/craft-mode.js?v=2026-11-04-money"></script>
177
+ <script src="/builders/shell.js?v=2026-11-04-money"></script>
178
178
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
179
179
  <script src="/builders/palette.js" defer></script>
180
180
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.36",
3
+ "version": "1.24.37",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.24.36",
9
+ "version": "1.24.37",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.36",
3
+ "version": "1.24.37",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -11179,5 +11179,11 @@
11179
11179
  "id": "1005046",
11180
11180
  "text": "The Docs section now has one reading room instead of four pages: ranks, credits and karma, and the five diagrams all live in the Primer, each topic explained once with its picture beside it. Old links to Ranks, Economy and Dia"
11181
11181
  }
11182
+ ],
11183
+ "1.24.37": [
11184
+ {
11185
+ "id": "1005047",
11186
+ "text": "Owners now have one Money page in the Government group: what the project spent and paid out, its treasury, and what each hosted project costs, all in one place. Project settings and the Primer link to it instead of repeating t"
11187
+ }
11182
11188
  ]
11183
11189
  }
@@ -406,10 +406,6 @@ const PAGE_GATES = Object.freeze({
406
406
  // live project; which project they may read or move is still the routes' question,
407
407
  // asked per request against that project's row.
408
408
  requireCorePinPage: Object.freeze({ permission: 'core.pin.move', deniedTo: '/builders', alsoAdmits: 'isProjectOwner' }),
409
- // The /fleet cost page (task 1001949) — on `provisioning.fleet.manage`, the SAME atom
410
- // its one read (GET /provisioning/fleet/cost-ledger) checks: every owner's projects
411
- // and what they cost, so the page reaches exactly as far as the data does.
412
- requireFleetPage: Object.freeze({ permission: 'provisioning.fleet.manage', deniedTo: '/builders' }),
413
409
  });
414
410
 
415
411
  // The `alsoAdmits` door — "is this visitor the page's OTHER audience?", asked of
@@ -568,9 +564,6 @@ async function requireBoardVotePage(req, res, next) {
568
564
  async function requireCorePinPage(req, res, next) {
569
565
  return runPageGate('requireCorePinPage', req, res, next);
570
566
  }
571
- async function requireFleetPage(req, res, next) {
572
- return runPageGate('requireFleetPage', req, res, next);
573
- }
574
567
 
575
568
  // ---------------------------------------------------------------------------
576
569
  // Route-gate inventory (V3.R06 #231) — the canonical "who can call what" list.
@@ -862,7 +855,6 @@ module.exports = {
862
855
  requireGovernmentPage,
863
856
  requireBoardVotePage,
864
857
  requireCorePinPage,
865
- requireFleetPage,
866
858
  requireRank,
867
859
  rankMeetsThreshold,
868
860
  requirePermission,
@@ -1010,8 +1010,9 @@ function mountInternalSurfaces(app) {
1010
1010
  // statically, and a comment in that gap makes the page test as UNGATED while
1011
1011
  // looking gated here. Prose goes above the const, as here.
1012
1012
  const DEPLOY_PAGE_RE = /^\/builders\/deploy(?:\.html)?\/?$/;
1013
- // The fleet cost page (task 1001949), on the atom its one read checks.
1014
- const FLEET_PAGE_RE = /^\/builders\/fleet(?:\.html)?\/?$/;
1013
+ // /money — the owner's one money page (task 1005047): the government floor; each
1014
+ // section's read keeps its own wall. NB as at PROJECT_SETTINGS_PAGE_RE.
1015
+ const MONEY_PAGE_RE = /^\/builders\/money(?:\.html)?\/?$/;
1015
1016
  // /people and its legacy twin /scouting were the hall's cross-project builder
1016
1017
  // directory. Task 1003440 deleted the /scouting door; task 1003444 deleted
1017
1018
  // /people itself (owner call, 2026-09-25 — ADR 0337 D5): cross-project
@@ -1048,6 +1049,16 @@ function mountInternalSurfaces(app) {
1048
1049
  return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/market-watch${qs}#settings`);
1049
1050
  });
1050
1051
 
1052
+ // task 1005047: Fleet costs is /money's #hosting section now; 302 and no gate, as above.
1053
+ const RETIRED_FLEET_RE = /^\/builders\/fleet(?:\.html)?\/?$/;
1054
+ app.use((req, res, next) => {
1055
+ if (isModuleSurfaceHost(req)) return next();
1056
+ if (req.method !== 'GET' && req.method !== 'HEAD') return next();
1057
+ if (!RETIRED_FLEET_RE.test(path.posix.normalize(req.path))) return next();
1058
+ const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
1059
+ return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/money${qs}#hosting`);
1060
+ });
1061
+
1051
1062
  // task 1005046 (owner decision 2026-11-03, idea 1001311): ONE reading room. Ranks,
1052
1063
  // Economy (/drachmae) and Diagrams explained what the Primer already explained, so
1053
1064
  // they are Primer chapters now, each with its diagram, and each old URL lands on the
@@ -1094,7 +1105,8 @@ function mountInternalSurfaces(app) {
1094
1105
  if (!req.path.startsWith('/builders')) return next();
1095
1106
  const norm = path.posix.normalize(req.path);
1096
1107
  if (WATCH_PAGE_RE.test(norm) || GATE_PAGE_RE.test(norm)
1097
- || SESSIONS_PAGE_RE.test(norm) || PROJECT_SETTINGS_PAGE_RE.test(norm)) {
1108
+ || SESSIONS_PAGE_RE.test(norm) || PROJECT_SETTINGS_PAGE_RE.test(norm)
1109
+ || MONEY_PAGE_RE.test(norm)) {
1098
1110
  return gdsAuth.requireGovernmentPage(req, res, next);
1099
1111
  }
1100
1112
  // R16 (goal 1000069) widened the government shell from the manage atom
@@ -1125,9 +1137,6 @@ function mountInternalSurfaces(app) {
1125
1137
  if (DEPLOY_PAGE_RE.test(norm)) {
1126
1138
  return gdsAuth.requireCorePinPage(req, res, next);
1127
1139
  }
1128
- if (FLEET_PAGE_RE.test(norm)) {
1129
- return gdsAuth.requireFleetPage(req, res, next);
1130
- }
1131
1140
  next();
1132
1141
  });
1133
1142
 
package/src/module-api.js CHANGED
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
81
81
  // MAJOR (see allowBoxScope below): passes the request through untouched.
82
82
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
83
83
 
84
- const CORE_VERSION = '1.24.36'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.24.37'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
85
85
 
86
86
  // A namespaced logger so a module's log lines are attributable + consistent.
87
87
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -97,18 +97,19 @@ test('AUDIT: every async middleware mounted directly on a router owns its reject
97
97
  // requireBoardVotePage / BV1.R107 (and the ADR 0266 /board-room vote gate)
98
98
  // requireCorePinPage / ADR 0293's /deploy door — the narrowest of them,
99
99
  // on the archon-floor `core.pin.move` atom.
100
- // requireFleetPage — task 1001949's /fleet cost page, same delegation.
101
- // Task 1003450 removed two more — requireNonXenosPage and the R113
100
+ // Task 1005047 removed requireFleetPage with the /fleet page it fronted (its
101
+ // ledger is a section of /money now, behind the government page floor and the
102
+ // route's own atom). Task 1003450 removed two more — requireNonXenosPage and the R113
102
103
  // requireGovernmentManagePage — which had both ended up dispatched from no
103
104
  // route at all; tests/hall_page_gate_map.mjs now fails if that recurs. Task
104
105
  // 1003444 removed requireProjectCuratePage with the /people page it fronted.
105
106
  // Anything else async and router-mounted must be added with its own try/catch.
106
107
  const src = fs.readFileSync(path.join(ROOT, 'src', 'bongos', 'auth.js'), 'utf8');
107
108
  const asyncMw = [...src.matchAll(/^async function (require\w+|allow\w+)\s*\(req, res, next\)/gm)].map((m) => m[1]);
108
- assert.deepEqual(asyncMw.sort(), ['requireBoardVotePage', 'requireBuilder', 'requireBuilderPage', 'requireCorePinPage', 'requireFleetPage', 'requireGovernmentPage'],
109
+ assert.deepEqual(asyncMw.sort(), ['requireBoardVotePage', 'requireBuilder', 'requireBuilderPage', 'requireCorePinPage', 'requireGovernmentPage'],
109
110
  'a new async router middleware appeared — give it a try/catch and add it here');
110
111
  // The page gates must actually delegate (that is WHY they are safe).
111
- for (const name of ['requireBuilderPage', 'requireGovernmentPage', 'requireBoardVotePage', 'requireCorePinPage', 'requireFleetPage']) {
112
+ for (const name of ['requireBuilderPage', 'requireGovernmentPage', 'requireBoardVotePage', 'requireCorePinPage']) {
112
113
  const fn = src.slice(src.indexOf(`async function ${name}`));
113
114
  assert.match(sliceFn(fn), /runPageGate\(/, `${name} must delegate to the guarded runPageGate`);
114
115
  }
@@ -103,24 +103,8 @@ test('no projects renders a sentence, not an empty table', () => {
103
103
  assert.match(costs.ledgerHtml(null), /No projects are hosted here yet/);
104
104
  });
105
105
 
106
- // ── the page: its wiring and its gate ────────────────────────────────────────
107
- test('the page loads the builders before its chrome and reads the ledger route', () => {
108
- const html = read(path.join(HALL, 'fleet.html'));
109
- assert.ok(html.indexOf('fleet-costs.js') !== -1 && html.indexOf('fleet-costs.js') < html.indexOf('/fleet.js'), 'fleet-costs.js loads first');
110
- const js = read(path.join(HALL, 'fleet.js'));
111
- assert.match(js, /\/provisioning\/fleet\/cost-ledger/);
112
- assert.match(js, /status === 403[\s\S]{0,200}fl-sealed/, 'a refusal shows the sealed state');
113
- });
114
-
115
- test('the page gate is the atom the ledger route checks, and the nav mirrors its floor', () => {
116
- const route = read(path.join(ROOT, 'modules', 'provisioning', 'routes', 'provisioning.js'));
117
- assert.match(route, /router\.get\('\/provisioning\/fleet\/cost-ledger', auth\.requireBuilder, auth\.requirePermission\('provisioning\.fleet\.manage'\)/);
118
- assert.match(read(path.join(ROOT, 'src', 'bongos', 'auth.js')), /requireFleetPage: Object\.freeze\(\{ permission: 'provisioning\.fleet\.manage'/);
119
- // The nav is cosmetic (the page gate is the wall); 'government' is the metic floor.
120
- const { byKey } = require(path.join(ROOT, 'modules', 'government', 'catalog.js'));
121
- assert.equal(byKey('provisioning.fleet.manage').floor, 'metic');
122
- assert.match(read(path.join(HALL, 'shell.js')), /href: '\/fleet', gate: 'government', module: 'provisioning'/);
123
- });
106
+ // The page's wiring and its gate moved to tests/hall_money_page.mjs with the page
107
+ // (task 1005047): the ledger is the money page's #hosting section, and /fleet redirects.
124
108
 
125
109
  // ── the owner's line on their own project page ───────────────────────────────
126
110
  const HUB = read(path.join(ROOT, 'modules', 'public-landing', 'public', 'projects.html'));
@@ -83,7 +83,7 @@ test('the page loaders step aside by themselves, and never show without :has()',
83
83
  assert.match(STYLE, /\.hall-loading--wait, \.hall-loading--zones \{ display: none; \}/);
84
84
  assert.match(STYLE, /@supports selector\(:has\(a\)\) \{\s*\.hall-loading--wait:not\(:has\(~ :not\(\[hidden\], script, template, style\)\)\) \{ display: flex; \}\s*\.hall-loading--zones:not\(:has\(~ :not\(\[hidden\]\) > :not\(\[hidden\], script, template, style\)\)\) \{ display: flex; \}\s*\}/);
85
85
  // A page whose sections all start [hidden] carries the --wait loader FIRST in its main.
86
- for (const page of ['board-room', 'deploy', 'fleet', 'gate', 'government', 'project-settings', 'sessions']) {
86
+ for (const page of ['board-room', 'deploy', 'gate', 'government', 'money', 'project-settings', 'sessions']) {
87
87
  const html = read(`${page}.html`);
88
88
  const main = html.slice(html.indexOf('<main id="hall-main">'));
89
89
  const first = main.replace(/<main id="hall-main">\s*(<!--[\s\S]*?-->\s*)*/, '');