@bongos/core 1.25.2 → 1.25.3

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/bongos/package-core.js",
5
- "core_version": "1.25.2",
6
- "core_contract": "1.25.2",
7
- "source_commit": "dc57adbfa99c5fe357e8cf6722c5706199587d8a",
5
+ "core_version": "1.25.3",
6
+ "core_contract": "1.25.3",
7
+ "source_commit": "c4f7d6ed73bae03db302ca0c90610ee96156e043",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-11-12T23:01:13.126Z",
9
+ "built_at": "2026-11-12T23:23:44.026Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 654,
13
13
  "agent_docs_stubbed": 32,
14
- "functional_verbatim": 3989,
14
+ "functional_verbatim": 3991,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 4676,
20
- "tree_sha256": "80c07d7b3023e83808ed7dc00f57b85a1c44550f7fd2b51df0d44c924dd5f2ad",
19
+ "file_count": 4678,
20
+ "tree_sha256": "78a85080ecdfae842028b410b9209820a47fa490fb13c4dc466a9c260642c821",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2527,12 +2527,12 @@
2527
2527
  {
2528
2528
  "path": "docs/copy-inventory.md",
2529
2529
  "mode": "0000644",
2530
- "sha256": "0466e87434c29e6c17fc74c146a7ecf5c2d795e6817c5a8870df0cb704373c52"
2530
+ "sha256": "fcf772fb2ab9a4658e100a05e824d54fd59d3ab380f18661c9b2aff484931287"
2531
2531
  },
2532
2532
  {
2533
2533
  "path": "docs/copy-registry.json",
2534
2534
  "mode": "0000644",
2535
- "sha256": "ad843392d336c5e2f459411b80e8426e9d8c7d0ffb1bd7cd60dc076c5a3cfc4e"
2535
+ "sha256": "bdd41d04284496d42f304866aa4da2678af9a2d6fc46c16098aebc2fac6ab9f5"
2536
2536
  },
2537
2537
  {
2538
2538
  "path": "docs/design/apex-pass-2-direction.md",
@@ -3087,7 +3087,7 @@
3087
3087
  {
3088
3088
  "path": "docs/module-api-changelog.md",
3089
3089
  "mode": "0000644",
3090
- "sha256": "1d1d128fddc2d52abe04ec70862a0f5cfda31ceb6e2e266630c140402e30830a"
3090
+ "sha256": "55e32cf77c554f40cf469f40e452324fb2ec74a867eaf9c84ebaead8f2949c7c"
3091
3091
  },
3092
3092
  {
3093
3093
  "path": "docs/modules-contract.md",
@@ -3167,12 +3167,12 @@
3167
3167
  {
3168
3168
  "path": "docs/page-inventory.json",
3169
3169
  "mode": "0000644",
3170
- "sha256": "c4ecf3b3c06f25fbbc46f5f424e1cb5d5328533c7ce677143acd825753e0a0ae"
3170
+ "sha256": "db4c5144737b8fa016950d6febc43b7d0b1913bc8a79fba3149ebbad0d206346"
3171
3171
  },
3172
3172
  {
3173
3173
  "path": "docs/page-readings.json",
3174
3174
  "mode": "0000644",
3175
- "sha256": "8d1710ac1c1a2e954e990604fa84ca6d8473b775affce45227159d992566017e"
3175
+ "sha256": "fc80550a8169b44f5432f66c0d465a6badc891f7a78f8224606fac631cea433d"
3176
3176
  },
3177
3177
  {
3178
3178
  "path": "docs/project-context.template.md",
@@ -6527,7 +6527,7 @@
6527
6527
  {
6528
6528
  "path": "modules/hall-ui/public/genesis-home.js",
6529
6529
  "mode": "0000644",
6530
- "sha256": "ae19fff3052ac92cf01e981ece176cc4bf3ed6458c752e9242bce8b94b40a31a"
6530
+ "sha256": "8dd86aab78586f87ad32e1e4e1c6e135e5d0664498f8dc559290e13f1f5c7d35"
6531
6531
  },
6532
6532
  {
6533
6533
  "path": "modules/hall-ui/public/genesis/r5-life-1-primordial-sea-cut.webp",
@@ -6839,6 +6839,11 @@
6839
6839
  "mode": "0000644",
6840
6840
  "sha256": "7e6507713279ce9b41e828909cbec31b6d022a5930e2f892ab4bf3a77667e518"
6841
6841
  },
6842
+ {
6843
+ "path": "modules/hall-ui/public/index-genesis-votes.states.json",
6844
+ "mode": "0000644",
6845
+ "sha256": "facba8e7db1047d626182292b14f4f35335d2e01bc6ab765a004790376853990"
6846
+ },
6842
6847
  {
6843
6848
  "path": "modules/hall-ui/public/index-genesis.states.json",
6844
6849
  "mode": "0000644",
@@ -9257,12 +9262,12 @@
9257
9262
  {
9258
9263
  "path": "modules/onboarding/founding-mode.js",
9259
9264
  "mode": "0000644",
9260
- "sha256": "6fbf4cbc870202220ab3b0c2bcc5d084767b1027d9aa0fc44aac8fb2c024880d"
9265
+ "sha256": "493cddaa9e142a17108a26e5e7068dd6cba77ac829f4e5a87908d36319a9820b"
9261
9266
  },
9262
9267
  {
9263
9268
  "path": "modules/onboarding/genesis-home.js",
9264
9269
  "mode": "0000644",
9265
- "sha256": "622d3177047a5e0df3c45ec334500c8578353d59a5eeb7f5a5e053f3847275a8"
9270
+ "sha256": "b5aaa9beb25427ae7d6a162a793ae58768ad753cfd0b66df525d7a315c81db2e"
9266
9271
  },
9267
9272
  {
9268
9273
  "path": "modules/onboarding/howto-guide.js",
@@ -9317,7 +9322,7 @@
9317
9322
  {
9318
9323
  "path": "modules/onboarding/routes/onboarding.js",
9319
9324
  "mode": "0000644",
9320
- "sha256": "799c58b275ea528fb8606cb8ecc55d6f36cd687b8e7394b3879d516af879a9bb"
9325
+ "sha256": "8101ab2764da560bc66ced58863aa7d4ab4fa9c1832cd5e255fa16d81660f17a"
9321
9326
  },
9322
9327
  {
9323
9328
  "path": "modules/page-notes/CLAUDE.md",
@@ -11809,6 +11814,11 @@
11809
11814
  "mode": "0000644",
11810
11815
  "sha256": "a7cd9966088a8aff6f434b22153b35410ab1489e4cc5e75bc8b4c3041466d284"
11811
11816
  },
11817
+ {
11818
+ "path": "modules/ui-design/kit/fixtures/founding-genesis-votes.json",
11819
+ "mode": "0000644",
11820
+ "sha256": "a5fb8999ebd08841ff32ee75d00d1a5e0525ed84df85285caf2265f04ff0564c"
11821
+ },
11812
11822
  {
11813
11823
  "path": "modules/ui-design/kit/fixtures/government-constitution-rules.json",
11814
11824
  "mode": "0000644",
@@ -11842,12 +11852,12 @@
11842
11852
  {
11843
11853
  "path": "modules/ui-design/kit/fixtures/me-founding-demo.json",
11844
11854
  "mode": "0000644",
11845
- "sha256": "a8eba43f342e5f5af18db38fab8c71b101527449cde3b257b88f8f2bd2febdc0"
11855
+ "sha256": "5a6df239e051e304bb360dac6fec3d9539e0cfac640f9c04b14ffe38010a51a4"
11846
11856
  },
11847
11857
  {
11848
11858
  "path": "modules/ui-design/kit/fixtures/me-founding.json",
11849
11859
  "mode": "0000644",
11850
- "sha256": "6b887d467e725b5e782d220e32aaa778af6461b8adf44041badd33319129237d"
11860
+ "sha256": "3953d7188d6c3dbd51ff6c22526e1d16726a49cb73f1689f3f131a2667f86e2c"
11851
11861
  },
11852
11862
  {
11853
11863
  "path": "modules/ui-design/kit/fixtures/me-google.json",
@@ -12217,12 +12227,12 @@
12217
12227
  {
12218
12228
  "path": "package-lock.json",
12219
12229
  "mode": "0000644",
12220
- "sha256": "5d2513f948e7a8850de02637e4484ede6f5179c86b04865397d95182d8d276b2"
12230
+ "sha256": "63eaac15f7b681f2caa78e63a2fa6c7e399ee11293ac0e21c4c966d29bef3130"
12221
12231
  },
12222
12232
  {
12223
12233
  "path": "package.json",
12224
12234
  "mode": "0000644",
12225
- "sha256": "427c5862690dc27e7d377994e6168ac30cc09bca716565934fc62f8c305e4b0e"
12235
+ "sha256": "2f64a3efcaeb1fa1a7536646874f4714db027dfdf713c005acf8ebd996f7a4b4"
12226
12236
  },
12227
12237
  {
12228
12238
  "path": "public-docs/index.html",
@@ -12242,7 +12252,7 @@
12242
12252
  {
12243
12253
  "path": "release-notes.json",
12244
12254
  "mode": "0000644",
12245
- "sha256": "6903717372407e77f5a906d0dcd4b06d8aa21dd10255a19a5602228c714a3457"
12255
+ "sha256": "3bced7913b164f259612eb7ebf91756bbaf7e358b49b8944bcdfad2d2fa35c00"
12246
12256
  },
12247
12257
  {
12248
12258
  "path": "scripts/bongos/CLAUDE.md",
@@ -16252,7 +16262,7 @@
16252
16262
  {
16253
16263
  "path": "src/module-api.js",
16254
16264
  "mode": "0000644",
16255
- "sha256": "83df2597f55dcfbaf89dde346ad6e2a3b697b254a1cb00e985cc3d94df7adece"
16265
+ "sha256": "40aef4e5ba65a5465b8e6c2f13dec3278c338ea6aadfa6be0cc8390f2e496834"
16256
16266
  },
16257
16267
  {
16258
16268
  "path": "src/module-loader/catalog.js",
@@ -18242,7 +18252,7 @@
18242
18252
  {
18243
18253
  "path": "tests/founding_mode.mjs",
18244
18254
  "mode": "0000644",
18245
- "sha256": "b20b862b8d72a256dfdc476e4567008f81a2f6990a02cac67b58964740b565b6"
18255
+ "sha256": "37e3cfc000f7ab17b04a5b145a8d209bfe911664bbb272340aabbe7152ad6c3a"
18246
18256
  },
18247
18257
  {
18248
18258
  "path": "tests/funded_project_pays_proof_db.mjs",
@@ -18277,7 +18287,7 @@
18277
18287
  {
18278
18288
  "path": "tests/genesis_home.mjs",
18279
18289
  "mode": "0000644",
18280
- "sha256": "5d8ce84d50fb862ca47a5282e9877374d0cb0956ce005fe12ade3ddc90708be9"
18290
+ "sha256": "b09fc950add6cc7b919dbfe2bee8038d97cfeda64ca62972fbf7155978f2f4c4"
18281
18291
  },
18282
18292
  {
18283
18293
  "path": "tests/genesis_life_plates.mjs",
@@ -4,13 +4,13 @@
4
4
  > Deterministic on a given tree — regenerate and commit rather than hand-editing.
5
5
  > Machine-readable twin: [`copy-registry.json`](copy-registry.json).
6
6
 
7
- **3184 strings** across 3 surfaces · 2521 distinct pieces of copy · 3184 certain, 0 heuristic · 266 shown in more than one place.
7
+ **3186 strings** across 3 surfaces · 2522 distinct pieces of copy · 3186 certain, 0 heuristic · 266 shown in more than one place.
8
8
 
9
9
  ## Surfaces
10
10
 
11
11
  | surface | lives at | files | strings |
12
12
  |---|---|---|---|
13
- | `builders-hall` | `modules/hall-ui/public` | 181 | 2072 |
13
+ | `builders-hall` | `modules/hall-ui/public` | 181 | 2074 |
14
14
  | `status-dashboard` | `modules/status-ui/public` | 4 | 89 |
15
15
  | `landing` | `modules/public-landing/public` | 10 | 1023 |
16
16
 
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
78
78
  | `eff70e59d12e` | 4x | Task {…} | modules/hall-ui/public/task.js:523, modules/hall-ui/public/task.js:559, modules/hall-ui/public/task.js:1059 |
79
79
  | … | | _226 more in the JSON registry_ | |
80
80
 
81
- ## `builders-hall` — 2072 strings
81
+ ## `builders-hall` — 2074 strings
82
82
 
83
83
  | id | conf | text | where |
84
84
  |---|---|---|---|
@@ -320,7 +320,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
320
320
  | `70f6a704d504` | certain | Close it | `modules/hall-ui/public/copy-desk.html:140` |
321
321
  | `053e576ba9a3` | certain | Close options | `modules/hall-ui/public/shell-game.js:454` |
322
322
  | `d12a2fb5730d` | certain | Close this flag | `modules/hall-ui/public/copy-desk.html:116` |
323
- | `9b9c2abeae90` | certain | Close this stage | `modules/hall-ui/public/genesis-home.js:220` |
323
+ | `9b9c2abeae90` | certain | Close this stage | `modules/hall-ui/public/genesis-home.js:228` |
324
324
  | `e6f1842afde1` | certain | Close transcript | `modules/hall-ui/public/watch.html:222` |
325
325
  | `29e8c5e30095` | certain | Close without choosing | `modules/hall-ui/public/craft-mode.js:744` |
326
326
  | `3d148e19e03a` | certain | Closed. | `modules/hall-ui/public/copy-desk.js:514` |
@@ -335,7 +335,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
335
335
  | `fe4a2b04eda5` | certain | Commands — {{worldName}} | `modules/hall-ui/public/commands.html:6` |
336
336
  | `cfc287c42263` | certain | Complete your profile | `modules/hall-ui/public/profile-nudge.js:79` |
337
337
  | `140f2126c0a3` | certain | Confirm the cost | `modules/hall-ui/public/ai-studio.js:354` |
338
- | `e74f4442c338` | certain | Confirm this stage | `modules/hall-ui/public/genesis-home.js:211` |
338
+ | `e74f4442c338` | certain | Confirm this stage | `modules/hall-ui/public/genesis-home.js:219` |
339
339
  | `9b16a22bf24c` | certain | Connect Discord & join the server | `modules/hall-ui/public/settings.js:536` |
340
340
  | `c07898571b24` | certain | Connects to | `modules/hall-ui/public/atlas.js:293` |
341
341
  | `999e1955bb1c` | certain | Context layer | `modules/hall-ui/public/atlas.html:45` |
@@ -496,7 +496,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
496
496
  | `6fd9b1bf8d97` | certain | Engineering work is open on this page ({…}), so code may change around your words. You can keep writing. | `modules/hall-ui/public/tweak-editor.js:304` |
497
497
  | `690609c8e9a9` | certain | Enter a goal number. | `modules/hall-ui/public/gate.js:323` |
498
498
  | `8eabe3d69bf3` | certain | Enter files the idea. Shift and Enter starts a new line. | `modules/hall-ui/public/home-idea-box.js:55` |
499
- | `4f776bc33d5f` | certain | Enter the builders’ hall | `modules/hall-ui/public/genesis-home.js:232` |
499
+ | `4f776bc33d5f` | certain | Enter the builders’ hall | `modules/hall-ui/public/genesis-home.js:271` |
500
500
  | `1825c9c467d8` | certain | Enter the id of the task this one should wait for. | `modules/hall-ui/public/task.js:965` |
501
501
  | `bd7a40639a1c` | certain | Estimate (min) | `modules/hall-ui/public/task-detail.js:465` |
502
502
  | `c15d1f0ddfb6` | certain | Estimation accuracy | `modules/hall-ui/public/watch.html:147` |
@@ -506,7 +506,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
506
506
  | `25b6c0f9e74e` | certain | Every page is taken or on its way to the site right now. Come back soon. | `modules/hall-ui/public/studio.js:737` |
507
507
  | `c188854da766` | certain | Every place you are currently signed in — this browser, your terminal, any other machine. A session refreshes | `modules/hall-ui/public/settings.html:191` |
508
508
  | `8b2cd7714fa4` | certain | Every rank and what it unlocks, in the Primer → | `modules/hall-ui/public/profile-own.js:103` |
509
- | `823db1913949` | certain | Every room in the hall is open now. The founding builders keep their mark for good. | `modules/hall-ui/public/genesis-home.js:231` |
509
+ | `823db1913949` | certain | Every room in the hall is open now. The founding builders keep their mark for good. | `modules/hall-ui/public/genesis-home.js:270` |
510
510
  | `47be006e5ad5` | certain | Every sitting since has been decided under the constitution below, not the ratified one. Nothing is re-applied | `modules/hall-ui/public/government.js:679` |
511
511
  | `6604d63c2fb4` | certain | Everyone who joined while this project was being founded, in the order they arrived. The mark is for good. | `modules/hall-ui/public/hall-widgets.js:75` |
512
512
  | `d9927beb8cb7` | certain | Everything shipped. By role, with goals and ideas: | `modules/hall-ui/public/profile.html:112` |
@@ -532,7 +532,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
532
532
  | `e77b158b074d` | certain | Find a skill | `modules/hall-ui/public/speciality-tools.js:262` |
533
533
  | `1def0e7c716e` | certain | Find something to flag | `modules/hall-ui/public/copy-desk.html:64` |
534
534
  | `32b40fdae34f` | certain | Finding the next page… | `modules/hall-ui/public/tweak-editor.html:101` |
535
- | `f3c56ef0d809` | certain | Finish every task above, then put this stage to the board. | `modules/hall-ui/public/genesis-home.js:222` |
535
+ | `f3c56ef0d809` | certain | Finish every task above, then put this stage to the board. | `modules/hall-ui/public/genesis-home.js:230` |
536
536
  | `e8738f535783` | certain | Finish later — everything stays off until you save | `modules/hall-ui/public/speciality-tools.js:199` |
537
537
  | `defed93e8e28` | certain | Finish these steps once and you’re in. Each one ticks itself as you go. | `modules/hall-ui/public/setup-gate.js:136` |
538
538
  | `149d97457483` | certain | Fit the sky (0) | `modules/hall-ui/public/goal-map.html:80` |
@@ -705,7 +705,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
705
705
  | `0f467c1359d7` | certain | Leaderboard place | `modules/hall-ui/public/hall-render.js:470` |
706
706
  | `7c9b7a89353b` | certain | Leave goal | `modules/hall-ui/public/goals-render.js:961` |
707
707
  | `6137d2679912` | certain | Leave it off | `modules/hall-ui/public/speciality-tools.js:157` |
708
- | `e14ebcf3eec5` | certain | Legislation has passed. Genesis is complete. | `modules/hall-ui/public/genesis-home.js:202` |
708
+ | `e14ebcf3eec5` | certain | Legislation has passed. Genesis is complete. | `modules/hall-ui/public/genesis-home.js:210` |
709
709
  | `d6a78b9e5368` | certain | Let it be for now | `modules/hall-ui/public/studio.js:238` |
710
710
  | `d6a78b9e5368` | certain | Let it be for now | `modules/hall-ui/public/studio.js:253` |
711
711
  | `97282f586c55` | certain | Level {…} | `modules/hall-ui/public/market-watch-item.js:41` |
@@ -874,7 +874,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
874
874
  | `3f2dfb5a98ce` | certain | No signed works hang here yet. A page an artist tweaks, or a design that ships with its picture, hangs here on | `modules/hall-ui/public/home-gallery.js:111` |
875
875
  | `4485497d87bb` | certain | No specialities yet. Create one below — it takes a name and a craft, and you can train it afterwards. | `modules/hall-ui/public/speciality-tools.js:436` |
876
876
  | `545dc44ad427` | certain | No task in this goal depends on another task in this goal. | `modules/hall-ui/public/goal-forest-render.js:164` |
877
- | `da838f6056b1` | certain | No tasks for this stage yet. | `modules/hall-ui/public/genesis-home.js:192` |
877
+ | `da838f6056b1` | certain | No tasks for this stage yet. | `modules/hall-ui/public/genesis-home.js:200` |
878
878
  | `712b64d8273f` | certain | No tasks match these filters. Try widening them. | `modules/hall-ui/public/work.js:445` |
879
879
  | `2302ee46744e` | certain | No terminal commands found. | `modules/hall-ui/public/commands.js:45` |
880
880
  | `78f78d1d1e1f` | certain | No version is in flight right now. | `modules/hall-ui/public/roadmap.js:191` |
@@ -982,8 +982,9 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
982
982
  | `855277dff074` | certain | Open task #{…} | `modules/hall-ui/public/goals-render.js:438` |
983
983
  | `855277dff074` | certain | Open task #{…} | `modules/hall-ui/public/work.js:882` |
984
984
  | `99c630009152` | certain | Open tasks whose declared files overlap — sequence these before two builders collide. | `modules/hall-ui/public/goals.html:217` |
985
- | `fe9986a9d821` | certain | Open the Board Room | `modules/hall-ui/public/genesis-home.js:212` |
986
- | `fe9986a9d821` | certain | Open the Board Room | `modules/hall-ui/public/genesis-home.js:217` |
985
+ | `fe9986a9d821` | certain | Open the Board Room | `modules/hall-ui/public/genesis-home.js:220` |
986
+ | `fe9986a9d821` | certain | Open the Board Room | `modules/hall-ui/public/genesis-home.js:225` |
987
+ | `fe9986a9d821` | certain | Open the Board Room | `modules/hall-ui/public/genesis-home.js:261` |
987
988
  | `e073aa0c3847` | certain | Open the conflicts for {…} | `modules/hall-ui/public/goal-inbox.js:111` |
988
989
  | `676361254d92` | certain | Open the idea composer | `modules/hall-ui/public/idea.js:504` |
989
990
  | `29e1cb2d32fc` | certain | Open the recording | `modules/hall-ui/public/criterion-uat-ui.js:124` |
@@ -1076,8 +1077,9 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1076
1077
  | `995dd1ee728d` | certain | Proposed to | `modules/hall-ui/public/my-agents.js:583` |
1077
1078
  | `3f85bdc47099` | certain | Protected module scope — join by request; an Archon admits members | `modules/hall-ui/public/goals-render.js:876` |
1078
1079
  | `e5c292c8b361` | certain | Provides (seams) | `modules/hall-ui/public/atlas.js:291` |
1079
- | `93b836382ec7` | certain | Put it to the board again | `modules/hall-ui/public/genesis-home.js:216` |
1080
+ | `93b836382ec7` | certain | Put it to the board again | `modules/hall-ui/public/genesis-home.js:224` |
1080
1081
  | `55ff5cd9f7a9` | certain | Put to a vote: | `modules/hall-ui/public/my-agents.js:603` |
1082
+ | `8888a7fd45be` | certain | Put to the board | `modules/hall-ui/public/genesis-home.js:185` |
1081
1083
  | `b3b52d7a9a40` | certain | Quiet. Nothing here is due. | `modules/hall-ui/public/home-studio.js:80` |
1082
1084
  | `4de232a05888` | certain | Raise after | `modules/hall-ui/public/project-settings.js:115` |
1083
1085
  | `1a4c7be3abb0` | certain | Ran {…} | `modules/hall-ui/public/sessions.js:205` |
@@ -1353,7 +1355,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1353
1355
  | `4462dcaaaa7d` | certain | The artist reads this instead of guessing. A pointer with no judgement costs them a look and tells them nothin | `modules/hall-ui/public/copy-desk.html:103` |
1354
1356
  | `ca3708a6566a` | certain | The bench | `modules/hall-ui/public/home-studio.js:82` |
1355
1357
  | `f38140e472df` | certain | The board ratified a change that is not in force. | `modules/hall-ui/public/government.js:679` |
1356
- | `ecc7215524c0` | certain | The board sent this stage back. Read why in the Board Room, then put it up again. | `modules/hall-ui/public/genesis-home.js:215` |
1358
+ | `ecc7215524c0` | certain | The board sent this stage back. Read why in the Board Room, then put it up again. | `modules/hall-ui/public/genesis-home.js:223` |
1357
1359
  | `a335af65398d` | certain | The city | `modules/hall-ui/public/city.html:62` |
1358
1360
  | `a335af65398d` | certain | The city | `modules/hall-ui/public/city.html:62` |
1359
1361
  | `4593b431e7b7` | certain | The city — {{worldName}} | `modules/hall-ui/public/city.html:6` |
@@ -3,9 +3,9 @@
3
3
  "generator": "scripts/bongos/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 3184,
7
- "unique_ids": 2521,
8
- "certain": 3184,
6
+ "strings": 3186,
7
+ "unique_ids": 2522,
8
+ "certain": 3186,
9
9
  "heuristic": 0,
10
10
  "duplicate_ids": 266
11
11
  },
@@ -13,7 +13,7 @@
13
13
  "builders-hall": {
14
14
  "at": "modules/hall-ui/public",
15
15
  "files": 181,
16
- "strings": 2072
16
+ "strings": 2074
17
17
  },
18
18
  "status-dashboard": {
19
19
  "at": "modules/status-ui/public",
@@ -912,6 +912,17 @@
912
912
  "modules/hall-ui/public/paintbrush.js:179"
913
913
  ]
914
914
  },
915
+ {
916
+ "id": "fe9986a9d821",
917
+ "surface": "builders-hall",
918
+ "text": "Open the Board Room",
919
+ "occurrences": 3,
920
+ "at": [
921
+ "modules/hall-ui/public/genesis-home.js:220",
922
+ "modules/hall-ui/public/genesis-home.js:225",
923
+ "modules/hall-ui/public/genesis-home.js:261"
924
+ ]
925
+ },
915
926
  {
916
927
  "id": "e08628967f2f",
917
928
  "surface": "builders-hall",
@@ -2025,16 +2036,6 @@
2025
2036
  "modules/hall-ui/public/work.js:882"
2026
2037
  ]
2027
2038
  },
2028
- {
2029
- "id": "fe9986a9d821",
2030
- "surface": "builders-hall",
2031
- "text": "Open the Board Room",
2032
- "occurrences": 2,
2033
- "at": [
2034
- "modules/hall-ui/public/genesis-home.js:212",
2035
- "modules/hall-ui/public/genesis-home.js:217"
2036
- ]
2037
- },
2038
2039
  {
2039
2040
  "id": "94f3d9f46098",
2040
2041
  "surface": "builders-hall",
@@ -5244,7 +5245,7 @@
5244
5245
  "surface": "builders-hall",
5245
5246
  "text": "Close this stage",
5246
5247
  "file": "modules/hall-ui/public/genesis-home.js",
5247
- "line": 220,
5248
+ "line": 228,
5248
5249
  "origin": "js-markup",
5249
5250
  "confidence": "certain"
5250
5251
  },
@@ -5379,7 +5380,7 @@
5379
5380
  "surface": "builders-hall",
5380
5381
  "text": "Confirm this stage",
5381
5382
  "file": "modules/hall-ui/public/genesis-home.js",
5382
- "line": 211,
5383
+ "line": 219,
5383
5384
  "origin": "js-markup",
5384
5385
  "confidence": "certain"
5385
5386
  },
@@ -6828,7 +6829,7 @@
6828
6829
  "surface": "builders-hall",
6829
6830
  "text": "Enter the builders’ hall",
6830
6831
  "file": "modules/hall-ui/public/genesis-home.js",
6831
- "line": 232,
6832
+ "line": 271,
6832
6833
  "origin": "js-markup",
6833
6834
  "confidence": "certain"
6834
6835
  },
@@ -6918,7 +6919,7 @@
6918
6919
  "surface": "builders-hall",
6919
6920
  "text": "Every room in the hall is open now. The founding builders keep their mark for good.",
6920
6921
  "file": "modules/hall-ui/public/genesis-home.js",
6921
- "line": 231,
6922
+ "line": 270,
6922
6923
  "origin": "js-markup",
6923
6924
  "confidence": "certain"
6924
6925
  },
@@ -7152,7 +7153,7 @@
7152
7153
  "surface": "builders-hall",
7153
7154
  "text": "Finish every task above, then put this stage to the board.",
7154
7155
  "file": "modules/hall-ui/public/genesis-home.js",
7155
- "line": 222,
7156
+ "line": 230,
7156
7157
  "origin": "js-markup",
7157
7158
  "confidence": "certain"
7158
7159
  },
@@ -8709,7 +8710,7 @@
8709
8710
  "surface": "builders-hall",
8710
8711
  "text": "Legislation has passed. Genesis is complete.",
8711
8712
  "file": "modules/hall-ui/public/genesis-home.js",
8712
- "line": 202,
8713
+ "line": 210,
8713
8714
  "origin": "js-markup",
8714
8715
  "confidence": "certain"
8715
8716
  },
@@ -10230,7 +10231,7 @@
10230
10231
  "surface": "builders-hall",
10231
10232
  "text": "No tasks for this stage yet.",
10232
10233
  "file": "modules/hall-ui/public/genesis-home.js",
10233
- "line": 192,
10234
+ "line": 200,
10234
10235
  "origin": "js-markup",
10235
10236
  "confidence": "certain"
10236
10237
  },
@@ -11202,7 +11203,7 @@
11202
11203
  "surface": "builders-hall",
11203
11204
  "text": "Open the Board Room",
11204
11205
  "file": "modules/hall-ui/public/genesis-home.js",
11205
- "line": 212,
11206
+ "line": 220,
11206
11207
  "origin": "js-markup",
11207
11208
  "confidence": "certain"
11208
11209
  },
@@ -11211,7 +11212,16 @@
11211
11212
  "surface": "builders-hall",
11212
11213
  "text": "Open the Board Room",
11213
11214
  "file": "modules/hall-ui/public/genesis-home.js",
11214
- "line": 217,
11215
+ "line": 225,
11216
+ "origin": "js-markup",
11217
+ "confidence": "certain"
11218
+ },
11219
+ {
11220
+ "id": "fe9986a9d821",
11221
+ "surface": "builders-hall",
11222
+ "text": "Open the Board Room",
11223
+ "file": "modules/hall-ui/public/genesis-home.js",
11224
+ "line": 261,
11215
11225
  "origin": "js-markup",
11216
11226
  "confidence": "certain"
11217
11227
  },
@@ -12048,7 +12058,7 @@
12048
12058
  "surface": "builders-hall",
12049
12059
  "text": "Put it to the board again",
12050
12060
  "file": "modules/hall-ui/public/genesis-home.js",
12051
- "line": 216,
12061
+ "line": 224,
12052
12062
  "origin": "js-markup",
12053
12063
  "confidence": "certain"
12054
12064
  },
@@ -12061,6 +12071,15 @@
12061
12071
  "origin": "js-notice",
12062
12072
  "confidence": "certain"
12063
12073
  },
12074
+ {
12075
+ "id": "8888a7fd45be",
12076
+ "surface": "builders-hall",
12077
+ "text": "Put to the board",
12078
+ "file": "modules/hall-ui/public/genesis-home.js",
12079
+ "line": 185,
12080
+ "origin": "js-markup",
12081
+ "confidence": "certain"
12082
+ },
12064
12083
  {
12065
12084
  "id": "b3b52d7a9a40",
12066
12085
  "surface": "builders-hall",
@@ -14541,7 +14560,7 @@
14541
14560
  "surface": "builders-hall",
14542
14561
  "text": "The board sent this stage back. Read why in the Board Room, then put it up again.",
14543
14562
  "file": "modules/hall-ui/public/genesis-home.js",
14544
- "line": 215,
14563
+ "line": 223,
14545
14564
  "origin": "js-markup",
14546
14565
  "confidence": "certain"
14547
14566
  },
@@ -3446,5 +3446,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
3446
3446
  landed since 1.25.0 with no explicit bump. run 43313162829. (task 1002620)
3447
3447
  1.25.2 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3448
3448
  landed since 1.25.1 with no explicit bump. run 43325008692. (task 1002620)
3449
+ 1.25.3 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3450
+ landed since 1.25.2 with no explicit bump. run 43328266045. (task 1002620)
3449
3451
  ---------------------------------------------------------------------------
3450
3452
  ```
@@ -13,7 +13,7 @@
13
13
  "pages": 45,
14
14
  "surfaces": 3,
15
15
  "with_states": 39,
16
- "states": 367
16
+ "states": 368
17
17
  },
18
18
  "surfaces": [
19
19
  {
@@ -587,7 +587,8 @@
587
587
  "modules/hall-ui/public/index-founding.states.json",
588
588
  "modules/hall-ui/public/index-genesis.states.json",
589
589
  "modules/hall-ui/public/index-genesis-decide.states.json",
590
- "modules/hall-ui/public/index-genesis-demo.states.json"
590
+ "modules/hall-ui/public/index-genesis-demo.states.json",
591
+ "modules/hall-ui/public/index-genesis-votes.states.json"
591
592
  ],
592
593
  "states": [
593
594
  "home",
@@ -595,7 +596,8 @@
595
596
  "founding",
596
597
  "genesis",
597
598
  "decide",
598
- "demo"
599
+ "demo",
600
+ "votes"
599
601
  ]
600
602
  },
601
603
  {
@@ -953,8 +953,8 @@
953
953
  "id": "builders:home",
954
954
  "surface": "builders",
955
955
  "title": "Builders' Hall",
956
- "states_read": ["home","out","founding","genesis","decide","demo"],
957
- "files_hash": "b3b116e114d183f4",
956
+ "states_read": ["home","out","founding","genesis","decide","demo","votes"],
957
+ "files_hash": "efb01ef72961e8cf",
958
958
  "reading_hash": "59797028e6124c9c",
959
959
  "counts": {"lines":32,"placed":12,"shared":8,"unplaced":12},
960
960
  "omitted": {"data":5},
@@ -28,7 +28,7 @@
28
28
  var DAY_CHOICES = [1, 2, 3, 4, 5, 7, 10, 14, 21, 30];
29
29
  var REFUSED = {
30
30
  stage_tasks_unfinished: 'This stage still has unfinished tasks.',
31
- not_current_stage: 'That stage is not the one in progress.',
31
+ not_current_stage: 'That stage is already behind you.',
32
32
  legislation_not_passed: 'Legislation has to pass the Board Room first.',
33
33
  not_deciding: 'The plan is already settled.',
34
34
  };
@@ -159,9 +159,15 @@
159
159
  }).join('') + '</ol>';
160
160
  }
161
161
 
162
+ // task 1005199: a later stage can already be in the Board Room, or passed and
163
+ // waiting for the stages before it, so its line says so instead of just "later".
162
164
  function stateText(s) {
163
165
  if (s.state === 'done') return 'done';
164
- if (s.state === 'later') return 'later';
166
+ var b = s.board && s.board.state;
167
+ if (b === 'sitting') return 'in the Board Room';
168
+ if (b === 'passed') return s.state === 'later' ? 'passed, waits its turn' : 'passed';
169
+ if (b === 'returned') return 'sent back';
170
+ if (s.state === 'later') return s.total && s.finished === s.total ? 'ready for the board' : 'later';
165
171
  return s.total ? s.finished + ' of ' + s.total + ' done' : 'in progress';
166
172
  }
167
173
 
@@ -175,7 +181,9 @@
175
181
  return '<li class="genesis__stage is-' + s.state + '">'
176
182
  + '<span class="genesis__stage-n">0' + s.number + '</span>'
177
183
  + '<span class="genesis__stage-t"><b>' + esc(s.label) + '</b>'
178
- + '<span>' + esc(wakes.length ? 'Wakes ' + wakes.join(', ') : '') + esc(due) + '</span></span>'
184
+ + '<span>' + esc(wakes.length ? 'Wakes ' + wakes.join(', ') : '') + esc(due) + '</span>'
185
+ + (s.state === 'later' && s.can_close ? '<span><button type="button" class="btn-ghost" data-genesis-close="' + esc(s.key) + '">Put to the board</button></span>' : '')
186
+ + '</span>'
179
187
  + '<span class="genesis__stage-st">' + esc(stateText(s)) + '</span></li>';
180
188
  }).join('') + '</ol>';
181
189
  }
@@ -222,6 +230,37 @@
222
230
  return '<p class="genesis__note">Finish every task above, then put this stage to the board.</p>';
223
231
  }
224
232
 
233
+ // ── every stage vote waiting on the viewer, in one place (task 1005199) ──────
234
+ // Each finished stage sits in the Board Room at once; this lists the ones still
235
+ // waiting on THIS viewer's vote, in stage order, and one button votes yes on all of
236
+ // them in that order. They still pass in order: the project only moves past a
237
+ // stage whose own sitting passed.
238
+ function votesWaiting(g, viewerId) {
239
+ return g.stages.filter(function (s) {
240
+ var b = s.board;
241
+ return b && b.state === 'sitting' && b.item_id && (b.waiting_on || []).some(function (m) {
242
+ return String(m.builder_id) === String(viewerId);
243
+ });
244
+ });
245
+ }
246
+
247
+ function votesHtml(g, viewerId) {
248
+ var list = votesWaiting(g, viewerId);
249
+ // The current stage's own Confirm button already covers a lone vote on it.
250
+ if (!list.length || (list.length === 1 && list[0].state === 'now')) return '';
251
+ var many = list.length > 1;
252
+ return '<section class="genesis__now" aria-labelledby="genesis-votes-h">'
253
+ + '<h3 id="genesis-votes-h" class="genesis__now-h">Waiting for your vote · ' + list.length + (many ? ' stages' : ' stage') + '</h3>'
254
+ + '<ul class="genesis__tasks">' + list.map(function (s) {
255
+ return '<li class="genesis__task"><span class="genesis__task-t">0' + s.number + ' ' + esc(s.label) + '</span></li>';
256
+ }).join('') + '</ul>'
257
+ // The rule is said once: every stage put up under one constitution reads the same.
258
+ + '<p class="genesis__note">' + esc(list[0].board.needs ? list[0].board.needs + ' ' : '')
259
+ + 'They pass in order: a later stage waits for the ones before it.</p>'
260
+ + '<div class="genesis__doors"><button type="button" class="btn-accent" data-genesis-vote-all>' + (many ? 'Vote yes on all ' + list.length : 'Vote yes') + '</button>'
261
+ + '<a class="btn-ghost" href="/board-room">Open the Board Room</a></div></section>';
262
+ }
263
+
225
264
  function aliveHtml() {
226
265
  var name = projectName();
227
266
  return '<div class="genesis__alive">'
@@ -254,7 +293,7 @@
254
293
  if (!g) { host.hidden = true; return; }
255
294
  host.innerHTML = headHtml(g) + (g.deciding
256
295
  ? decideHtml(g, state.gov)
257
- : demoHtml(g) + lifeHtml(g) + '<div class="genesis__grid">' + stagesHtml(g) + nowHtml(g, state.viewer) + '</div>');
296
+ : demoHtml(g) + lifeHtml(g) + votesHtml(g, state.viewer) + '<div class="genesis__grid">' + stagesHtml(g) + nowHtml(g, state.viewer) + '</div>');
258
297
  host.hidden = false;
259
298
  }
260
299
 
@@ -345,6 +384,17 @@
345
384
  } else if (t.hasAttribute('data-genesis-confirm')) {
346
385
  act(send('POST', '/government/board/items/' + encodeURIComponent(t.getAttribute('data-genesis-confirm')) + '/votes', { direction: 'yes' })
347
386
  .then(function () { return load(host, state); }));
387
+ } else if (t.hasAttribute('data-genesis-vote-all')) {
388
+ // One yes per stage, earliest first, each after the last has landed (task
389
+ // 1005199). A refusal on one stage stops the run and says why; the rest stay.
390
+ var ids = votesWaiting(state.genesis, state.viewer).map(function (s) { return s.board.item_id; });
391
+ act(ids.reduce(function (p, id) {
392
+ return p.then(function () {
393
+ return send('POST', '/government/board/items/' + encodeURIComponent(id) + '/votes', { direction: 'yes' });
394
+ });
395
+ }, Promise.resolve()).then(function () { return load(host, state); }, function (err) {
396
+ return load(host, state).then(function () { throw err; });
397
+ }));
348
398
  } else if (t.hasAttribute('data-genesis-alive')) {
349
399
  act(send('POST', '/founding/alive', {}).then(function () { host.innerHTML = aliveHtml(); }));
350
400
  } else if (t.hasAttribute('data-genesis-enter')) {
@@ -0,0 +1,61 @@
1
+ {
2
+ "_": "Several genesis stages sitting in the Board Room at once, with the vote-on-all panel (task 1005199). Same shell as index-genesis.states.json; /founding/genesis answers founding-genesis-votes.json, built from the real server shaper.",
3
+ "page": "/builders/",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": {
6
+ "session": {
7
+ "hall-craft-asked": "1"
8
+ }
9
+ },
10
+ "surface": "hall-ui",
11
+ "stub": {
12
+ "prefix": "/builders/",
13
+ "fixtures": {
14
+ "me": "me-founding",
15
+ "founding-genesis": "founding-genesis-votes"
16
+ }
17
+ },
18
+ "modeQuery": false,
19
+ "ignoreRequests": [
20
+ "/api/(gds|bongos)/me$",
21
+ "/api/(gds|bongos)/me/",
22
+ "/api/(gds|bongos)/tasks",
23
+ "/api/(gds|bongos)/claims",
24
+ "/api/(gds|bongos)/goals",
25
+ "/api/(gds|bongos)/inbox",
26
+ "/api/(gds|bongos)/live",
27
+ "/api/(gds|bongos)/analytics/",
28
+ "/api/(gds|bongos)/achievements",
29
+ "/api/(gds|bongos)/versions",
30
+ "/api/(gds|bongos)/leaderboard",
31
+ "/api/(gds|bongos)/removal-proposals",
32
+ "/api/(gds|bongos)/blockers"
33
+ ],
34
+ "_ignored": "removal-proposals and blockers are fixtures the kit stub does not carry; index.states.json's home render reports the same two 404s.",
35
+ "ignoreConsole": [
36
+ "/api/(gds|bongos)/",
37
+ "\\b401\\b",
38
+ "\\b404\\b",
39
+ "Unauthorized",
40
+ "Not Found",
41
+ "load failed"
42
+ ],
43
+ "states": {
44
+ "votes": {
45
+ "auth": true,
46
+ "actions": [
47
+ [
48
+ "wait",
49
+ 800
50
+ ]
51
+ ],
52
+ "expect": {
53
+ "visible": [
54
+ "#genesis-home",
55
+ "#founding-band",
56
+ "#genesis-votes-h"
57
+ ]
58
+ }
59
+ }
60
+ }
61
+ }
@@ -37,7 +37,10 @@ const WAKES = Object.freeze({
37
37
  // task 1005051: Roadmap folded into Goals, so scope wakes Goals and its map.
38
38
  scope: Object.freeze(['goals', 'goal-map']),
39
39
  strategize: Object.freeze(['work']),
40
- charter: Object.freeze(['board-room']),
40
+ // task 1005199: charter woke the Board Room, but scope and strategize are already
41
+ // closed by a vote there, so the room read as locked while the founder had a ballot
42
+ // in it. The Board Room is awake for the whole of genesis; charter wakes no room.
43
+ charter: Object.freeze([]),
41
44
  // task 1005046: legalize woke Economy and legislation woke Ranks. Both pages
42
45
  // are Primer chapters now (one reading room), and the Primer never sleeps —
43
46
  // it is the newcomer's first read — so these two stages wake no sidebar room.
@@ -156,6 +156,8 @@ function shapeGenesis({ mode, plan, tasks = {}, board = {}, stageAt = {} }) {
156
156
  const idx = fm.STAGES.indexOf(key);
157
157
  const list = (tasks[key] || []).map((t) => ({ ...t, finished: FINISHED.includes(t.status) }));
158
158
  const b = board[key] || null;
159
+ const allFinished = list.length > 0 && list.every((t) => t.finished);
160
+ const onBoard = !!(b && (b.state === 'sitting' || b.state === 'passed'));
159
161
  const days = plan && plan.days && plan.days[key] ? plan.days[key] : info.days;
160
162
  // A demo's stage is minutes long (task 1004506); its due time is counted in them.
161
163
  const minutes = plan && plan.minutes && plan.minutes[key] ? plan.minutes[key] : null;
@@ -177,6 +179,9 @@ function shapeGenesis({ mode, plan, tasks = {}, board = {}, stageAt = {} }) {
177
179
  tasks: list,
178
180
  finished: list.filter((t) => t.finished).length,
179
181
  total: list.length,
182
+ // task 1005199: this stage (the current one or any later one) can be put to
183
+ // the Board Room now — its tasks are all finished and it is not already there.
184
+ can_close: at >= 1 && idx >= at && allFinished && !onBoard,
180
185
  board: b ? {
181
186
  state: b.state, item_id: b.item_id || null, needs: b.needs || null,
182
187
  one_vote_decides: !!b.one_vote_decides, waiting_on: b.waiting_on || [],
@@ -349,7 +354,15 @@ async function closeStage(stageKey, builderId, deps = {}) {
349
354
  const s = seams(deps);
350
355
  const mode = await reconcile(s);
351
356
  if (!mode) return { ok: false, reason: 'not_founding' };
352
- if (mode.stage !== stageKey) return { ok: false, reason: 'not_current_stage', stage: mode.stage };
357
+ // task 1005199: any stage at or AFTER the current one may be put up, so every
358
+ // finished stage can sit in the Board Room at once. Order is kept by reconcile, not
359
+ // here: the project only moves past a stage whose own sitting passed, so a later
360
+ // stage's pass waits for the earlier ones. A stage already behind, or the decide
361
+ // pre-stage, is still refused.
362
+ if (!PROPER.includes(stageKey)) return { ok: false, reason: 'unknown_stage' };
363
+ if (!PROPER.includes(mode.stage) || fm.STAGES.indexOf(stageKey) < fm.STAGES.indexOf(mode.stage)) {
364
+ return { ok: false, reason: 'not_current_stage', stage: mode.stage };
365
+ }
353
366
  const all = s.lifecycle && typeof s.lifecycle.genesisTasks === 'function' ? await s.lifecycle.genesisTasks() : {};
354
367
  const list = all[stageKey] || [];
355
368
  const taskIds = list.map((t) => t.id);
@@ -357,9 +370,13 @@ async function closeStage(stageKey, builderId, deps = {}) {
357
370
  const nextOf = (key) => (key === 'legislation' ? null : fm.STAGES[fm.STAGES.indexOf(key) + 1]);
358
371
  const closedAnswer = async (itemId, confirm) => {
359
372
  const after = await reconcile(s);
373
+ const at = after && after.stage;
360
374
  return {
361
375
  ok: true, closed: true, stage: stageKey,
362
- next_stage: stageKey === 'legislation' ? null : (after && after.stage) || nextOf(stageKey),
376
+ next_stage: stageKey === 'legislation' && at === 'legislation' ? null : at || nextOf(stageKey),
377
+ // task 1005199: a later stage passed while an earlier one is still open, so
378
+ // the project stays at the earlier stage until that one passes too.
379
+ waits_for_earlier: !!(at && fm.STAGES.indexOf(at) < fm.STAGES.indexOf(stageKey)),
363
380
  item_id: itemId, confirm,
364
381
  };
365
382
  };
@@ -379,6 +396,9 @@ async function closeStage(stageKey, builderId, deps = {}) {
379
396
  return sittingOpen(stageKey, r.item ? String(r.item.id) : null, !r.opened, board[stageKey] || null, builderId);
380
397
  }
381
398
  if (!list.length || !list.every((t) => FINISHED.includes(t.status))) return { ok: false, reason: 'stage_tasks_unfinished' };
399
+ // With no Board Room there is no sitting to hold a later stage, so only the
400
+ // current one can be closed here; a later one waits its turn.
401
+ if (stageKey !== mode.stage) return { ok: false, reason: 'not_current_stage', stage: mode.stage };
382
402
  if (stageKey !== 'legislation') {
383
403
  await fm.setFoundingMode({ stage: nextOf(stageKey) }, builderId, modeDeps(s));
384
404
  }
@@ -452,9 +472,44 @@ async function onStagePassed(deps = {}) {
452
472
  try { await reconcile(seams(deps)); } catch (_) { /* recovered on the next read */ }
453
473
  }
454
474
 
475
+ // putFinishedStages — shipping a stage puts it in the Board Room by itself (task
476
+ // 1005199). Every stage at or after the current one whose tasks are all finished and
477
+ // that has NEVER been put to the board is opened as a sitting, with nobody's vote cast:
478
+ // the founder still says yes, but no longer has to press "close" first. A stage the
479
+ // board sent back is left for the founder to put up again on purpose. Opening is the
480
+ // government port's own gate (finished tasks, one sitting per stage), so a race with a
481
+ // manual close opens one sitting, not two. Returns the stage keys it put up.
482
+ async function putFinishedStages(deps = {}) {
483
+ const s = seams(deps);
484
+ const gov = s.government;
485
+ if (!gov || typeof gov.openGenesisStageClose !== 'function') return [];
486
+ const mode = await reconcile(s);
487
+ if (!mode || !PROPER.includes(mode.stage)) return [];
488
+ const all = s.lifecycle && typeof s.lifecycle.genesisTasks === 'function' ? await s.lifecycle.genesisTasks() : {};
489
+ const board = await boardByStage(gov);
490
+ const put = [];
491
+ for (const key of PROPER.slice(PROPER.indexOf(mode.stage))) {
492
+ const list = all[key] || [];
493
+ if (!list.length || !list.every((t) => FINISHED.includes(t.status))) continue;
494
+ const st = board[key] && board[key].state;
495
+ if (st && st !== 'not_put') continue;
496
+ const r = await gov.openGenesisStageClose({ stageKey: key, taskIds: list.map((t) => t.id), openedBy: null });
497
+ if (r && r.opened) put.push(key);
498
+ }
499
+ return put;
500
+ }
501
+
502
+ // onTaskShipped — the `task.shipped` listener. Any ship may be the last task of a
503
+ // stage; on a project that is not founding this costs the one genesis.enabled read.
504
+ // Best-effort: the founder's own close button is still there if this misses.
505
+ async function onTaskShipped(deps = {}) {
506
+ try { return await putFinishedStages(deps); } catch (_) { return []; }
507
+ }
508
+
455
509
  module.exports = {
456
510
  PLAN_KEY, STAGE_INFO, PROPER, DEMO_PEOPLE, DEMO_HOURS, intIn, parsePlan, shapeGenesis, demoMinutes, demoPlan,
457
511
  readGenesis, startFounding, savePlan, applyGovernmentChoice, closeStage, comeAlive, skipGenesis, onStagePassed,
512
+ putFinishedStages, onTaskShipped,
458
513
  // the founding-mode deps this file reads the mode with, for founding-birth.js (task 1004506)
459
514
  modeDepsFor: (deps) => modeDeps(seams(deps)),
460
515
  };
@@ -134,6 +134,8 @@ function mountGenesisRoutes(router) {
134
134
  if (!genesisListening && typeof api.on === 'function') {
135
135
  genesisListening = true;
136
136
  api.on('board.genesis_stage.passed', () => genesis.onStagePassed());
137
+ // task 1005199: a ship that finishes a stage puts that stage in the Board Room.
138
+ api.on('task.shipped', () => genesis.onTaskShipped());
137
139
  }
138
140
  const owns = api.requirePermission('project.settings.manage');
139
141
  const refuse = (res, r) => res.status(GENESIS_REFUSALS[r.reason] || 409).json({
@@ -0,0 +1,193 @@
1
+ {
2
+ "_": "Genesis with several stages in the Board Room at once (task 1005199): scope, strategize and charter sitting on the founder, legalize finished and not yet put up, legislation in progress.",
3
+ "genesis": {
4
+ "stage": "scope",
5
+ "stage_number": 1,
6
+ "stage_count": 5,
7
+ "day": 2,
8
+ "deciding": false,
9
+ "plan": {
10
+ "days": {
11
+ "scope": 7,
12
+ "strategize": 7,
13
+ "charter": 4,
14
+ "legalize": 7,
15
+ "legislation": 3
16
+ },
17
+ "passes": 1,
18
+ "advisors": "none",
19
+ "government": "monarchy",
20
+ "expected_builders": 1,
21
+ "existing": false
22
+ },
23
+ "demo": null,
24
+ "stages": [
25
+ {
26
+ "key": "scope",
27
+ "number": 1,
28
+ "label": "Scope",
29
+ "life": "primordial sea",
30
+ "plate": "r5-life-1-primordial-sea",
31
+ "done_when": "The scope is written: what you are making, for whom, and what is left out.",
32
+ "wakes": [
33
+ "goals",
34
+ "goal-map"
35
+ ],
36
+ "days": 7,
37
+ "minutes": null,
38
+ "due": "2026-11-17T09:00:00.000Z",
39
+ "state": "now",
40
+ "tasks": [
41
+ {
42
+ "id": "2001",
43
+ "title": "Scope: write it down",
44
+ "status": "shipped",
45
+ "finished": true
46
+ }
47
+ ],
48
+ "finished": 1,
49
+ "total": 1,
50
+ "can_close": false,
51
+ "board": {
52
+ "state": "sitting",
53
+ "item_id": "41",
54
+ "needs": "One yes decides: the first member to say yes closes it.",
55
+ "one_vote_decides": true,
56
+ "waiting_on": [
57
+ {
58
+ "builder_id": "1",
59
+ "display_name": "Founder"
60
+ }
61
+ ]
62
+ }
63
+ },
64
+ {
65
+ "key": "strategize",
66
+ "number": 2,
67
+ "label": "Strategize",
68
+ "life": "first cells",
69
+ "plate": "r5-life-2-first-cells",
70
+ "done_when": "The strategy is written out, or the one you have is linked.",
71
+ "wakes": [
72
+ "work"
73
+ ],
74
+ "days": 7,
75
+ "minutes": null,
76
+ "due": null,
77
+ "state": "later",
78
+ "tasks": [
79
+ {
80
+ "id": "2002",
81
+ "title": "Strategize: write it down",
82
+ "status": "shipped",
83
+ "finished": true
84
+ }
85
+ ],
86
+ "finished": 1,
87
+ "total": 1,
88
+ "can_close": false,
89
+ "board": {
90
+ "state": "sitting",
91
+ "item_id": "42",
92
+ "needs": "One yes decides: the first member to say yes closes it.",
93
+ "one_vote_decides": true,
94
+ "waiting_on": [
95
+ {
96
+ "builder_id": "1",
97
+ "display_name": "Founder"
98
+ }
99
+ ]
100
+ }
101
+ },
102
+ {
103
+ "key": "charter",
104
+ "number": 3,
105
+ "label": "Charter",
106
+ "life": "the genome",
107
+ "plate": "r5-life-3-genome",
108
+ "done_when": "The starting government is chosen and the bylaws are written.",
109
+ "wakes": [],
110
+ "days": 4,
111
+ "minutes": null,
112
+ "due": null,
113
+ "state": "later",
114
+ "tasks": [
115
+ {
116
+ "id": "2003",
117
+ "title": "Charter: write it down",
118
+ "status": "shipped",
119
+ "finished": true
120
+ }
121
+ ],
122
+ "finished": 1,
123
+ "total": 1,
124
+ "can_close": false,
125
+ "board": {
126
+ "state": "sitting",
127
+ "item_id": "43",
128
+ "needs": "One yes decides: the first member to say yes closes it.",
129
+ "one_vote_decides": true,
130
+ "waiting_on": [
131
+ {
132
+ "builder_id": "1",
133
+ "display_name": "Founder"
134
+ }
135
+ ]
136
+ }
137
+ },
138
+ {
139
+ "key": "legalize",
140
+ "number": 4,
141
+ "label": "Legalize",
142
+ "life": "the membrane",
143
+ "plate": "r5-life-4-membrane",
144
+ "done_when": "Rewards are decided, and who can be paid.",
145
+ "wakes": [],
146
+ "days": 7,
147
+ "minutes": null,
148
+ "due": null,
149
+ "state": "later",
150
+ "tasks": [
151
+ {
152
+ "id": "2004",
153
+ "title": "Legalize: write it down",
154
+ "status": "shipped",
155
+ "finished": true
156
+ }
157
+ ],
158
+ "finished": 1,
159
+ "total": 1,
160
+ "can_close": true,
161
+ "board": null
162
+ },
163
+ {
164
+ "key": "legislation",
165
+ "number": 5,
166
+ "label": "Legislation",
167
+ "life": "the nervous system",
168
+ "plate": "r5-life-5-nervous-system",
169
+ "done_when": "The voting rules are set: who votes, and how many must agree.",
170
+ "wakes": [],
171
+ "days": 3,
172
+ "minutes": null,
173
+ "due": null,
174
+ "state": "later",
175
+ "tasks": [
176
+ {
177
+ "id": "2005",
178
+ "title": "Legislation: write it down",
179
+ "status": "active",
180
+ "finished": false
181
+ }
182
+ ],
183
+ "finished": 0,
184
+ "total": 1,
185
+ "can_close": false,
186
+ "board": null
187
+ }
188
+ ],
189
+ "can_close": false,
190
+ "can_come_alive": false
191
+ },
192
+ "viewer_id": "1"
193
+ }
@@ -17,7 +17,6 @@
17
17
  "goals": "scope",
18
18
  "goal-map": "scope",
19
19
  "work": "strategize",
20
- "board-room": "charter",
21
20
  "economy": "legalize",
22
21
  "ranks": "legislation",
23
22
  "collab": "alive",
@@ -14,7 +14,6 @@
14
14
  "day": 9,
15
15
  "asleep": {
16
16
  "work": "strategize",
17
- "board-room": "charter",
18
17
  "economy": "legalize",
19
18
  "ranks": "legislation",
20
19
  "collab": "alive",
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.25.2",
3
+ "version": "1.25.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.25.2",
9
+ "version": "1.25.3",
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.25.2",
3
+ "version": "1.25.3",
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",
@@ -11706,5 +11706,11 @@
11706
11706
  "id": "1005248",
11707
11707
  "text": "In game mode, no two buttons on the visor share a hotkey any more, and Options now sits only inside the Actions menu with its own key."
11708
11708
  }
11709
+ ],
11710
+ "1.25.3": [
11711
+ {
11712
+ "id": "1005199",
11713
+ "text": "Founding a project is faster: each finished stage goes to the Board Room by itself, every stage can be voted on at once with one \"vote yes on all\" button, and the Board Room no longer looks locked during genesis."
11714
+ }
11709
11715
  ]
11710
11716
  }
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.25.2'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.25.3'; // 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');
@@ -38,22 +38,32 @@ test('an unknown stage fails CLOSED — OFF, never a hall stuck half-asleep', ()
38
38
  assert.equal(fm.shapeFoundingMode({ active: 'true', stage: 'launch' }), null);
39
39
  });
40
40
 
41
- test('active with no stage yet is the decide pre-stage, and every room sleeps', () => {
41
+ test('active with no stage yet is the decide pre-stage, and every room but the Board Room sleeps', () => {
42
42
  const f = fm.shapeFoundingMode({ active: 'true' });
43
43
  assert.equal(f.stage, 'decide');
44
44
  assert.equal(f.stage_number, 0);
45
45
  assert.equal(f.stage_count, 5);
46
46
  // task 1005051: Roadmap folded into Goals; scope wakes Goals and its map.
47
- for (const id of ['goals', 'goal-map', 'work', 'board-room', 'collab', 'roster']) {
47
+ for (const id of ['goals', 'goal-map', 'work', 'collab', 'roster']) {
48
48
  assert.ok(id in f.asleep, `${id} sleeps while deciding`);
49
49
  }
50
50
  });
51
51
 
52
+ // task 1005199: every stage, from scope on, is closed by a vote in the Board Room, so
53
+ // the room must never read as locked while genesis has a ballot in it.
54
+ test('the Board Room is awake at every stage of genesis', () => {
55
+ for (const stage of fm.STAGES) {
56
+ const f = fm.shapeFoundingMode({ active: 'true', stage });
57
+ assert.ok(!('board-room' in f.asleep), `board-room is awake at ${stage}`);
58
+ }
59
+ assert.ok(!Object.values(fm.WAKES).some((ids) => ids.includes('board-room')), 'no stage wakes it, because it never sleeps');
60
+ });
61
+
52
62
  test('a room wakes once the stage that wakes it is behind', () => {
53
63
  const f = fm.shapeFoundingMode({ active: 'true', stage: 'charter' });
54
64
  for (const id of ['goals', 'goal-map', 'work']) assert.ok(!(id in f.asleep), `${id} woke`);
55
65
  assert.ok(!('roadmap' in f.asleep), 'the folded Roadmap is no room to wake');
56
- assert.equal(f.asleep['board-room'], 'charter');
66
+ assert.ok(!('board-room' in f.asleep), 'the Board Room never sleeps (task 1005199)');
57
67
  // task 1005046: Economy and Ranks were the rooms legalize and legislation woke;
58
68
  // they are Primer chapters now, and the Primer never sleeps.
59
69
  assert.ok(!('economy' in f.asleep) && !('ranks' in f.asleep), 'no retired Docs page is put to sleep');
@@ -249,13 +249,65 @@ test('the owner can skip genesis from any stage: no sitting, hall opens, recorde
249
249
  assert.equal(await gh.readGenesis(mid.deps()), null);
250
250
  });
251
251
 
252
- test('only the stage in progress can be put to the board', async () => {
252
+ test('a later stage can be put to the board once finished; a stage behind cannot (task 1005199)', async () => {
253
253
  const w = world();
254
254
  await gh.startFounding(1, w.deps());
255
255
  await gh.savePlan({}, 1, w.deps());
256
+ assert.equal((await gh.closeStage('charter', 1, w.deps())).reason, 'stage_tasks_unfinished', 'the finished-tasks gate still holds');
257
+ w.finish('charter');
256
258
  const r = await gh.closeStage('charter', 1, w.deps());
257
- assert.equal(r.reason, 'not_current_stage');
258
- assert.equal(w.opened.length, 0);
259
+ assert.equal(r.reason, 'sitting_open', 'charter sits while the project is still at scope');
260
+ assert.deepEqual(w.opened.at(-1).stageKey, 'charter');
261
+ assert.equal((await gh.readGenesis(w.deps())).stage, 'scope');
262
+ w.finish('scope'); w.pass('scope'); await gh.onStagePassed(w.deps());
263
+ assert.equal((await gh.closeStage('scope', 1, w.deps())).reason, 'not_current_stage', 'a stage behind is refused');
264
+ assert.equal((await gh.closeStage('decide', 1, w.deps())).reason, 'unknown_stage');
265
+ });
266
+
267
+ test('every finished stage sits at once, and they pass in order (task 1005199)', async () => {
268
+ const w = world();
269
+ await gh.startFounding(1, w.deps());
270
+ await gh.savePlan({}, 1, w.deps());
271
+ for (const s of STAGES5) w.finish(s);
272
+ let g = await gh.readGenesis(w.deps());
273
+ assert.deepEqual(g.stages.map((s) => s.can_close), [true, true, true, true, true], 'every finished stage can be put up');
274
+ for (const s of STAGES5) await gh.closeStage(s, 1, w.deps());
275
+ g = await gh.readGenesis(w.deps());
276
+ assert.deepEqual(g.stages.map((s) => s.board && s.board.state), Array(5).fill('sitting'));
277
+ assert.deepEqual(g.stages.map((s) => s.can_close), [false, false, false, false, false], 'a sitting stage is not put up twice');
278
+ // A later pass does not move the project past an earlier stage still sitting.
279
+ w.pass('charter'); w.pass('legalize');
280
+ await gh.onStagePassed(w.deps());
281
+ assert.equal((await gh.readGenesis(w.deps())).stage, 'scope', 'scope still sits: the project waits for it');
282
+ w.pass('scope'); await gh.onStagePassed(w.deps());
283
+ assert.equal((await gh.readGenesis(w.deps())).stage, 'strategize', 'scope passed; strategize still sits, so the project stops there');
284
+ w.pass('strategize'); await gh.onStagePassed(w.deps());
285
+ assert.equal((await gh.readGenesis(w.deps())).stage, 'legislation', 'strategize passed: charter and legalize had passed already');
286
+ w.pass('legislation'); await gh.onStagePassed(w.deps());
287
+ g = await gh.readGenesis(w.deps());
288
+ assert.equal(g.can_come_alive, true, 'one click left: bring it to life');
289
+ assert.equal((await gh.comeAlive(1, w.deps())).ok, true);
290
+ });
291
+
292
+ test('shipping the last task of a stage puts it in the Board Room by itself (task 1005199)', async () => {
293
+ const w = world();
294
+ assert.deepEqual(await gh.onTaskShipped(w.deps()), [], 'not founding: nothing is put up');
295
+ await gh.startFounding(1, w.deps());
296
+ await gh.savePlan({}, 1, w.deps());
297
+ assert.deepEqual(await gh.onTaskShipped(w.deps()), [], 'no stage is finished yet');
298
+ w.finish('scope'); w.finish('legalize');
299
+ assert.deepEqual(await gh.onTaskShipped(w.deps()), ['scope', 'legalize'], 'every finished stage, current and later');
300
+ assert.equal(w.opened.length, 2);
301
+ assert.deepEqual(await gh.onTaskShipped(w.deps()), [], 'a stage already sitting is not put up again');
302
+ w.board.strategize = { state: 'returned', waiting_on: [] };
303
+ w.finish('strategize');
304
+ assert.deepEqual(await gh.onTaskShipped(w.deps()), [], 'a stage the board sent back waits for the founder');
305
+ assert.deepEqual(await gh.onTaskShipped({ ...w.deps(), government: { genesisStageStatus: async () => { throw new Error('down'); }, openGenesisStageClose: async () => ({}) } }), [], 'a failure never throws into the ship');
306
+ });
307
+
308
+ test('the route factory listens for ships (task 1005199)', () => {
309
+ const src = fs.readFileSync(path.join(ROOT, 'modules', 'onboarding', 'routes', 'onboarding.js'), 'utf8');
310
+ assert.match(src, /api\.on\('task\.shipped', \(\) => genesis\.onTaskShipped\(\)\)/);
259
311
  });
260
312
 
261
313
  test('a returned stage stays put and can be put up again', async () => {
@@ -316,7 +368,7 @@ function bootCard({ founding, data = { genesis: null } }) {
316
368
  const attrs = founding ? { 'data-founding': 'on' } : {};
317
369
  const sandbox = {
318
370
  window: {
319
- OTB: { escapeHtml: (s) => String(s), contributeHallWidget: (w) => { widget = w; } },
371
+ OTB: { escapeHtml: (s) => String(s), statusPill: (s) => `<span>${s}</span>`, contributeHallWidget: (w) => { widget = w; } },
320
372
  BongosClient: { createClient: () => ({ request: async (m, p) => { calls.push(`${m} ${p}`); return { ok: true, status: 200, data }; } }) },
321
373
  },
322
374
  document: { documentElement: { getAttribute: (k) => attrs[k] || null } },
@@ -371,6 +423,42 @@ test('a demo\'s genesis card: no decide form, the settled plan in one line, each
371
423
  assert.doesNotMatch(host.innerHTML, /data-genesis-plan/, 'a demo never shows the decide form');
372
424
  });
373
425
 
426
+ test('the card lists every stage vote waiting on the viewer, and votes yes on all in order (task 1005199)', async () => {
427
+ const me = [{ builder_id: '1', display_name: 'Founder' }];
428
+ const genesis = gh.shapeGenesis({
429
+ mode: { stage: 'scope', stage_number: 1, stage_count: 5, day: 1 },
430
+ plan: gh.parsePlan({}).plan,
431
+ tasks: Object.fromEntries(STAGES5.map((s, i) => [s, [{ id: String(100 + i), title: s, status: i < 4 ? 'shipped' : 'ready' }]])),
432
+ board: {
433
+ scope: { state: 'sitting', item_id: '11', waiting_on: me, needs: 'One yes decides.' },
434
+ strategize: { state: 'sitting', item_id: '12', waiting_on: me },
435
+ charter: { state: 'passed', item_id: '13', waiting_on: [] },
436
+ },
437
+ });
438
+ assert.deepEqual(genesis.stages.map((s) => s.can_close), [false, false, false, true, false], 'only legalize is finished and not yet on the board');
439
+ const b = bootCard({ founding: true, data: { genesis, viewer_id: '1' } });
440
+ let onClick = null;
441
+ b.host.addEventListener = (type, fn) => { if (type === 'click') onClick = fn; };
442
+ b.host.contains = () => true;
443
+ b.widget.mount(b.host); // re-mount so the click handler is captured
444
+ await new Promise((r) => setImmediate(r));
445
+ await new Promise((r) => setImmediate(r));
446
+ const html = b.host.innerHTML;
447
+ assert.match(html, /Waiting for your vote · 2 stages/);
448
+ assert.match(html, /data-genesis-vote-all>Vote yes on all 2</);
449
+ assert.match(html, /passed, waits its turn/, 'a later stage that passed says it waits for the earlier ones');
450
+ assert.match(html, /data-genesis-close="legalize">Put to the board</, 'a finished later stage can be put up from its row');
451
+ assert.doesNotMatch(html, /data-genesis-close="legislation"/, 'an unfinished stage cannot');
452
+ b.calls.length = 0;
453
+ const btn = { hasAttribute: (k) => k === 'data-genesis-vote-all' };
454
+ onClick({ target: { closest: () => btn } });
455
+ for (let i = 0; i < 6; i++) await new Promise((r) => setImmediate(r));
456
+ assert.deepEqual(b.calls.slice(0, 2), [
457
+ 'POST /api/bongos/government/board/items/11/votes',
458
+ 'POST /api/bongos/government/board/items/12/votes',
459
+ ], 'one yes per stage, earliest first');
460
+ });
461
+
374
462
  test('the hall carries the five life plates byte-identical to the world\'s', () => {
375
463
  const world = path.join(ROOT, 'modules', 'public-landing', 'public', 'assets', 'world');
376
464
  const hall = path.join(ROOT, 'modules', 'hall-ui', 'public', 'genesis');