@bongos/core 1.24.21 → 1.24.23

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,11 +2,11 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/bongos/package-core.js",
5
- "core_version": "1.24.21",
6
- "core_contract": "1.24.21",
7
- "source_commit": "7b027cf222baf3f5fdd12b802afdc14ad7c5747d",
5
+ "core_version": "1.24.23",
6
+ "core_contract": "1.24.23",
7
+ "source_commit": "1c3eeccc81e91b49ceb762dbe0f15c50db48d4c5",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-11-02T00:01:42.101Z",
9
+ "built_at": "2026-11-02T01:03:41.659Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 632,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 4468,
20
- "tree_sha256": "b017a6fd8efa5ec4e886e0f2877b6d50acec7e1febd611464b0f6c7c2975f8b5",
20
+ "tree_sha256": "08764542a326ca67a7bd4cb22c5eb65f3d618c7e0bee04031fe0c4b3bfe6f77d",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -352,7 +352,7 @@
352
352
  {
353
353
  "path": "clients/bongos-client/index.d.ts",
354
354
  "mode": "0000644",
355
- "sha256": "0e60eb71a4c912b79c2cac3bc338403883667da5c6f871ed811f21dec58d33f9"
355
+ "sha256": "3c301345609619337a39f6385a735722d54552a8e2c4496bc1ab923cd9a65ca5"
356
356
  },
357
357
  {
358
358
  "path": "clients/bongos-client/index.mjs",
@@ -2457,12 +2457,12 @@
2457
2457
  {
2458
2458
  "path": "docs/api-reference.md",
2459
2459
  "mode": "0000644",
2460
- "sha256": "57e77a6ffdbe2d558c088fce69e154b3b595b4d6d10b5053ad4ba0043cb00c46"
2460
+ "sha256": "391006a66d14634cfdc2333d95a9e4aab90376f679b6d4e5b9ff58cd33cec51f"
2461
2461
  },
2462
2462
  {
2463
2463
  "path": "docs/api/openapi.json",
2464
2464
  "mode": "0000644",
2465
- "sha256": "d2e12182597058030c086d3283f652f8d8e2b2e62900c9babed2eba71a97996a"
2465
+ "sha256": "a143477b8f8b9bb1facc61bd0c79f638981c498da2cd04b58997a2cc112df1d8"
2466
2466
  },
2467
2467
  {
2468
2468
  "path": "docs/architecture.md",
@@ -3002,7 +3002,7 @@
3002
3002
  {
3003
3003
  "path": "docs/module-api-changelog.md",
3004
3004
  "mode": "0000644",
3005
- "sha256": "f7f4cdac07096ec9cfe4f08041687ef307af195bc58edc60ae7a248d922e9bbb"
3005
+ "sha256": "afa5a90dd4c0277bf47e5f4c9be826088e1e03f533ba548db0ab46f40629d829"
3006
3006
  },
3007
3007
  {
3008
3008
  "path": "docs/modules-contract.md",
@@ -8802,7 +8802,7 @@
8802
8802
  {
8803
8803
  "path": "modules/onboarding/founding-birth.js",
8804
8804
  "mode": "0000644",
8805
- "sha256": "0477341f98578088d22f7bef85bdeff5a83ab4f680a7edfeac2d4fe2ee8f4e3c"
8805
+ "sha256": "af17a3f63c31e32ccd863a4ce0a6622673642fa1119796c4b555b8b2696bad94"
8806
8806
  },
8807
8807
  {
8808
8808
  "path": "modules/onboarding/founding-builders.js",
@@ -8812,12 +8812,12 @@
8812
8812
  {
8813
8813
  "path": "modules/onboarding/founding-mode.js",
8814
8814
  "mode": "0000644",
8815
- "sha256": "986b71918aba76f55ed303f74314928129f5b4e71f96a342240d8ddff1deb10f"
8815
+ "sha256": "9fd376d97133479cebf4c8f8e26696b44c238cead34df335b8c1aa0b4af107e1"
8816
8816
  },
8817
8817
  {
8818
8818
  "path": "modules/onboarding/genesis-home.js",
8819
8819
  "mode": "0000644",
8820
- "sha256": "22904d8c06daabb634b55f240b895c906e90ba4c6ed81e7a41cdbbb78635ace8"
8820
+ "sha256": "79a712a6dc1ed7633f25b9558ddca0ca75ee5eba0b996dc5a4a57bb57cfc5356"
8821
8821
  },
8822
8822
  {
8823
8823
  "path": "modules/onboarding/howto-guide.js",
@@ -9417,7 +9417,7 @@
9417
9417
  {
9418
9418
  "path": "modules/provisioning/birth.js",
9419
9419
  "mode": "0000644",
9420
- "sha256": "0cec3216e114c185dd536e2ecec9dc2ccf91c4ae4562b33ed022eab3206578cf"
9420
+ "sha256": "e148bfa6918bf5b6a805d6323de91ab99ab81d9c2a796747e17d31dde2db72a2"
9421
9421
  },
9422
9422
  {
9423
9423
  "path": "modules/provisioning/capacity-gate.js",
@@ -9817,7 +9817,7 @@
9817
9817
  {
9818
9818
  "path": "modules/provisioning/routes/provisioning.js",
9819
9819
  "mode": "0000644",
9820
- "sha256": "e59a56ad8551052b8d9087f9a1c8015f8dcd0e50ff94a47070400f08141d6b80"
9820
+ "sha256": "c72d11bef0a5fd0f8bbf070dbc28152caac1b1babbdfc9de101172c4fc99d33d"
9821
9821
  },
9822
9822
  {
9823
9823
  "path": "modules/provisioning/routes/render-standup.js",
@@ -11697,12 +11697,12 @@
11697
11697
  {
11698
11698
  "path": "package-lock.json",
11699
11699
  "mode": "0000644",
11700
- "sha256": "2fd9cac0d64f6c35f9d9e86fc9fea8ebcb261e49247cc1ef8d1efa5a69b85433"
11700
+ "sha256": "419d4d1ad165101fd9a1017dfc7dbef2905e54d2374ca0c751bfc13f9f14bf7e"
11701
11701
  },
11702
11702
  {
11703
11703
  "path": "package.json",
11704
11704
  "mode": "0000644",
11705
- "sha256": "c622f574c6c57132b0bf851323d45f64c35dd5672466216dabc5268b5eaf15d6"
11705
+ "sha256": "bb1cca3496a3ea2dcc2422d08c342a7afcf7023eebee2f176e51fd8884514bb4"
11706
11706
  },
11707
11707
  {
11708
11708
  "path": "public-docs/index.html",
@@ -11722,7 +11722,7 @@
11722
11722
  {
11723
11723
  "path": "release-notes.json",
11724
11724
  "mode": "0000644",
11725
- "sha256": "40836a48667cc62b5d077e56aee4b721cf462a93ec57ea6a40988213cc283c7c"
11725
+ "sha256": "d027a083157c960a3a8825087b912dc021de55417f406bbed603c3907e352c32"
11726
11726
  },
11727
11727
  {
11728
11728
  "path": "scripts/bongos/CLAUDE.md",
@@ -15567,7 +15567,7 @@
15567
15567
  {
15568
15568
  "path": "src/branding.js",
15569
15569
  "mode": "0000644",
15570
- "sha256": "746468968253bdca892997acf10e0d50bece2788d27ea0c2a3b82c52d128ba23"
15570
+ "sha256": "8bb8aebce8394eedabcbdf4d11fc5f190b7e03d2ef8b7d902d80d530f117a35f"
15571
15571
  },
15572
15572
  {
15573
15573
  "path": "src/build-info.js",
@@ -15592,7 +15592,7 @@
15592
15592
  {
15593
15593
  "path": "src/module-api.js",
15594
15594
  "mode": "0000644",
15595
- "sha256": "c5adfcce9c4c9e69692d36fc7af8035af773ec75c3c774532087a0acc8600b3b"
15595
+ "sha256": "acfad6615c99259d2b6ffc0d3ed7f23f83a527f5d281937e236378f913c4a8a5"
15596
15596
  },
15597
15597
  {
15598
15598
  "path": "src/module-loader/catalog.js",
@@ -17457,7 +17457,7 @@
17457
17457
  {
17458
17458
  "path": "tests/founding_birth.mjs",
17459
17459
  "mode": "0000644",
17460
- "sha256": "086308833bfbf52a07e04d0c831ee9685822d05da9cbdeff6766a743e42f5113"
17460
+ "sha256": "0a5bcb5742bf1d2fc5fbbd03180f8542ce1557b4616783ba3751cf6ba43abd8e"
17461
17461
  },
17462
17462
  {
17463
17463
  "path": "tests/founding_builders.mjs",
@@ -17467,7 +17467,7 @@
17467
17467
  {
17468
17468
  "path": "tests/founding_mode.mjs",
17469
17469
  "mode": "0000644",
17470
- "sha256": "40dc19d63b81964f699247709ad91bb70f13f6d562863d1ca2f13c16933f76de"
17470
+ "sha256": "76b3892dc8e9811f454778a997dbe3930a991524b0929f89f608d9fba38d80f7"
17471
17471
  },
17472
17472
  {
17473
17473
  "path": "tests/gating_review_retired.mjs",
@@ -17487,7 +17487,7 @@
17487
17487
  {
17488
17488
  "path": "tests/genesis_home.mjs",
17489
17489
  "mode": "0000644",
17490
- "sha256": "0d4fcb7bdc41561fb1fd969739460a06098fca615a97f210080e75300fc6533d"
17490
+ "sha256": "6cadbdc1c3e1a276f2d381f7256d483fda6e07d8cfab1d7ac89837f2b0d64ffd"
17491
17491
  },
17492
17492
  {
17493
17493
  "path": "tests/genesis_life_plates.mjs",
@@ -20522,7 +20522,7 @@
20522
20522
  {
20523
20523
  "path": "tests/provision_settings_apply.mjs",
20524
20524
  "mode": "0000644",
20525
- "sha256": "d18b3b00b10f9b581a3b768906decc6be6f17ccfb3f4e535f228d4014fa90675"
20525
+ "sha256": "cc5a5c1eabf6bf4ebbda9726798393114c543b49d5df533a8e3c6f9d0c800295"
20526
20526
  },
20527
20527
  {
20528
20528
  "path": "tests/provision_teardown_address.mjs",
@@ -20677,7 +20677,7 @@
20677
20677
  {
20678
20678
  "path": "tests/provisioning_settings_env.mjs",
20679
20679
  "mode": "0000644",
20680
- "sha256": "1e67e9d4bd4b2fdc89e3712f3eba4c07467be3e0ecc10b1ffdd08a202106e013"
20680
+ "sha256": "26c91fde2113a2dff3dd5b9d14ab3609839f69b1f27b768b5028ddee003c737a"
20681
20681
  },
20682
20682
  {
20683
20683
  "path": "tests/provisioning_slug_available.mjs",
@@ -22267,7 +22267,7 @@
22267
22267
  {
22268
22268
  "path": "tests/wizard_demo.mjs",
22269
22269
  "mode": "0000644",
22270
- "sha256": "cde0f1f967f8f6c8fa270fb08a77207764dce9cdedca9de6a3de1f61fa32a80a"
22270
+ "sha256": "d65fcde85566b1cd0d9ded2c267273d6b8717595348c92953803f54485e04c49"
22271
22271
  },
22272
22272
  {
22273
22273
  "path": "tests/wizard_draft_resume.mjs",
@@ -577,7 +577,7 @@ export interface PostProvisioningInstancesIdRenderResponse { ok: boolean; queued
577
577
  export interface PostProvisioningInstancesIdRepoPrivateResponse { ok: boolean; queued: unknown; action: unknown; status: unknown; message: unknown }
578
578
  export interface PostProvisioningInstancesIdRestartResponse { ok: boolean; queued: unknown; action: unknown; status: unknown; message: unknown }
579
579
  export interface PostProvisioningInstancesIdTeardownResponse { ok: boolean; queued: unknown; action: unknown; status: unknown; superseded: unknown; message: unknown }
580
- export interface PostProvisioningInstancesRequest { slug: string; target_ref?: string; hosting_shape?: string; domain?: string; no_address?: boolean; tier?: string; onboard_mode?: string; type?: string; description?: string; detail?: Record<string, unknown>; modules?: string[]; terms_acknowledged?: boolean; demo?: Record<string, unknown>; ship_mode?: "auto" | "manual" }
580
+ export interface PostProvisioningInstancesRequest { slug: string; target_ref?: string; hosting_shape?: string; domain?: string; no_address?: boolean; tier?: string; onboard_mode?: string; type?: string; description?: string; detail?: Record<string, unknown>; modules?: string[]; terms_acknowledged?: boolean; demo?: Record<string, unknown>; ship_mode?: "auto" | "manual"; genesis?: boolean }
581
581
  export interface PostProvisioningPhysicsCheckRequest { description?: string; detail?: Record<string, unknown> }
582
582
  export interface PostProvisioningPhysicsCheckResponse { stop: boolean; message: unknown; soft_limits: unknown }
583
583
  export interface PostProvisioningRenderLookupRequest { key: string; owner_id?: string }
@@ -39404,6 +39404,9 @@
39404
39404
  "auto",
39405
39405
  "manual"
39406
39406
  ]
39407
+ },
39408
+ "genesis": {
39409
+ "type": "boolean"
39407
39410
  }
39408
39411
  },
39409
39412
  "required": [
@@ -789,7 +789,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
789
789
  | GET | `/api/bongos/provisioning/github-app/status` | `public` | — | GET /provisioning/github-app/status?state=<> — the PUBLIC progress read (task 1004548; the checklist since task 1004649). |
790
790
  | GET | `/api/bongos/provisioning/health` | `public` | — | GET /provisioning/health — liveness + the core version this module mounted against. |
791
791
  | GET | `/api/bongos/provisioning/instances` | `any-builder` | — | GET /provisioning/instances — the caller's own instances + each open intent. |
792
- | POST | `/api/bongos/provisioning/instances` | `any-builder` | `slug`, `target_ref`, `hosting_shape`, `domain`, `no_address`, `tier`, `onboard_mode`, `type`, `description`, `detail`, `modules`, `terms_acknowledged`, `demo`, `ship_mode` | POST /provisioning/instances — request a new instance. |
792
+ | POST | `/api/bongos/provisioning/instances` | `any-builder` | `slug`, `target_ref`, `hosting_shape`, `domain`, `no_address`, `tier`, `onboard_mode`, `type`, `description`, `detail`, `modules`, `terms_acknowledged`, `demo`, `ship_mode`, `genesis` | POST /provisioning/instances — request a new instance. |
793
793
  | GET | `/api/bongos/provisioning/instances/:id` | `any-builder` | — | GET /provisioning/instances/:id — read one instance (the manage page's read). |
794
794
  | GET | `/api/bongos/provisioning/instances/:id/core-upgrade` | `archon` | — | GET the same path — step ONE of the two-step (ADR 0293 D4): what a move would do. |
795
795
  | POST | `/api/bongos/provisioning/instances/:id/core-upgrade` | `archon` | — | POST /provisioning/instances/:id/core-upgrade — move the core pin. |
@@ -3298,5 +3298,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
3298
3298
  landed since 1.24.19 with no explicit bump. run 41141610027. (task 1002620)
3299
3299
  1.24.21 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3300
3300
  landed since 1.24.20 with no explicit bump. run 41364831803. (task 1002620)
3301
+ 1.24.22 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3302
+ landed since 1.24.21 with no explicit bump. run 41366737579. (task 1002620)
3303
+ 1.24.23 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3304
+ landed since 1.24.22 with no explicit bump. run 41370306880. (task 1002620)
3301
3305
  ---------------------------------------------------------------------------
3302
3306
  ```
@@ -50,11 +50,13 @@ const { DEMO_PEOPLE, DEMO_HOURS, intIn } = genesis;
50
50
  function birthMarker(founding) {
51
51
  const f = founding && typeof founding === 'object' ? founding : {};
52
52
  const born = typeof f.birth === 'string' && Number.isFinite(new Date(f.birth).getTime());
53
- if (!born) return { born: false, demo: null };
53
+ if (!born) return { born: false, demo: null, genesis: false };
54
54
  const people = Number(f.demoPeople);
55
55
  const hours = Number(f.demoHours);
56
56
  const demo = intIn(people, DEMO_PEOPLE[0], DEMO_PEOPLE[1]) && intIn(hours, DEMO_HOURS[0], DEMO_HOURS[1]) ? { people, hours_each: hours } : null;
57
- return { born: true, demo };
57
+ // Genesis is paused (owner, 2026-11-01): only a birth stamped with the genesis companion starts it, so a
58
+ // project born before the pause, carrying a marker alone, never does.
59
+ return { born: true, demo, genesis: f.genesis === 'on' };
58
60
  }
59
61
 
60
62
  function markerFrom(deps) {
@@ -71,6 +73,7 @@ function markerFrom(deps) {
71
73
  async function bornFounding(deps = {}) {
72
74
  const marker = markerFrom(deps);
73
75
  if (!marker.born) return { ran: false, reason: 'no_marker' };
76
+ if (!marker.genesis) return { ran: false, reason: 'genesis_paused' };
74
77
  const get = deps.getSetting || require('../../src/module-api').projectSettings.get;
75
78
  const [started, ended] = await Promise.all([get(fm.KEYS.STARTED_AT), get(fm.KEYS.ENDED_AT)]);
76
79
  if (ended && ended.value) return { ran: false, reason: 'already_alive' };
@@ -50,6 +50,11 @@ const KEYS = Object.freeze({
50
50
  // When the planet came alive (founding switched off). With STARTED_AT it bounds the
51
51
  // window a builder must have joined in to carry the founding mark (task 1004424).
52
52
  ENDED_AT: 'founding.ended_at',
53
+ // Genesis is OFF unless a project turns it on (owner, 2026-11-01: not polished enough, so every
54
+ // project opens as the full hall). Read as ordinary 'true'; absent or anything else is OFF, which is
55
+ // also what pauses a project that was already mid-founding. A demo, or a project born with genesis,
56
+ // turns it on once at birth (founding-birth.js); the owner's own switch can follow it.
57
+ ENABLED: 'genesis.enabled',
53
58
  });
54
59
 
55
60
  // The key that records when a stage first became current, so a builder's join time
@@ -121,6 +126,11 @@ async function foundingModeFor(deps = {}) {
121
126
  const get = deps.getSetting || require('../../src/module-api').projectSettings.get;
122
127
  let shaped;
123
128
  try {
129
+ // Paused unless the project turned genesis on: the hall opens full, whatever founding.active says.
130
+ // Asked FIRST and alone, because this is the answer for nearly every project and every /me, so the
131
+ // common case is one read, not four.
132
+ const enabled = await get(KEYS.ENABLED);
133
+ if (!parseActive(enabled && enabled.value)) return null;
124
134
  const [active, stage, startedAt] = await Promise.all([
125
135
  get(KEYS.ACTIVE), get(KEYS.STAGE), get(KEYS.STARTED_AT),
126
136
  ]);
@@ -264,6 +264,9 @@ async function startFounding(builderId, deps = {}) {
264
264
  const s = seams(deps);
265
265
  const ended = await s.get(fm.KEYS.ENDED_AT);
266
266
  if (ended && ended.value) return { ok: false, reason: 'already_alive' };
267
+ // Genesis is off unless a project turns it on (task 1005035): starting it IS turning it on, whether the
268
+ // birth does it or the owner's own start route does, so the mode read below never answers null for it.
269
+ await s.set(fm.KEYS.ENABLED, 'true', builderId);
267
270
  const mode = await fm.setFoundingMode({ active: true }, builderId, modeDeps(s));
268
271
  return { ok: true, mode };
269
272
  }
@@ -21,6 +21,9 @@
21
21
  // that came and went would leave a stale line behind.
22
22
 
23
23
  const BIRTH_KEY = 'founding_birth';
24
+ // Genesis is paused (owner, 2026-11-01): only a birth stamped with this key starts it. Reserved like the
25
+ // birth itself; a row born before the pause carries the birth alone, so it never founds.
26
+ const GENESIS_KEY = 'founding_genesis';
24
27
  // The wizard's ship-mode answer (task 1004629): auto | manual, stamped beside the birth on
25
28
  // the row the create route just made. Reserved like the birth (out of SETTINGS_VOCAB): the
26
29
  // instance's own project_settings['ship.mode'] is where the setting LIVES and where the
@@ -36,6 +39,11 @@ function bornAt(row) {
36
39
  return ISO.test(v) && Number.isFinite(new Date(v).getTime()) ? v : '';
37
40
  }
38
41
 
42
+ // genesisOn(row) — PURE. 'on' only for a born row stamped with genesis, else ''.
43
+ function genesisOn(row) {
44
+ return bornAt(row) && storedOf(row)[GENESIS_KEY] === 'on' ? 'on' : '';
45
+ }
46
+
39
47
  // demoBox(row) — PURE. A born demo's { people, hours_each }, or null. Only a row that is
40
48
  // both born and a demo carries one: an older demo has no marker and founds nothing.
41
49
  function demoBox(row) {
@@ -57,6 +65,7 @@ const bornShipMode = (row) => parseShipMode(storedOf(row)[SHIP_MODE_KEY]).mode |
57
65
  // The companion env (SETTINGS_COMPANION_ENV). src/branding.js maps them onto founding.*.
58
66
  const BIRTH_COMPANION_ENV = Object.freeze({
59
67
  FOUNDING_BIRTH: bornAt,
68
+ FOUNDING_GENESIS: genesisOn,
60
69
  DEMO_PEOPLE: (row) => { const d = demoBox(row); return d ? String(d.people) : ''; },
61
70
  DEMO_HOURS: (row) => { const d = demoBox(row); return d ? String(d.hours_each) : ''; },
62
71
  SHIP_MODE: bornShipMode,
@@ -67,9 +76,9 @@ const BIRTH_COMPANION_ENV = Object.freeze({
67
76
  async function stampBirth(db, instanceId, now = new Date()) {
68
77
  await db.query(
69
78
  `UPDATE provisioning_instances
70
- SET settings = COALESCE(settings, '{}'::jsonb) || jsonb_build_object($2::text, $3::text)
79
+ SET settings = COALESCE(settings, '{}'::jsonb) || jsonb_build_object($2::text, $3::text, $4::text, 'on')
71
80
  WHERE id = $1 AND NOT (COALESCE(settings, '{}'::jsonb) ? $2::text)`,
72
- [instanceId, BIRTH_KEY, now.toISOString()],
81
+ [instanceId, BIRTH_KEY, now.toISOString(), GENESIS_KEY],
73
82
  );
74
83
  }
75
84
 
@@ -357,7 +357,7 @@ module.exports = function provisioningRoutes() {
357
357
  // exactly the set the panel preselected. An explicit [] is the different
358
358
  // answer "none of them". maxItems is a sanity ceiling well above a catalog
359
359
  // that is three modules wide today.
360
- modules: { type: 'array', itemsType: 'string', maxItems: 50 }, terms_acknowledged: { type: 'boolean' }, demo: { type: 'object' }, ship_mode: { type: 'string', enum: ['auto', 'manual'] }, // the wizard's terms tick (task 1004417, ../terms-ack.js); the demo route's { people, hours_each } (task 1004420, ../demo.js)
360
+ modules: { type: 'array', itemsType: 'string', maxItems: 50 }, terms_acknowledged: { type: 'boolean' }, demo: { type: 'object' }, ship_mode: { type: 'string', enum: ['auto', 'manual'] }, genesis: { type: 'boolean' }, // the wizard's terms tick (task 1004417, ../terms-ack.js); the demo route's { people, hours_each } (task 1004420, ../demo.js)
361
361
  })) return;
362
362
  const body = req.body || {};
363
363
  const detailFault = badProjectDetail(body.detail);
@@ -472,7 +472,9 @@ module.exports = function provisioningRoutes() {
472
472
  // Born into genesis (task 1004506, ../birth.js): only the row this request MADE, and before the
473
473
  // provision intent exists, so the runner's web.env already carries the marker and the demo's box.
474
474
  // Best-effort: a project that misses its marker opens as an ordinary hall; the create still stands.
475
- if (created) await birth.stampBirth(pool, instance.id).catch((err) => log.error('[provisioning] birth marker not stamped', err));
475
+ // Genesis is OFF by default (owner, 2026-11-01): a new project opens as the full hall. Only a demo (its
476
+ // time box lives on the birth marker) or an explicit `genesis: true` (the test toggle) is born into it.
477
+ if (created && (demoAsk.demo || body.genesis === true)) await birth.stampBirth(pool, instance.id).catch((err) => log.error('[provisioning] birth marker not stamped', err));
476
478
  // The wizard's ship mode (task 1004629) rides beside it; a miss means the project asks at its first ship.
477
479
  if (created) await birth.stampShipMode(pool, instance.id, body.ship_mode).catch((err) => log.error('[provisioning] ship mode not stamped', err));
478
480
  const demoRow = created && demoAsk.demo ? await demo.startDemoProject(pool, provisioning, instance.id, demoAsk.demo) : null;
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.21",
3
+ "version": "1.24.23",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.24.21",
9
+ "version": "1.24.23",
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.21",
3
+ "version": "1.24.23",
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",
@@ -11015,5 +11015,17 @@
11015
11015
  "id": "1005013",
11016
11016
  "text": "Every card in the city now says what it is. Under each question sits a short plain explanation: what kind of thing it is, which goal, task, person or module it belongs to, and what answering does. A Review card used to show a"
11017
11017
  }
11018
+ ],
11019
+ "1.24.22": [
11020
+ {
11021
+ "id": "1005034",
11022
+ "text": "New projects now open as the full hall instead of starting in the guided setup (genesis and founding mode). The guided setup is paused for now and stays available for demo projects and for test projects that ask for it. Projec"
11023
+ }
11024
+ ],
11025
+ "1.24.23": [
11026
+ {
11027
+ "id": "1005035",
11028
+ "text": "Projects that were still in the guided setup now open as the full project instead, and no project starts the guided setup unless it is switched on for that project. Hosted projects pick this up when their version is moved forw"
11029
+ }
11018
11030
  ]
11019
11031
  }
package/src/branding.js CHANGED
@@ -101,6 +101,9 @@ const ENV_OVERRIDES = [
101
101
  // start genesis (modules/onboarding/founding-birth.js). SERVER-ONLY: clientBranding
102
102
  // publishes no `founding` block.
103
103
  ['FOUNDING_BIRTH', ['founding', 'birth']],
104
+ // FOUNDING_GENESIS is 'on' only when the platform stamped the birth with genesis (a demo, or a
105
+ // test project asking for it); empty on every project born before the pause.
106
+ ['FOUNDING_GENESIS', ['founding', 'genesis']],
104
107
  ['DEMO_PEOPLE', ['founding', 'demoPeople']],
105
108
  ['DEMO_HOURS', ['founding', 'demoHours']],
106
109
  // The ship mode the founder picked in the wizard (task 1004629), empty when none was.
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.21'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.24.23'; // 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');
@@ -21,8 +21,8 @@ const birth = require('../modules/onboarding/founding-birth.js');
21
21
  const gh = require('../modules/onboarding/genesis-home.js');
22
22
  const fm = require('../modules/onboarding/founding-mode.js');
23
23
 
24
- const BORN = { born: true, demo: null };
25
- const BORN_DEMO = { born: true, demo: { people: 3, hours_each: 4 } };
24
+ const BORN = { born: true, demo: null, genesis: true };
25
+ const BORN_DEMO = { born: true, demo: { people: 3, hours_each: 4 }, genesis: true };
26
26
 
27
27
  function world({ marker = BORN, settings = {}, withLifecycle = true } = {}) {
28
28
  const store = new Map(Object.entries(settings));
@@ -43,19 +43,59 @@ function world({ marker = BORN, settings = {}, withLifecycle = true } = {}) {
43
43
 
44
44
  // ── the marker ───────────────────────────────────────────────────────────────
45
45
 
46
- test('the marker: born only with a readable birth stamp; a demo box only inside its limits', () => {
47
- assert.deepEqual(birth.birthMarker(undefined), { born: false, demo: null });
48
- assert.deepEqual(birth.birthMarker({}), { born: false, demo: null });
49
- assert.deepEqual(birth.birthMarker({ birth: 'whenever' }), { born: false, demo: null });
50
- assert.deepEqual(birth.birthMarker({ birth: '2026-10-01T00:00:00.000Z' }), { born: true, demo: null });
51
- assert.deepEqual(birth.birthMarker({ birth: '2026-10-01T00:00:00.000Z', demoPeople: '3', demoHours: '4' }),
52
- { born: true, demo: { people: 3, hours_each: 4 } });
53
- assert.deepEqual(birth.birthMarker({ birth: '2026-10-01T00:00:00.000Z', demoPeople: '99', demoHours: '4' }),
54
- { born: true, demo: null }, 'a malformed box still founds, with the ordinary plan');
55
- assert.deepEqual(birth.birthMarker({ demoPeople: '3', demoHours: '4' }), { born: false, demo: null },
46
+ test('the marker: born only with a readable birth stamp; a demo box only inside its limits; genesis only when stamped', () => {
47
+ const AT = '2026-10-01T00:00:00.000Z';
48
+ assert.deepEqual(birth.birthMarker(undefined), { born: false, demo: null, genesis: false });
49
+ assert.deepEqual(birth.birthMarker({}), { born: false, demo: null, genesis: false });
50
+ assert.deepEqual(birth.birthMarker({ birth: 'whenever' }), { born: false, demo: null, genesis: false });
51
+ assert.deepEqual(birth.birthMarker({ birth: AT }), { born: true, demo: null, genesis: false }, 'a birth alone (every project born before the pause) never founds');
52
+ assert.deepEqual(birth.birthMarker({ birth: AT, genesis: 'on' }), { born: true, demo: null, genesis: true });
53
+ assert.deepEqual(birth.birthMarker({ birth: AT, genesis: 'yes' }), { born: true, demo: null, genesis: false }, 'only the stamped spelling counts');
54
+ assert.deepEqual(birth.birthMarker({ birth: AT, genesis: 'on', demoPeople: '3', demoHours: '4' }),
55
+ { born: true, demo: { people: 3, hours_each: 4 }, genesis: true });
56
+ assert.deepEqual(birth.birthMarker({ birth: AT, genesis: 'on', demoPeople: '99', demoHours: '4' }),
57
+ { born: true, demo: null, genesis: true }, 'a malformed box still founds, with the ordinary plan');
58
+ assert.deepEqual(birth.birthMarker({ demoPeople: '3', demoHours: '4' }), { born: false, demo: null, genesis: false },
56
59
  'a demo box without a birth is not a birth');
57
60
  });
58
61
 
62
+ test('genesis is paused: a birth without the genesis stamp starts nothing and writes nothing', async () => {
63
+ const w = world({ marker: { born: true, demo: null, genesis: false } });
64
+ const r = await birth.bornFounding(w.deps());
65
+ assert.deepEqual(r, { ran: false, reason: 'genesis_paused' });
66
+ assert.deepEqual(w.writes, [], 'no setting is touched');
67
+ });
68
+
69
+ test('the switch: founding.active alone reads as not founding; the switch makes it read', async () => {
70
+ const store = new Map([[fm.KEYS.ACTIVE, 'true'], [fm.KEYS.STAGE, 'charter']]);
71
+ const getSetting = async (k) => (store.has(k) ? { key: k, value: store.get(k) } : null);
72
+ assert.equal(await fm.foundingModeFor({ getSetting, government: null }), null, 'a project already mid-founding opens as the full hall');
73
+ store.set(fm.KEYS.ENABLED, 'true');
74
+ assert.equal((await fm.foundingModeFor({ getSetting, government: null })).stage, 'charter');
75
+ });
76
+
77
+ test('an owner starting genesis turns the switch on, so a started genesis is never invisible', async () => {
78
+ const w = world();
79
+ const r = await gh.startFounding('7', w.deps());
80
+ assert.equal(r.ok, true);
81
+ assert.equal(w.store.get(fm.KEYS.ENABLED), 'true');
82
+ assert.equal(r.mode.active, true, 'the mode it answers with reads, not null');
83
+ });
84
+
85
+ test('a paused project costs ONE settings read, not four', async () => {
86
+ let reads = 0;
87
+ const getSetting = async () => { reads += 1; return null; };
88
+ assert.equal(await fm.foundingModeFor({ getSetting }), null);
89
+ assert.equal(reads, 1, 'the switch is asked first and alone');
90
+ });
91
+
92
+ test('a birth with genesis turns the switch on, once, before founding starts', async () => {
93
+ const w = world();
94
+ await birth.bornFounding(w.deps());
95
+ assert.equal(w.store.get(fm.KEYS.ENABLED), 'true');
96
+ assert.ok(w.writes.indexOf(fm.KEYS.ENABLED) < w.writes.indexOf(fm.KEYS.ACTIVE), 'the switch is on before founding is');
97
+ });
98
+
59
99
  // ── the birth hook ───────────────────────────────────────────────────────────
60
100
 
61
101
  test('no marker: nothing runs, and the database is never read (a pre-existing hall is unchanged)', async () => {
@@ -70,7 +70,7 @@ test('foundingModeFor fails closed when the settings read throws', async () => {
70
70
  });
71
71
 
72
72
  test('setFoundingMode refuses an unknown stage and stamps started_at once', async () => {
73
- const store = new Map();
73
+ const store = new Map([[fm.KEYS.ENABLED, 'true']]);
74
74
  const deps = {
75
75
  getSetting: async (k) => (store.has(k) ? { key: k, value: store.get(k) } : null),
76
76
  setSetting: async (k, v) => { store.set(k, String(v)); return { key: k, value: String(v) }; },
@@ -227,7 +227,7 @@ test('the band escapes what it prints — a project name cannot inject markup',
227
227
 
228
228
  test('who the stage close is waiting on shows in the band when the server sends it', async () => {
229
229
  const f = await fm.foundingModeFor({
230
- getSetting: async (k) => ({ [fm.KEYS.ACTIVE]: { value: 'true' }, [fm.KEYS.STAGE]: { value: 'charter' } }[k] || null),
230
+ getSetting: async (k) => ({ [fm.KEYS.ENABLED]: { value: 'true' }, [fm.KEYS.ACTIVE]: { value: 'true' }, [fm.KEYS.STAGE]: { value: 'charter' } }[k] || null),
231
231
  waitingOnFor: async (stage) => (stage === 'charter' ? ['Avery', 'Jordan'] : []),
232
232
  });
233
233
  assert.deepEqual(f.waiting_on, ['Avery', 'Jordan']);
@@ -245,20 +245,20 @@ test('waiting-on reads the government port\'s genesis-stage status, sitting stag
245
245
  ] }),
246
246
  };
247
247
  const read = (stage) => fm.foundingModeFor({
248
- getSetting: async (k) => ({ [fm.KEYS.ACTIVE]: { value: 'true' }, [fm.KEYS.STAGE]: { value: stage } }[k] || null),
248
+ getSetting: async (k) => ({ [fm.KEYS.ENABLED]: { value: 'true' }, [fm.KEYS.ACTIVE]: { value: 'true' }, [fm.KEYS.STAGE]: { value: stage } }[k] || null),
249
249
  government: gov,
250
250
  });
251
251
  assert.deepEqual((await read('charter')).waiting_on, ['Avery', 'jordan']);
252
252
  assert.equal((await read('scope')).waiting_on, null, 'a passed stage waits on nobody');
253
253
  assert.equal(fm.waitingOnFromGovernment({}), null, 'a port without the read offers none');
254
254
  assert.equal((await fm.foundingModeFor({
255
- getSetting: async (k) => ({ [fm.KEYS.ACTIVE]: { value: 'true' } }[k] || null), government: null,
255
+ getSetting: async (k) => ({ [fm.KEYS.ENABLED]: { value: 'true' }, [fm.KEYS.ACTIVE]: { value: 'true' } }[k] || null), government: null,
256
256
  })).waiting_on, null, 'no government module: the mode still reads, without names');
257
257
  });
258
258
 
259
259
  test('a failing waiting-on source never turns the mode off', async () => {
260
260
  const f = await fm.foundingModeFor({
261
- getSetting: async (k) => ({ [fm.KEYS.ACTIVE]: { value: 'true' }, [fm.KEYS.STAGE]: { value: 'scope' } }[k] || null),
261
+ getSetting: async (k) => ({ [fm.KEYS.ENABLED]: { value: 'true' }, [fm.KEYS.ACTIVE]: { value: 'true' }, [fm.KEYS.STAGE]: { value: 'scope' } }[k] || null),
262
262
  waitingOnFor: async () => { throw new Error('government down'); },
263
263
  });
264
264
  assert.equal(f.active, true);
@@ -146,7 +146,7 @@ test('a demo genesis reads in minutes: each stage due minutes after it began', (
146
146
  // ── the flow, end to end over fakes ──────────────────────────────────────────
147
147
 
148
148
  function world({ withGovernment = true } = {}) {
149
- const store = new Map();
149
+ const store = new Map([['genesis.enabled', 'true']]);
150
150
  const tasks = {};
151
151
  const board = {};
152
152
  let now = new Date('2026-10-01T00:00:00Z');
@@ -76,6 +76,7 @@ ${NEUTRAL_PREFIX}_ARTIST_GATE_SINCE=
76
76
  ${NEUTRAL_PREFIX}_LOOK_ACCENT=
77
77
  ${NEUTRAL_PREFIX}_LOGO_URL=
78
78
  ${NEUTRAL_PREFIX}_FOUNDING_BIRTH=
79
+ ${NEUTRAL_PREFIX}_FOUNDING_GENESIS=
79
80
  ${NEUTRAL_PREFIX}_DEMO_PEOPLE=
80
81
  ${NEUTRAL_PREFIX}_DEMO_HOURS=
81
82
  ${NEUTRAL_PREFIX}_SHIP_MODE=
@@ -148,7 +149,7 @@ t('settingsEnvVarsFor: the runner pushes the SAME pair webEnvBody writes at stan
148
149
  [`${NEUTRAL_PREFIX}_LOOK`]: 'own', [`${NEUTRAL_PREFIX}_NIGHT_TINT`]: 'on',
149
150
  [`${NEUTRAL_PREFIX}_LOOK_ACCENT`]: '', [`${NEUTRAL_PREFIX}_LOGO_URL`]: '',
150
151
  // the birth marker and a demo's box (task 1004506), empty on an un-stamped row
151
- [`${NEUTRAL_PREFIX}_FOUNDING_BIRTH`]: '', [`${NEUTRAL_PREFIX}_DEMO_PEOPLE`]: '', [`${NEUTRAL_PREFIX}_DEMO_HOURS`]: '',
152
+ [`${NEUTRAL_PREFIX}_FOUNDING_BIRTH`]: '', [`${NEUTRAL_PREFIX}_FOUNDING_GENESIS`]: '', [`${NEUTRAL_PREFIX}_DEMO_PEOPLE`]: '', [`${NEUTRAL_PREFIX}_DEMO_HOURS`]: '',
152
153
  // the wizard's ship mode (task 1004629), empty when none was picked
153
154
  [`${NEUTRAL_PREFIX}_SHIP_MODE`]: '',
154
155
  // where the hall's Update button goes (task 1004608), empty when no hub origin resolves
@@ -126,7 +126,7 @@ t('settingsEnvVars is the single source both legs derive from — same keys, sam
126
126
  CLOUDBONGOS_LOOK: 'own', CLOUDBONGOS_NIGHT_TINT: 'on', CLOUDBONGOS_LOOK_ACCENT: '', CLOUDBONGOS_LOGO_URL: '',
127
127
  // The birth marker and a demo's box (task 1004506): empty on a row the create
128
128
  // route never stamped, which is every row made before it shipped (spec D8).
129
- CLOUDBONGOS_FOUNDING_BIRTH: '', CLOUDBONGOS_DEMO_PEOPLE: '', CLOUDBONGOS_DEMO_HOURS: '',
129
+ CLOUDBONGOS_FOUNDING_BIRTH: '', CLOUDBONGOS_FOUNDING_GENESIS: '', CLOUDBONGOS_DEMO_PEOPLE: '', CLOUDBONGOS_DEMO_HOURS: '',
130
130
  // The wizard's ship mode (task 1004629): empty when none was picked.
131
131
  CLOUDBONGOS_SHIP_MODE: '',
132
132
  });
@@ -137,7 +137,7 @@ t('settingsEnvVars is the single source both legs derive from — same keys, sam
137
137
  X_PLATFORM_VISIBILITY: 'public', X_JOINABILITY: 'apply', X_VISIBILITY: 'public', X_JOIN_GRANT: 'full',
138
138
  X_ARTIST_GATE: 'strict', X_ARTIST_GATE_SINCE: '', X_SPEND_PAYER: 'none',
139
139
  X_LOOK: 'own', X_NIGHT_TINT: 'on', X_LOOK_ACCENT: '', X_LOGO_URL: '',
140
- X_FOUNDING_BIRTH: '', X_DEMO_PEOPLE: '', X_DEMO_HOURS: '', X_SHIP_MODE: '',
140
+ X_FOUNDING_BIRTH: '', X_FOUNDING_GENESIS: '', X_DEMO_PEOPLE: '', X_DEMO_HOURS: '', X_SHIP_MODE: '',
141
141
  }, 'defaults, explicitly');
142
142
  });
143
143
 
@@ -155,6 +155,10 @@ t('a stamped row carries its birth; only a stamped DEMO carries the box', () =>
155
155
  assert.deepEqual([oldDemo.X_FOUNDING_BIRTH, oldDemo.X_DEMO_PEOPLE, oldDemo.X_DEMO_HOURS], ['', '', ''],
156
156
  'a demo made before the birth shipped is not born into anything, box and all');
157
157
  assert.equal(provisioning.settingsEnvVars({ settings: { founding_birth: 'yesterday' } }, 'X').X_FOUNDING_BIRTH, '', 'a mangled stamp reads as none');
158
+ // Genesis is paused: only a birth stamped with the genesis key carries the companion (owner, 2026-11-01).
159
+ assert.equal(provisioning.settingsEnvVars({ settings: { founding_birth: at, founding_genesis: 'on' } }, 'X').X_FOUNDING_GENESIS, 'on');
160
+ assert.equal(real.X_FOUNDING_GENESIS, '', 'a birth alone, the shape of every project before the pause, does not found');
161
+ assert.equal(provisioning.settingsEnvVars({ settings: { founding_genesis: 'on' } }, 'X').X_FOUNDING_GENESIS, '', 'the genesis key without a birth is nothing');
158
162
  });
159
163
 
160
164
  t('the stamp can neither leak as a setting nor be written by a client patch', () => {
@@ -172,7 +176,7 @@ t('stampBirth writes the row it is given, once: an existing stamp is kept', () =
172
176
  assert.equal(seen.length, 1);
173
177
  assert.match(seen[0].sql, /UPDATE provisioning_instances/);
174
178
  assert.match(seen[0].sql, /WHERE id = \$1 AND NOT \(COALESCE\(settings, '\{\}'::jsonb\) \? \$2::text\)/, 'write-once');
175
- assert.deepEqual(seen[0].params, [41, BIRTH_KEY, '2026-10-01T00:00:00.000Z']);
179
+ assert.deepEqual(seen[0].params, [41, BIRTH_KEY, '2026-10-01T00:00:00.000Z', 'founding_genesis']);
176
180
  });
177
181
 
178
182
  t('the instance reads the marker into founding.*, and never publishes it to the client', () => {
@@ -171,7 +171,10 @@ test('a new project is stamped born, once, before its provision intent; an exist
171
171
  assert.deepEqual(order, ['birth:41', 'demo:41', 'enqueue:provision'], 'the marker and the demo box are on the row before the runner can read it');
172
172
  order.length = 0;
173
173
  assert.equal((await post('/provisioning/instances', { slug: 'born-real', hosting_shape: 'byo-host' })).status, 201);
174
- assert.deepEqual(order, ['birth:41', 'enqueue:provision'], 'a real project is born too, with no demo box');
174
+ assert.deepEqual(order, ['enqueue:provision'], 'a real project opens as the full hall: genesis is off by default (owner, 2026-11-01)');
175
+ order.length = 0;
176
+ assert.equal((await post('/provisioning/instances', { slug: 'born-test', hosting_shape: 'byo-host', genesis: true })).status, 201);
177
+ assert.deepEqual(order, ['birth:41', 'enqueue:provision'], 'the genesis toggle still births a test project, with no demo box');
175
178
  order.length = 0;
176
179
  const again = await post('/provisioning/instances', { slug: 'old-one', hosting_shape: 'byo-host' });
177
180
  assert.ok(again.status < 300, JSON.stringify(again.body));
@@ -179,7 +182,7 @@ test('a new project is stamped born, once, before its provision intent; an exist
179
182
  const keep = birth.stampBirth;
180
183
  birth.stampBirth = async () => { throw new Error('db hiccup'); };
181
184
  try {
182
- assert.equal((await post('/provisioning/instances', { slug: 'unlucky', hosting_shape: 'byo-host' })).status, 201,
185
+ assert.equal((await post('/provisioning/instances', { slug: 'unlucky', hosting_shape: 'byo-host', genesis: true })).status, 201,
183
186
  'a missed marker costs the project its genesis, never its creation');
184
187
  } finally { birth.stampBirth = keep; }
185
188
  });