@bongos/core 1.19.1000 → 1.19.1001

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.
package/.bongos-core.json CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.1000",
6
- "core_contract": "1.19.1000",
7
- "source_commit": "418cf03d39cecaf2d846caa21fdb61edb6d8b0f3",
5
+ "core_version": "1.19.1001",
6
+ "core_contract": "1.19.1001",
7
+ "source_commit": "e3841cdcf855bc3c78235929cb9b991fe6771293",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-24T01:30:19.307Z",
9
+ "built_at": "2026-09-24T14:59:23.930Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 521,
13
13
  "agent_docs_stubbed": 26,
14
- "functional_verbatim": 2408,
14
+ "functional_verbatim": 2410,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2955,
20
- "tree_sha256": "7199a5097f99b2342ed5b37fc07050fa409594d5d006e6a72c3228e7b3f2f7dd",
19
+ "file_count": 2957,
20
+ "tree_sha256": "316e3f355b647effab194c60ce7bd7be3e67446b6c021d7bf3c163987ac4a2a6",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2157,12 +2157,12 @@
2157
2157
  {
2158
2158
  "path": "docs/api-reference.md",
2159
2159
  "mode": "0000644",
2160
- "sha256": "a92aafc05e0bf1434d96a9032b5b2d4b1d13177d9969ee02d9f6287777a64df7"
2160
+ "sha256": "20ab80e06f03ad1c1f1f33e3f804d402d011bca4c7b2b9df0090269958009d92"
2161
2161
  },
2162
2162
  {
2163
2163
  "path": "docs/api/openapi.json",
2164
2164
  "mode": "0000644",
2165
- "sha256": "36beb96ae7c9b5a49e02252ff10b222cfae14224eaf5ed639d7515f36a3f1f73"
2165
+ "sha256": "51f4a9dd2fcd6d34bd3eaa08285acf9b7c9c1ccc294d1e616c1338607701439e"
2166
2166
  },
2167
2167
  {
2168
2168
  "path": "docs/architecture.md",
@@ -2722,7 +2722,7 @@
2722
2722
  {
2723
2723
  "path": "docs/module-api-changelog.md",
2724
2724
  "mode": "0000644",
2725
- "sha256": "a9c208d7638a739dbbe12a7b1ee4a52e4b3c4ad047a043ccf5e3e4ae262eb805"
2725
+ "sha256": "9c37bc89b41410876a2d54f99781ff465a2cd106fd3cd8ef6502bb097e832afe"
2726
2726
  },
2727
2727
  {
2728
2728
  "path": "docs/modules-contract.md",
@@ -6634,6 +6634,11 @@
6634
6634
  "mode": "0000644",
6635
6635
  "sha256": "d2ff6733aaab0c009cfe2e90e756fa9742193dba7c5078c5e1d5fa2fd6ef6e8a"
6636
6636
  },
6637
+ {
6638
+ "path": "modules/provisioning/cost-ledger.js",
6639
+ "mode": "0000644",
6640
+ "sha256": "0f6a25220ce3786ba3d813bcba2e39ef902aa25ed4420264efaca36b9ddb3432"
6641
+ },
6637
6642
  {
6638
6643
  "path": "modules/provisioning/credential-preflight.js",
6639
6644
  "mode": "0000644",
@@ -6827,7 +6832,7 @@
6827
6832
  {
6828
6833
  "path": "modules/provisioning/provisioning.js",
6829
6834
  "mode": "0000644",
6830
- "sha256": "5c409a38ac511fe0fa331a5f012c4eda8598d7e8532070e13a42ba707f934f27"
6835
+ "sha256": "fba7da2883254cc0f7f2eafee6c437aeac00f97fb1f8a35acade67e48c6a6eff"
6831
6836
  },
6832
6837
  {
6833
6838
  "path": "modules/provisioning/rate-limit.js",
@@ -6872,7 +6877,7 @@
6872
6877
  {
6873
6878
  "path": "modules/provisioning/routes/provisioning.js",
6874
6879
  "mode": "0000644",
6875
- "sha256": "7738dad310a0de636739fecd9d98036c1d9e4381f7e972337fa8c01d59ccfe9c"
6880
+ "sha256": "6f123553f9fd1f3d48f5356dcddb628c78660c10615f3d7e137f71bf2e7d0750"
6876
6881
  },
6877
6882
  {
6878
6883
  "path": "modules/provisioning/routes/repo-private.js",
@@ -8202,12 +8207,12 @@
8202
8207
  {
8203
8208
  "path": "package-lock.json",
8204
8209
  "mode": "0000644",
8205
- "sha256": "eb742b3a898d8b78fc95893d284f6db4151a98f35ddc81a131f16ff586466a98"
8210
+ "sha256": "ef44492854e8c7dc0bffd40525911500bb56a175d59d82d1807dc1e55c58d0bc"
8206
8211
  },
8207
8212
  {
8208
8213
  "path": "package.json",
8209
8214
  "mode": "0000644",
8210
- "sha256": "130f3e4d93f2846e676c68bd54fb3a318311dfc0912ffa48994fcb2b941a2dfd"
8215
+ "sha256": "2687fb0dcd9a64ca3bc3d4d39f634ec22490bbc935df1d7aa1fc02826d8e76ca"
8211
8216
  },
8212
8217
  {
8213
8218
  "path": "public-docs/index.html",
@@ -10252,7 +10257,7 @@
10252
10257
  {
10253
10258
  "path": "src/module-api.js",
10254
10259
  "mode": "0000644",
10255
- "sha256": "24c5dcae4a5e4be1d9c543791a1be89713e71e8f06150e414463e6064628e6b9"
10260
+ "sha256": "499b3d104f70faa2b9cc4252419f7ff3f08a645fcdf329ec9a26761a25289e65"
10256
10261
  },
10257
10262
  {
10258
10263
  "path": "src/module-loader/catalog.js",
@@ -13434,6 +13439,11 @@
13434
13439
  "mode": "0000644",
13435
13440
  "sha256": "406ea4d32fe1bbb7aa1f69e0c4dc967feb2fc43c8bf0f70848547fdc6faf8762"
13436
13441
  },
13442
+ {
13443
+ "path": "tests/provisioning_cost_ledger.mjs",
13444
+ "mode": "0000644",
13445
+ "sha256": "a0176f26c37067860379df4da0274646771c7d6c427d84fad0145763c7366152"
13446
+ },
13437
13447
  {
13438
13448
  "path": "tests/provisioning_credential_preflight.mjs",
13439
13449
  "mode": "0000644",
@@ -12819,7 +12819,7 @@
12819
12819
  "provisioning"
12820
12820
  ],
12821
12821
  "summary": "GET /provisioning/fleet/cost-ledger",
12822
- "description": "GET /provisioning/fleet/cost-ledger — per-instance monthly estimate + total. rank: archon — cost oversight across the fleet (the /boxes/cost-ledger precedent). Declared among the archon routes (archon-first) for correct auditor classification.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `provisioning.fleet.manage` (all required).",
12822
+ "description": "GET /provisioning/fleet/cost-ledger — per-instance monthly cost to us + estimated cost to its owner, with totals. rank: archon — cost oversight across the fleet (the /boxes/cost-ledger precedent). Declared among the archon routes (archon-first) for correct auditor classification.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `provisioning.fleet.manage` (all required).",
12823
12823
  "x-rank": "metic+archon",
12824
12824
  "x-source": "modules/provisioning/routes/provisioning.js",
12825
12825
  "x-permissions": [
@@ -515,7 +515,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
515
515
  |---|---|---|---|---|
516
516
  | GET | `/api/bongos/provisioning/control-plane` | `archon` | — | GET /provisioning/control-plane — "which row is this platform, and where does it stand?" The deploy page's first call: the self row is cr… |
517
517
  | GET | `/api/bongos/provisioning/fleet` | `metic+archon` | — | GET /provisioning/fleet — the full instance roster across all owners. |
518
- | GET | `/api/bongos/provisioning/fleet/cost-ledger` | `metic+archon` | — | GET /provisioning/fleet/cost-ledger — per-instance monthly estimate + total. |
518
+ | GET | `/api/bongos/provisioning/fleet/cost-ledger` | `metic+archon` | — | GET /provisioning/fleet/cost-ledger — per-instance monthly cost to us + estimated cost to its owner, with totals. |
519
519
  | GET | `/api/bongos/provisioning/github-app/callback` | `public` | — | GET /provisioning/github-app/callback?code=&state= — GitHub redirects the owner's BROWSER here after they approve the App Manifest (task … |
520
520
  | GET | `/api/bongos/provisioning/github-app/status` | `public` | — | GET /provisioning/github-app/status?state=<> — the PUBLIC progress read the onboarding callback page polls (task 2096) to know when setup… |
521
521
  | GET | `/api/bongos/provisioning/health` | `public` | — | GET /provisioning/health — liveness + the core version this module mounted against. |
@@ -2474,5 +2474,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
2474
2474
  landed since 1.19.998 with no explicit bump. run 35942048393. (task 1002620)
2475
2475
  1.19.1000 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2476
2476
  landed since 1.19.999 with no explicit bump. run 35943151697. (task 1002620)
2477
+ 1.19.1001 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2478
+ landed since 1.19.1000 with no explicit bump. run 36016738965. (task 1002620)
2477
2479
  ---------------------------------------------------------------------------
2478
2480
  ```
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+ // The fleet cost ledger (GET /provisioning/fleet/cost-ledger) — VISIBILITY only (ADR 0111 §6).
3
+ //
4
+ // It asks two questions, one column each (task 1004148, ADR 0327). Before, one column
5
+ // asked both and reported a confident $0 for every project:
6
+ // platform_monthly_usd — what a row costs US. A cloud-host runs on its owner's own
7
+ // account, so $0 is correct; the one thing we pay for is the control plane's box.
8
+ // owner_monthly_usd — an ESTIMATE of the owner's own hosting bill, never ours.
9
+ // null in either column means UNPRICED — the row names no plan we can price — and is
10
+ // never folded into a total as 0.
11
+
12
+ const { HOSTING_SHAPES, TIER_MONTHLY_USD } = require('./provisioning');
13
+
14
+ // What a cloud-host's owner pays Render per month, keyed by the plan stored in `tier`:
15
+ // web service + Postgres, ~$13 at the default plans (ADR 0327). No row names a plan yet;
16
+ // the Render standup (task 1004183) writes one, and the pilot (task 1004142) confirms
17
+ // these names and prices.
18
+ const RENDER_PLAN_MONTHLY_USD = Object.freeze({
19
+ 'starter+basic-256mb': 13,
20
+ });
21
+
22
+ function priceOf(table, key) {
23
+ return typeof key === 'string' && Object.hasOwn(table, key) ? table[key] : null;
24
+ }
25
+
26
+ function platformMonthlyUsd({ hostingShape, tier } = {}) {
27
+ // The box the projects share today is priced once, here, by its droplet size.
28
+ if (hostingShape === 'control-plane') return priceOf(TIER_MONTHLY_USD, tier);
29
+ return HOSTING_SHAPES.includes(hostingShape) ? 0 : null;
30
+ }
31
+
32
+ function ownerMonthlyUsd({ hostingShape, tier } = {}) {
33
+ if (hostingShape === 'cloud-host') return priceOf(RENDER_PLAN_MONTHLY_USD, tier);
34
+ // co-tenant shares our box and the control plane is ours: no owner is billed for either.
35
+ if (hostingShape === 'co-tenant' || hostingShape === 'control-plane') return 0;
36
+ return null; // byo-host runs on the owner's own machine, whose bill we cannot see
37
+ }
38
+
39
+ function columnTotal(instances, key) {
40
+ const priced = instances.map((i) => i[key]).filter((v) => v !== null);
41
+ const sum = priced.reduce((a, v) => a + v, 0);
42
+ return { monthly_usd: Number(sum.toFixed(4)), unpriced: instances.length - priced.length };
43
+ }
44
+
45
+ // Per-instance costs + a total per column, over the instances not torn down.
46
+ async function costLedger(db, { limit = 500 } = {}) {
47
+ const { rows } = await db.query(
48
+ `SELECT pi.id, pi.slug, pi.hosting_shape, pi.tier, pi.status, b.github_login
49
+ FROM provisioning_instances pi
50
+ JOIN builders b ON b.id = pi.owner_builder_id
51
+ WHERE pi.status <> 'torn_down'
52
+ ORDER BY pi.updated_at DESC
53
+ LIMIT $1`,
54
+ [Math.min(Math.max(Number(limit) || 500, 1), 2000)]
55
+ );
56
+ const instances = rows.map((r) => {
57
+ const shape = { hostingShape: r.hosting_shape, tier: r.tier };
58
+ return {
59
+ id: r.id, slug: r.slug, owner: r.github_login,
60
+ hosting_shape: r.hosting_shape, tier: r.tier, status: r.status,
61
+ platform_monthly_usd: platformMonthlyUsd(shape),
62
+ owner_monthly_usd: ownerMonthlyUsd(shape),
63
+ };
64
+ });
65
+ return {
66
+ instances,
67
+ totals: {
68
+ platform: columnTotal(instances, 'platform_monthly_usd'),
69
+ owner: columnTotal(instances, 'owner_monthly_usd'),
70
+ },
71
+ };
72
+ }
73
+
74
+ module.exports = { RENDER_PLAN_MONTHLY_USD, platformMonthlyUsd, ownerMonthlyUsd, costLedger };
@@ -307,9 +307,10 @@ function isValidSlug(slug) {
307
307
  // 'dedicated' was the only shape that priced above zero and provisioning_027 folded it into
308
308
  // 'cloud-host', so no row can carry it. ADR 0327 says a ledger of OUR spend "should read
309
309
  // ~$0 per cloud-host and be *correct*"; what an owner is worth telling is THEIR platform
310
- // cost, a different column — task 1004148. The 'dedicated' arm and the tier table stay
311
- // because this is pure and its tests call it with that value; the droplet code path is
312
- // task 1004185's to delete.
310
+ // cost, a different column — ownerMonthlyUsd in cost-ledger.js, which the fleet ledger
311
+ // reads instead of this stored value. The 'dedicated' arm and the tier table stay because
312
+ // this is pure and its tests call it with that value; the droplet code path is task
313
+ // 1004185's to delete.
313
314
  function costEstimateUsd({ hostingShape, tier } = {}) {
314
315
  if (hostingShape !== 'dedicated') return 0;
315
316
  // An ABSENT tier is not an unknown one. The runner substitutes its own default
@@ -1434,30 +1435,6 @@ async function planetChoicesForHosts(db, hosts) {
1434
1435
  return out;
1435
1436
  }
1436
1437
 
1437
- // Fleet cost ledger (Archon): per-instance monthly estimate + a total, over the
1438
- // instances that still cost something (not torn_down). VISIBILITY only (ADR 0111 §6).
1439
- async function costLedger(db, { limit = 500 } = {}) {
1440
- const { rows } = await db.query(
1441
- `SELECT pi.id, pi.slug, pi.hosting_shape, pi.tier, pi.status,
1442
- pi.cost_estimate_usd, b.github_login
1443
- FROM provisioning_instances pi
1444
- JOIN builders b ON b.id = pi.owner_builder_id
1445
- WHERE pi.status <> 'torn_down'
1446
- ORDER BY pi.cost_estimate_usd DESC, pi.updated_at DESC
1447
- LIMIT $1`,
1448
- [Math.min(Math.max(Number(limit) || 500, 1), 2000)]
1449
- );
1450
- const totalMonthlyUsd = rows.reduce((a, r) => a + Number(r.cost_estimate_usd || 0), 0);
1451
- return {
1452
- instances: rows.map((r) => ({
1453
- id: r.id, slug: r.slug, owner: r.github_login,
1454
- hosting_shape: r.hosting_shape, tier: r.tier, status: r.status,
1455
- cost_estimate_usd: Number(r.cost_estimate_usd || 0),
1456
- })),
1457
- total_monthly_usd: Number(totalMonthlyUsd.toFixed(4)),
1458
- };
1459
- }
1460
-
1461
1438
  module.exports = {
1462
1439
  // constants
1463
1440
  INSTANCE_STATES, HOSTING_SHAPES, ONBOARD_MODES, PROJECT_TYPES, INTENT_ACTIONS, TEARDOWN_FROM,
@@ -1488,7 +1465,7 @@ module.exports = {
1488
1465
  recordSettingsPushOwed,
1489
1466
  createInstance, setInstanceStatus, updateInstanceDomain, clearReleasedDomains, updateInstanceSettings, updateInstanceDetail,
1490
1467
  enqueueIntent, enqueueTeardown, enqueueSettingsApply, enqueueRestart, getOpenIntent, claimNextIntent, resolveIntent,
1491
- recordEvent, instanceStateForSlug, planetChoicesForHosts, projectOwnedBy, costLedger,
1468
+ recordEvent, instanceStateForSlug, planetChoicesForHosts, projectOwnedBy,
1492
1469
  // data access — GitHub App Manifest attempts (task 2080)
1493
1470
  createOAuthManifest, getOAuthManifestByState, attachManifestCode,
1494
1471
  getLatestOAuthManifestForInstance, claimNextOAuthManifest, resolveOAuthManifest,
@@ -29,7 +29,7 @@
29
29
  // GET /provisioning/instances/:id/invite-suggestions — who is worth inviting (R28, ADR 0251)
30
30
  // Archon-scoped (requireRank('archon') — the fleet, the /boxes precedent):
31
31
  // GET /provisioning/fleet — the full instance roster + the box's slot `capacity` (ADR 0285)
32
- // GET /provisioning/fleet/cost-ledger — per-instance + total cost estimate
32
+ // GET /provisioning/fleet/cost-ledger — per-instance cost to us + to its owner, with totals
33
33
  // POST /provisioning/instances/:id/force-teardown — tear down ANY instance
34
34
  // public:
35
35
  // GET /provisioning/health — liveness + core version
@@ -49,6 +49,7 @@ const express = require('express');
49
49
  const api = require('../../../src/module-api');
50
50
  const auth = api, { pool, validateOrRespond, parseId } = api;
51
51
  const provisioning = require('../provisioning');
52
+ const { costLedger } = require('../cost-ledger');
52
53
  const { onboardPlan, stepsForShape } = require('../onboard-plan');
53
54
  const { ALWAYS_ON_CORE, OPTIONAL_MODULES, starterBundleAnswerTable, optionalModules, effectiveModules } = require('../starter-bundles');
54
55
  const { recommendationsForDetails } = require('../recommendations');
@@ -214,14 +215,14 @@ module.exports = function provisioningRoutes() {
214
215
  }
215
216
  });
216
217
 
217
- // GET /provisioning/fleet/cost-ledger — per-instance monthly estimate + total.
218
+ // GET /provisioning/fleet/cost-ledger — per-instance monthly cost to us + estimated cost to its owner, with totals.
218
219
  // rank: archon — cost oversight across the fleet (the /boxes/cost-ledger precedent).
219
220
  // Declared among the archon routes (archon-first) for correct auditor classification.
220
221
  router.get('/provisioning/fleet/cost-ledger', auth.requireBuilder, auth.requirePermission('provisioning.fleet.manage'), async (req, res) => {
221
222
  try {
222
223
  // R14 (#2001 / ADR 0119): shared helper; per-endpoint cap preserved (default 500, max 2000).
223
224
  const { limit } = api.parsePagination(req, { defaultLimit: 500, maxLimit: 2000 });
224
- const out = await provisioning.costLedger(pool, { limit });
225
+ const out = await costLedger(pool, { limit });
225
226
  res.json(out);
226
227
  } catch (err) {
227
228
  log.error('[provisioning] GET /provisioning/fleet/cost-ledger', err);
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.1000",
3
+ "version": "1.19.1001",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.1000",
9
+ "version": "1.19.1001",
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.19.1000",
3
+ "version": "1.19.1001",
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",
package/src/module-api.js CHANGED
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.1000'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.1001'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -0,0 +1,98 @@
1
+ // tests/provisioning_cost_ledger.mjs — the fleet cost ledger asks two questions, one
2
+ // column each (task 1004148, ADR 0327): what a row costs US, and an estimate of what it
3
+ // costs its OWNER. A row neither can price reads null (unpriced), never 0 — the old
4
+ // single column reported a confident $0 for every project, with no way to tell
5
+ // "costs nothing" from "we don't know".
6
+ //
7
+ // DB-free: CI has no Postgres, so costLedger runs against a recording fake.
8
+ //
9
+ // Run: node tests/provisioning_cost_ledger.mjs
10
+ import { strict as assert } from 'node:assert';
11
+ import { makeRunner } from './helpers.mjs';
12
+ import {
13
+ platformMonthlyUsd, ownerMonthlyUsd, costLedger, RENDER_PLAN_MONTHLY_USD,
14
+ } from '../modules/provisioning/cost-ledger.js';
15
+
16
+ const { test, summary } = makeRunner();
17
+
18
+ const PLAN = Object.keys(RENDER_PLAN_MONTHLY_USD)[0];
19
+
20
+ await test('a cloud-host with no plan named is unpriced for its owner, not $0', () => {
21
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'cloud-host', tier: null }), null);
22
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'cloud-host' }), null);
23
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'cloud-host', tier: '' }), null);
24
+ });
25
+
26
+ await test('a cloud-host on a named plan is priced from the plan table', () => {
27
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'cloud-host', tier: PLAN }), RENDER_PLAN_MONTHLY_USD[PLAN]);
28
+ assert.ok(RENDER_PLAN_MONTHLY_USD[PLAN] > 0);
29
+ });
30
+
31
+ await test('an unrecognised plan is unpriced, including an Object.prototype key', () => {
32
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'cloud-host', tier: 'pro-max+pro-64gb' }), null);
33
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'cloud-host', tier: 'constructor' }), null);
34
+ assert.equal(platformMonthlyUsd({ hostingShape: 'control-plane', tier: 'toString' }), null);
35
+ });
36
+
37
+ await test('a cloud-host costs US a correct $0: it runs on its owner\'s own account', () => {
38
+ assert.equal(platformMonthlyUsd({ hostingShape: 'cloud-host', tier: PLAN }), 0);
39
+ assert.equal(platformMonthlyUsd({ hostingShape: 'cloud-host', tier: null }), 0);
40
+ });
41
+
42
+ await test('byo-host: $0 to us, and unpriced for the owner (their own machine)', () => {
43
+ assert.equal(platformMonthlyUsd({ hostingShape: 'byo-host' }), 0);
44
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'byo-host' }), null);
45
+ });
46
+
47
+ await test('co-tenant shares our box: no owner is billed, and the box is priced once', () => {
48
+ assert.equal(platformMonthlyUsd({ hostingShape: 'co-tenant' }), 0);
49
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'co-tenant' }), 0);
50
+ });
51
+
52
+ await test('the control plane is OUR cost, priced by its droplet size; no size is unpriced', () => {
53
+ assert.equal(platformMonthlyUsd({ hostingShape: 'control-plane', tier: 's-1vcpu-2gb' }), 12);
54
+ assert.equal(platformMonthlyUsd({ hostingShape: 'control-plane', tier: null }), null);
55
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'control-plane', tier: 's-1vcpu-2gb' }), 0);
56
+ });
57
+
58
+ await test('an unknown shape is unpriced in both columns', () => {
59
+ assert.equal(platformMonthlyUsd({ hostingShape: 'dedicated', tier: 's-2vcpu-4gb' }), null);
60
+ assert.equal(ownerMonthlyUsd({ hostingShape: 'dedicated', tier: 's-2vcpu-4gb' }), null);
61
+ });
62
+
63
+ function recordingDb(rows) {
64
+ const calls = [];
65
+ return { calls, query: async (sql, params) => { calls.push({ sql, params }); return { rows }; } };
66
+ }
67
+ const row = (id, hosting_shape, tier = null) =>
68
+ ({ id, slug: `p${id}`, hosting_shape, tier, status: 'active', github_login: 'owner' });
69
+
70
+ await test('the live fleet today: every total says how many rows it could not price', async () => {
71
+ // Measured 2026-09-24: the control plane (no size recorded) + five cloud-hosts (no plan).
72
+ const db = recordingDb([row(1, 'control-plane'), ...[2, 3, 4, 5, 6].map((id) => row(id, 'cloud-host'))]);
73
+ const out = await costLedger(db);
74
+ assert.deepEqual(out.totals.platform, { monthly_usd: 0, unpriced: 1 });
75
+ assert.deepEqual(out.totals.owner, { monthly_usd: 0, unpriced: 5 });
76
+ assert.equal(out.instances[0].platform_monthly_usd, null);
77
+ assert.equal(out.instances[1].owner_monthly_usd, null);
78
+ });
79
+
80
+ await test('totals sum only priced rows, per column', async () => {
81
+ const db = recordingDb([
82
+ row(1, 'control-plane', 's-1vcpu-2gb'), row(2, 'cloud-host', PLAN), row(3, 'cloud-host', PLAN),
83
+ row(4, 'cloud-host'), row(5, 'byo-host'),
84
+ ]);
85
+ const out = await costLedger(db);
86
+ assert.deepEqual(out.totals.platform, { monthly_usd: 12, unpriced: 0 });
87
+ assert.deepEqual(out.totals.owner, { monthly_usd: 2 * RENDER_PLAN_MONTHLY_USD[PLAN], unpriced: 2 });
88
+ assert.equal('cost_estimate_usd' in out.instances[0], false, 'the one-column-two-questions field is gone');
89
+ });
90
+
91
+ await test('the query skips torn-down rows and clamps the limit', async () => {
92
+ const db = recordingDb([]);
93
+ await costLedger(db, { limit: 99999 });
94
+ assert.match(db.calls[0].sql, /status <> 'torn_down'/);
95
+ assert.deepEqual(db.calls[0].params, [2000]);
96
+ });
97
+
98
+ summary();