@bongos/core 1.24.22 → 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.22",
6
- "core_contract": "1.24.22",
7
- "source_commit": "25ff48c6d442a8dce64b481eaf3f4e7a8b9e02fa",
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:23:18.248Z",
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": "5e6e3d870be65a768c0574be5c3d7a16b2be8850748f9f622d66da5c4f3f5ef2",
20
+ "tree_sha256": "08764542a326ca67a7bd4cb22c5eb65f3d618c7e0bee04031fe0c4b3bfe6f77d",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -3002,7 +3002,7 @@
3002
3002
  {
3003
3003
  "path": "docs/module-api-changelog.md",
3004
3004
  "mode": "0000644",
3005
- "sha256": "f2c013805088a650ca5c4ea40b1c62033024d680fcdbf3cbce6bce525506af3d"
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",
@@ -11697,12 +11697,12 @@
11697
11697
  {
11698
11698
  "path": "package-lock.json",
11699
11699
  "mode": "0000644",
11700
- "sha256": "f8d14423c45cc4a1765b3fb41c65c0a73634f5de15da0ab67bae8be2baab8875"
11700
+ "sha256": "419d4d1ad165101fd9a1017dfc7dbef2905e54d2374ca0c751bfc13f9f14bf7e"
11701
11701
  },
11702
11702
  {
11703
11703
  "path": "package.json",
11704
11704
  "mode": "0000644",
11705
- "sha256": "074860e850922d563941d386715a13c8426c5e7f3378fe433b768a616a6c6f75"
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": "8f5fa868a83aa8030c953ac337abb41b7e99ca41fc26e76f0d657f1732eecad8"
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": "fa65cb9fab046030b7ccaebb950e2a3489aefb2ba8a7797454b2684356481f33"
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",
@@ -3300,5 +3300,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
3300
3300
  landed since 1.24.20 with no explicit bump. run 41364831803. (task 1002620)
3301
3301
  1.24.22 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3302
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)
3303
3305
  ---------------------------------------------------------------------------
3304
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
 
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.22",
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.22",
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.22",
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",
@@ -11021,5 +11021,11 @@
11021
11021
  "id": "1005034",
11022
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
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
+ }
11024
11030
  ]
11025
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.22'; // 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', () => {