@bongos/core 1.25.4 → 1.25.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.bongos-core.json +77 -47
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +6 -0
  4. package/clients/bongos-client/index.cjs +6 -0
  5. package/clients/bongos-client/index.d.ts +12 -1
  6. package/clients/bongos-client/index.mjs +6 -0
  7. package/docs/api/openapi.json +239 -3
  8. package/docs/api-reference.md +8 -5
  9. package/docs/copy-inventory.md +12 -12
  10. package/docs/copy-registry.json +13 -13
  11. package/docs/module-api-changelog.md +4 -0
  12. package/docs/page-readings.json +49 -52
  13. package/modules/hall-ui/public/shell-game.js +34 -3
  14. package/modules/hall-ui/public/welcome-boot.js +27 -11
  15. package/modules/hall-ui/records/start-screen.md +10 -0
  16. package/modules/onboarding/genesis-home.js +35 -1
  17. package/modules/onboarding/routes/onboarding.js +7 -2
  18. package/modules/provisioning/catalog-bridge.js +3 -0
  19. package/modules/provisioning/migrations/provisioning_037_update_subscribed.sql +21 -0
  20. package/modules/provisioning/module.json +1 -0
  21. package/modules/provisioning/paid-shape-gate.js +3 -1
  22. package/modules/provisioning/provisioning.js +3 -3
  23. package/modules/provisioning/routes/provisioning.js +11 -4
  24. package/modules/provisioning/routes/update-subscription.js +51 -0
  25. package/modules/provisioning/test-project.js +65 -0
  26. package/package-lock.json +2 -2
  27. package/package.json +1 -1
  28. package/release-notes.json +20 -0
  29. package/scripts/bongos/government-walk.js +2 -2
  30. package/scripts/bongos/update-channel.js +27 -4
  31. package/scripts/bongos/update-sweep.js +17 -3
  32. package/src/bongos/api-errors.js +2 -0
  33. package/src/bongos/db.js +43 -0
  34. package/src/bongos/route-rank-check.js +1 -0
  35. package/src/bongos/routes/builders.js +37 -0
  36. package/src/branding.js +7 -0
  37. package/src/module-api.js +1 -1
  38. package/tests/builder_card_requirement.mjs +75 -0
  39. package/tests/currency_label.mjs +3 -1
  40. package/tests/hall_start_screen.mjs +62 -8
  41. package/tests/provision_settings_apply.mjs +2 -1
  42. package/tests/provisioning_paid_shape_gate.mjs +16 -0
  43. package/tests/provisioning_settings_env.mjs +2 -2
  44. package/tests/provisioning_update_subscription_route.mjs +141 -0
  45. package/tests/test_project.mjs +122 -0
  46. package/tests/update_channel_db.mjs +24 -0
  47. package/tests/wizard_demo.mjs +25 -4
@@ -5,14 +5,14 @@
5
5
  "adr": "0341 D3",
6
6
  "reread": "node scripts/bongos/page-reader.js --stale",
7
7
  "how": ["Generated by node scripts/bongos/page-reader.js (BV2.TW04, task 1004315; ADR 0341 D3). Do not hand-edit.","One entry per page of docs/page-inventory.json. Lines are in page order: the default state first, and a line only another state shows sits where that state shows it, with its state named.","placement: placed = a docs/copy-registry.json row renders it (file, line, string_id); shared = shell text every page of the surface carries (top bar, sidebar, footer), with its row when the registry has one; unplaced = the registry cannot place it (a rewrite becomes an engineer task, ADR 0341 D4).","text keeps {…} where the registry row has a hole, or where a value the page read from its API was filled in. A string that is only such data is counted under omitted.data, not listed.","reading_hash is over the non-shared lines (key, section, text); files_hash is over the page's inventory files. --check compares files_hash only and never opens a browser.","Rendered through the hall-preview harness (hall) and the ui-design kit stub (landing, status), at 1280x800 with the clock pinned, so a reading is the fixture world's page."],
8
- "counts": {"pages":45,"lines":4411,"placed":1638,"shared":369,"unplaced":2404,"omitted_data":2980},
8
+ "counts": {"pages":45,"lines":4408,"placed":1633,"shared":369,"unplaced":2406,"omitted_data":2980},
9
9
  "pages": [
10
10
  {
11
11
  "id": "builders:ai-studio",
12
12
  "surface": "builders",
13
13
  "title": "Studio",
14
14
  "states_read": ["pick","describe","make","waiting","result","result-own","failed","balance","balance-all","out"],
15
- "files_hash": "5b785de6639c8ec5",
15
+ "files_hash": "a9b4990d9b84e7f9",
16
16
  "reading_hash": "d9e0e85bcee4aff4",
17
17
  "counts": {"lines":106,"placed":41,"shared":7,"unplaced":58},
18
18
  "omitted": {"data":17},
@@ -131,7 +131,7 @@
131
131
  "surface": "builders",
132
132
  "title": "Repo Atlas",
133
133
  "states_read": ["map","truth","detail","index","out"],
134
- "files_hash": "76f3f9d62c147df5",
134
+ "files_hash": "84e8a3802285d54c",
135
135
  "reading_hash": "6934dead335b2507",
136
136
  "counts": {"lines":25,"placed":14,"shared":7,"unplaced":4},
137
137
  "omitted": {"data":5},
@@ -169,7 +169,7 @@
169
169
  "surface": "builders",
170
170
  "title": "Blockers",
171
171
  "states_read": ["default"],
172
- "files_hash": "8492188331702a2f",
172
+ "files_hash": "22d64ad0bdbfb20a",
173
173
  "reading_hash": "60d73d854ced958a",
174
174
  "counts": {"lines":36,"placed":17,"shared":7,"unplaced":12},
175
175
  "omitted": {"data":51},
@@ -218,7 +218,7 @@
218
218
  "surface": "builders",
219
219
  "title": "Board Room",
220
220
  "states_read": ["room","out","sitting-meta","objection-form"],
221
- "files_hash": "4218d029418aa874",
221
+ "files_hash": "71ccdec4d840f239",
222
222
  "reading_hash": "0260b797b8dc1526",
223
223
  "counts": {"lines":52,"placed":21,"shared":7,"unplaced":24},
224
224
  "omitted": {"data":17},
@@ -283,7 +283,7 @@
283
283
  "surface": "builders",
284
284
  "title": "The city",
285
285
  "states_read": ["city","fullscreen","focus","pins","living","card","card-ask"],
286
- "files_hash": "3a16e78aaab2e893",
286
+ "files_hash": "d3b4d1e3da4cbf34",
287
287
  "reading_hash": "277096d903879a57",
288
288
  "counts": {"lines":26,"placed":9,"shared":6,"unplaced":11},
289
289
  "omitted": {"data":7},
@@ -322,7 +322,7 @@
322
322
  "surface": "builders",
323
323
  "title": "Collab",
324
324
  "states_read": ["board","out"],
325
- "files_hash": "9e9d0d3b86089631",
325
+ "files_hash": "08ca51a52701caa7",
326
326
  "reading_hash": "e6452bf92231656f",
327
327
  "counts": {"lines":82,"placed":36,"shared":6,"unplaced":40},
328
328
  "omitted": {"data":23},
@@ -417,7 +417,7 @@
417
417
  "surface": "builders",
418
418
  "title": "Commands",
419
419
  "states_read": ["in","out"],
420
- "files_hash": "14d04212aba71360",
420
+ "files_hash": "4184181832abeb8e",
421
421
  "reading_hash": "ecc452689a8ee9d7",
422
422
  "counts": {"lines":71,"placed":49,"shared":7,"unplaced":15},
423
423
  "omitted": {"data":177},
@@ -501,7 +501,7 @@
501
501
  "surface": "builders",
502
502
  "title": "Copy desk",
503
503
  "states_read": ["queue","out","search","composer","closer","proposer"],
504
- "files_hash": "498777214970f599",
504
+ "files_hash": "70d8131ed9bed50a",
505
505
  "reading_hash": "3819ef6fa8f6fb7c",
506
506
  "counts": {"lines":72,"placed":36,"shared":7,"unplaced":29},
507
507
  "omitted": {"data":94},
@@ -586,7 +586,7 @@
586
586
  "surface": "builders",
587
587
  "title": "Deploy",
588
588
  "states_read": ["work","out"],
589
- "files_hash": "d93352de799f272f",
589
+ "files_hash": "c3726270034f0886",
590
590
  "reading_hash": "3770e8b2fa56bee1",
591
591
  "counts": {"lines":14,"placed":5,"shared":7,"unplaced":2},
592
592
  "omitted": {"data":5},
@@ -613,7 +613,7 @@
613
613
  "surface": "builders",
614
614
  "title": "The sky",
615
615
  "states_read": ["tour","sky","wells","ledger","full","full-back","out"],
616
- "files_hash": "50735ca110164fe8",
616
+ "files_hash": "117557e051f209a0",
617
617
  "reading_hash": "4a879397a84631e3",
618
618
  "counts": {"lines":50,"placed":25,"shared":6,"unplaced":19},
619
619
  "omitted": {"data":10},
@@ -676,7 +676,7 @@
676
676
  "surface": "builders",
677
677
  "title": "Goals",
678
678
  "states_read": ["list","filters","cards","detail","dependencies","out","uat","uat-panel","author"],
679
- "files_hash": "23bb6e3ad6306135",
679
+ "files_hash": "4a6b3e46f1553c25",
680
680
  "reading_hash": "13a5d07b3804b627",
681
681
  "counts": {"lines":187,"placed":61,"shared":7,"unplaced":119},
682
682
  "omitted": {"data":315},
@@ -876,7 +876,7 @@
876
876
  "surface": "builders",
877
877
  "title": "Government",
878
878
  "states_read": ["constitution","out","permissions","board","constitution-tab"],
879
- "files_hash": "d168f2cd8aa66e32",
879
+ "files_hash": "ddbd09c6c1629fd3",
880
880
  "reading_hash": "37c6878d60447fd6",
881
881
  "counts": {"lines":65,"placed":27,"shared":7,"unplaced":31},
882
882
  "omitted": {"data":276},
@@ -954,9 +954,9 @@
954
954
  "surface": "builders",
955
955
  "title": "Builders' Hall",
956
956
  "states_read": ["home","out","founding","genesis","decide","demo","votes"],
957
- "files_hash": "730694ff18f4468d",
958
- "reading_hash": "177d0c13a53c0491",
959
- "counts": {"lines":32,"placed":12,"shared":8,"unplaced":12},
957
+ "files_hash": "fc89efd93f0419a9",
958
+ "reading_hash": "620b6c4d18b7e501",
959
+ "counts": {"lines":29,"placed":7,"shared":8,"unplaced":14},
960
960
  "omitted": {"data":4},
961
961
  "notes": ["founding: an action failed (page.focus: Timeout 6000ms exceeded.); read as it stood"],
962
962
  "lines": [
@@ -967,7 +967,7 @@
967
967
  {"key":"L0005","section":"top bar","text":"Jump to…","placement":"shared","file":"modules/hall-ui/public/shell.js","line":536,"string_id":"eba610d25523"},
968
968
  {"key":"L0006","section":"top bar","text":"Ctrl K","placement":"shared"},
969
969
  {"key":"L0007","section":"top bar","text":"Artist","placement":"shared"},
970
- {"key":"L0008","section":"Good morning, {…}","text":"Good morning, {…}","placement":"unplaced","occurrences":2},
970
+ {"key":"L0008","section":"Good morning, {…}","text":"Good morning, {…}","placement":"unplaced"},
971
971
  {"key":"L0009","section":"Good morning, {…}","text":"B","placement":"unplaced","occurrences":9},
972
972
  {"key":"L0010","section":"Good morning, {…}","text":"Aug","placement":"unplaced"},
973
973
  {"key":"L0011","section":"Good morning, {…}","text":"Oct","placement":"unplaced"},
@@ -977,21 +977,18 @@
977
977
  {"key":"L0015","section":"Needs your attention13","text":"Ideas awaiting triage","placement":"placed","file":"modules/hall-ui/public/builders.js","line":320,"string_id":"e181637c5a5e"},
978
978
  {"key":"L0016","section":"Needs your attention13","text":"Requires project {…} fix","placement":"unplaced"},
979
979
  {"key":"L0017","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":514,"string_id":"1883ad7f07fc"},
980
- {"key":"L0018","section":"Needs your attention13","text":"core 1.25.2","placement":"unplaced"},
981
- {"key":"L0019","section":"Needs your attention13","text":"Skip","placement":"unplaced"},
982
- {"key":"L0020","section":"Needs your attention13","text":"Built on","placement":"placed","file":"modules/hall-ui/public/welcome-boot.js","line":183,"string_id":"fef59346ffee"},
983
- {"key":"L0021","section":"Needs your attention13","text":"cloudbongos.com","placement":"unplaced"},
984
- {"key":"L0022","section":"Good morning, {…}","text":"Where do you want to start the day?","placement":"placed","file":"modules/hall-ui/public/welcome-boot.js","line":188,"string_id":"0ec8d133f878","state":"founding"},
985
- {"key":"L0023","section":"Good morning, {…}","text":"Start with your role","placement":"placed","file":"modules/hall-ui/public/welcome-boot.js","line":190,"string_id":"58cef7963b6c","state":"founding"},
986
- {"key":"L0024","section":"Good morning, {…}","text":"Pick today’s craft and go straight to its room.","placement":"placed","file":"modules/hall-ui/public/welcome-boot.js","line":190,"string_id":"100e4665514d","state":"founding"},
987
- {"key":"L0025","section":"Good morning, {…}","text":"Your home page","placement":"placed","file":"modules/hall-ui/public/welcome-boot.js","line":191,"string_id":"2e6866532113","state":"founding"},
988
- {"key":"L0026","section":"Good morning, {…}","text":"Your work, what needs you, and what shipped.","placement":"placed","file":"modules/hall-ui/public/welcome-boot.js","line":191,"string_id":"e8d92c773db0","state":"founding"},
989
- {"key":"L0027","section":"Update the builders hall?","text":"Update the builders hall?","placement":"unplaced"},
990
- {"key":"L0028","section":"Update the builders hall?","text":"A newer version of Bongos is ready, with the latest changes and bug fixes.","placement":"placed","file":"modules/hall-ui/public/update-pill.js","line":604,"string_id":"105876fd9038"},
991
- {"key":"L0029","section":"Update the builders hall?","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":3},
992
- {"key":"L0030","section":"Update the builders hall?","text":"Closed a security hole where a builder's branch could choose the program the server runs while auto-resolving merge…","placement":"unplaced"},
993
- {"key":"L0031","section":"Update the builders hall?","text":"Later","placement":"unplaced"},
994
- {"key":"L0032","section":"Update the builders hall?","text":"Update now","placement":"placed","file":"modules/hall-ui/public/update-pill.js","line":288,"string_id":"5fe7d2d395be"}
980
+ {"key":"L0018","section":"{…}","text":"How do you want to see it?","placement":"placed","file":"modules/hall-ui/public/shell-game.js","line":165,"string_id":"1e8da0baf421"},
981
+ {"key":"L0019","section":"{…}","text":"Game","placement":"unplaced"},
982
+ {"key":"L0020","section":"{…}","text":"The city, {…} screen","placement":"unplaced"},
983
+ {"key":"L0021","section":"{…}","text":"Classic","placement":"unplaced"},
984
+ {"key":"L0022","section":"{…}","text":"The hall with its sidebar","placement":"unplaced"},
985
+ {"key":"L0023","section":"{…}","text":"Escape opens the options menu, where every setting lives.","placement":"unplaced"},
986
+ {"key":"L0024","section":"Update the builders hall?","text":"Update the builders hall?","placement":"unplaced"},
987
+ {"key":"L0025","section":"Update the builders hall?","text":"A newer version of Bongos is ready, with the latest changes and bug fixes.","placement":"placed","file":"modules/hall-ui/public/update-pill.js","line":604,"string_id":"105876fd9038"},
988
+ {"key":"L0026","section":"Update the builders hall?","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":3},
989
+ {"key":"L0027","section":"Update the builders hall?","text":"Closed a security hole where a builder's branch could choose the program the server runs while auto-resolving merge…","placement":"unplaced"},
990
+ {"key":"L0028","section":"Update the builders hall?","text":"Later","placement":"unplaced"},
991
+ {"key":"L0029","section":"Update the builders hall?","text":"Update now","placement":"placed","file":"modules/hall-ui/public/update-pill.js","line":288,"string_id":"5fe7d2d395be"}
995
992
  ]
996
993
  },
997
994
  {
@@ -999,7 +996,7 @@
999
996
  "surface": "builders",
1000
997
  "title": "Idea",
1001
998
  "states_read": ["record","missing"],
1002
- "files_hash": "02820c742a0bceb9",
999
+ "files_hash": "27923dd71c6cc61e",
1003
1000
  "reading_hash": "f7d644748c87e426",
1004
1001
  "counts": {"lines":52,"placed":25,"shared":8,"unplaced":19},
1005
1002
  "omitted": {"data":25},
@@ -1064,7 +1061,7 @@
1064
1061
  "surface": "builders",
1065
1062
  "title": "Ideas",
1066
1063
  "states_read": ["compose","full","develop"],
1067
- "files_hash": "59688a8b224ba2ec",
1064
+ "files_hash": "14fb04299f9c5294",
1068
1065
  "reading_hash": "a62c05dcef3bf87f",
1069
1066
  "counts": {"lines":68,"placed":34,"shared":7,"unplaced":27},
1070
1067
  "omitted": {"data":118},
@@ -1145,7 +1142,7 @@
1145
1142
  "surface": "builders",
1146
1143
  "title": "Market Watch",
1147
1144
  "states_read": ["desk","settings","out"],
1148
- "files_hash": "377ad66b86dfdd0b",
1145
+ "files_hash": "86254f61eee1660d",
1149
1146
  "reading_hash": "30512dda207b5ea6",
1150
1147
  "counts": {"lines":23,"placed":11,"shared":7,"unplaced":5},
1151
1148
  "omitted": {"data":5},
@@ -1181,7 +1178,7 @@
1181
1178
  "surface": "builders",
1182
1179
  "title": "Market Watch",
1183
1180
  "states_read": ["dossier","out","propose","correct","filtered"],
1184
- "files_hash": "bb602ff95badc43c",
1181
+ "files_hash": "b0744b7328c2f1f6",
1185
1182
  "reading_hash": "129bff28c4e4ed6d",
1186
1183
  "counts": {"lines":24,"placed":12,"shared":7,"unplaced":5},
1187
1184
  "omitted": {"data":5},
@@ -1218,7 +1215,7 @@
1218
1215
  "surface": "builders",
1219
1216
  "title": "Market Watch rules",
1220
1217
  "states_read": ["in","scoring","names","propose","out"],
1221
- "files_hash": "6c2edc411eed69bc",
1218
+ "files_hash": "159bc203c644dbed",
1222
1219
  "reading_hash": "ca0c62e8dce51c8b",
1223
1220
  "counts": {"lines":36,"placed":12,"shared":7,"unplaced":17},
1224
1221
  "omitted": {"data":20},
@@ -1267,7 +1264,7 @@
1267
1264
  "surface": "builders",
1268
1265
  "title": "Modules",
1269
1266
  "states_read": ["browse","search-empty","lens","details","out"],
1270
- "files_hash": "61872edf35808d71",
1267
+ "files_hash": "a19bba03c78ce50a",
1271
1268
  "reading_hash": "6a12e9306bba5622",
1272
1269
  "counts": {"lines":59,"placed":29,"shared":7,"unplaced":23},
1273
1270
  "omitted": {"data":107},
@@ -1339,7 +1336,7 @@
1339
1336
  "surface": "builders",
1340
1337
  "title": "Money",
1341
1338
  "states_read": ["money","hosting","out"],
1342
- "files_hash": "cbce1cf96f277a69",
1339
+ "files_hash": "f521ccd6c8c5928c",
1343
1340
  "reading_hash": "e355d5466b123e07",
1344
1341
  "counts": {"lines":83,"placed":28,"shared":7,"unplaced":48},
1345
1342
  "omitted": {"data":27},
@@ -1435,7 +1432,7 @@
1435
1432
  "surface": "builders",
1436
1433
  "title": "My agents",
1437
1434
  "states_read": ["desk","factory","saved","what-it-knows","speciality-walk","speciality-forms","nobody"],
1438
- "files_hash": "d3dbce13a5390286",
1435
+ "files_hash": "aaf60004a2341d14",
1439
1436
  "reading_hash": "5347e0496b38727d",
1440
1437
  "counts": {"lines":100,"placed":33,"shared":7,"unplaced":60},
1441
1438
  "omitted": {"data":38},
@@ -1548,7 +1545,7 @@
1548
1545
  "surface": "builders",
1549
1546
  "title": "Primer",
1550
1547
  "states_read": ["in","toc-open","jump","out"],
1551
- "files_hash": "df4ef875bc5238b7",
1548
+ "files_hash": "4ba10f0fe35fcf02",
1552
1549
  "reading_hash": "85790bd421e5c387",
1553
1550
  "counts": {"lines":487,"placed":57,"shared":7,"unplaced":423},
1554
1551
  "omitted": {"data":18},
@@ -2048,7 +2045,7 @@
2048
2045
  "surface": "builders",
2049
2046
  "title": "Profile",
2050
2047
  "states_read": ["own","works","activity","yours","activity-artist","activity-artist-ideas","other","artist","artist-ideas","out"],
2051
- "files_hash": "412b85b0c70da301",
2048
+ "files_hash": "1f829822c59de8d6",
2052
2049
  "reading_hash": "3ff8d703d17e8c2a",
2053
2050
  "counts": {"lines":232,"placed":84,"shared":7,"unplaced":141},
2054
2051
  "omitted": {"data":263},
@@ -2293,7 +2290,7 @@
2293
2290
  "surface": "builders",
2294
2291
  "title": "Project settings",
2295
2292
  "states_read": ["default"],
2296
- "files_hash": "4f917ac224479a34",
2293
+ "files_hash": "22d9382cc6f62dcd",
2297
2294
  "reading_hash": "283df27d0d5bfc1b",
2298
2295
  "counts": {"lines":43,"placed":25,"shared":7,"unplaced":11},
2299
2296
  "omitted": {"data":33},
@@ -2349,7 +2346,7 @@
2349
2346
  "surface": "builders",
2350
2347
  "title": "Builders",
2351
2348
  "states_read": ["roster","artists","search","out"],
2352
- "files_hash": "337395b2465f66fc",
2349
+ "files_hash": "20931556211ad519",
2353
2350
  "reading_hash": "665ac2e36339f5d3",
2354
2351
  "counts": {"lines":56,"placed":17,"shared":7,"unplaced":32},
2355
2352
  "omitted": {"data":137},
@@ -2418,7 +2415,7 @@
2418
2415
  "surface": "builders",
2419
2416
  "title": "Options",
2420
2417
  "states_read": ["account","access","preferences","install-reports","sound","update"],
2421
- "files_hash": "77001d9bd6423a92",
2418
+ "files_hash": "16028f41e693ccde",
2422
2419
  "reading_hash": "898a13fda24c3c90",
2423
2420
  "counts": {"lines":162,"placed":104,"shared":7,"unplaced":51},
2424
2421
  "omitted": {"data":23},
@@ -2593,7 +2590,7 @@
2593
2590
  "surface": "builders",
2594
2591
  "title": "The street",
2595
2592
  "states_read": ["street","fullscreen","card","focus","here","inside-rules","inside-disputes","inside-people","inside-planning","inside-automation","inside-review","inside-release","inside-treasury","floor","amend","district","desk","desk-facts","desk-quiet","desk-people-builders","desk-people-tier","desk-disputes","desk-automation-goals","desk-release-core","desk-rules","desk-rules-category","desk-planning-priorities","desk-planning-ideas","desk-planning-blockers","pins","pins-pager","living","deck","deck-treasury","sitting","object","voted","admission","settled","blocker","blocker-more","report","report-more","core","core-more","recheck","recheck-more","idea","idea-more","queue","queue-more","automation","automation-more"],
2596
- "files_hash": "8cd0e137ff9ec280",
2593
+ "files_hash": "e2df36c75a82662a",
2597
2594
  "reading_hash": "e788e6bf4be8d68c",
2598
2595
  "counts": {"lines":325,"placed":54,"shared":6,"unplaced":265},
2599
2596
  "omitted": {"data":240},
@@ -2931,7 +2928,7 @@
2931
2928
  "surface": "builders",
2932
2929
  "title": "Studio",
2933
2930
  "states_read": ["home","out","flow","flow-pick","status","words-offer","words-edit","reviews","full","full-back","approvals","approvals-before","approvals-phone","approvals-note","approved","sent-back","approvals-page"],
2934
- "files_hash": "3378703eddb55c9f",
2931
+ "files_hash": "fe0a3ca248e50554",
2935
2932
  "reading_hash": "c4e3b4188c1db755",
2936
2933
  "counts": {"lines":51,"placed":29,"shared":7,"unplaced":15},
2937
2934
  "omitted": {"data":23},
@@ -2995,7 +2992,7 @@
2995
2992
  "surface": "builders",
2996
2993
  "title": "Task",
2997
2994
  "states_read": ["record","from-goal","missing"],
2998
- "files_hash": "ddaf83cea69ef7e7",
2995
+ "files_hash": "44063a120d39cba9",
2999
2996
  "reading_hash": "8dd803b6934e6388",
3000
2997
  "counts": {"lines":151,"placed":46,"shared":9,"unplaced":96},
3001
2998
  "omitted": {"data":111},
@@ -3159,7 +3156,7 @@
3159
3156
  "surface": "builders",
3160
3157
  "title": "Your thinking",
3161
3158
  "states_read": ["tour","sky","list","simple","full-idea","full","full-list","full-back","out"],
3162
- "files_hash": "63660aafe0fd02da",
3159
+ "files_hash": "511ee89d9ee0ab96",
3163
3160
  "reading_hash": "b73f848b46b0f9b8",
3164
3161
  "counts": {"lines":50,"placed":25,"shared":6,"unplaced":19},
3165
3162
  "omitted": {"data":10},
@@ -3222,7 +3219,7 @@
3222
3219
  "surface": "builders",
3223
3220
  "title": "Tweak a page",
3224
3221
  "states_read": ["doc","typed","chrome","submitted","picking","out"],
3225
- "files_hash": "e1bf8da355d6cfb7",
3222
+ "files_hash": "9bc999d8335172c8",
3226
3223
  "reading_hash": "f521f8d9a7147557",
3227
3224
  "counts": {"lines":26,"placed":18,"shared":2,"unplaced":6},
3228
3225
  "omitted": {"data":19},
@@ -3261,7 +3258,7 @@
3261
3258
  "surface": "builders",
3262
3259
  "title": "Watch",
3263
3260
  "states_read": ["board","out","operations","security","sessions","autobongos"],
3264
- "files_hash": "293ae9537963df09",
3261
+ "files_hash": "3a3894aedea11977",
3265
3262
  "reading_hash": "1961a0c06b9d8477",
3266
3263
  "counts": {"lines":72,"placed":33,"shared":7,"unplaced":32},
3267
3264
  "omitted": {"data":52},
@@ -3346,7 +3343,7 @@
3346
3343
  "surface": "builders",
3347
3344
  "title": "Tasks",
3348
3345
  "states_read": ["ready","in-progress","backlog","completed","filters-open","out","founding"],
3349
- "files_hash": "8fee1f62e3f188bc",
3346
+ "files_hash": "34f5b6e547c2319f",
3350
3347
  "reading_hash": "e9b412d1ec59ceee",
3351
3348
  "counts": {"lines":122,"placed":49,"shared":7,"unplaced":66},
3352
3349
  "omitted": {"data":323},
@@ -303,9 +303,19 @@
303
303
  if (open) box.querySelector('a').focus();
304
304
  }
305
305
  // Game: on to the start screen. Classic: the screen goes and the hall is as it always was.
306
+ // Signed out (task 1005219): the pick is kept for the trip, the visitor signs in, and the
307
+ // return opens the picked menu without asking again (start() reads PENDING_KEY).
306
308
  function choose(view) {
307
309
  if (phase !== 'choose') return;
310
+ if (opts && typeof opts.signIn === 'function') {
311
+ write('localStorage', PENDING_KEY, view);
312
+ opts.signIn();
313
+ return;
314
+ }
308
315
  if (view === 'classic') { set(false); close(); return; }
316
+ toMenu();
317
+ }
318
+ function toMenu() {
309
319
  set(true);
310
320
  phase = 'menu';
311
321
  root.querySelector('#gs-ask').hidden = true;
@@ -344,10 +354,30 @@
344
354
  f[(at + step + f.length) % f.length].focus();
345
355
  }
346
356
 
347
- // Put the start screen up. `o`: { name, art, done } — the project's name, the still to stand
348
- // behind both screens, and what to do once the world is showing. True when it is up.
357
+ // The hall's front opens on the chooser once each time the site is opened (task 1005219;
358
+ // owner, 2026-11-12: "every visit"): one sessionStorage mark per tab, so moving about the
359
+ // hall (game mode sends a classic page Home) never asks again. Storage that throws: never asked twice.
360
+ var SEEN_KEY = 'hall-chooser-seen';
361
+ var PENDING_KEY = 'hall-view-pending';
362
+ function seen() {
363
+ try { return window.sessionStorage.getItem(SEEN_KEY) === '1'; } catch (_) { return true; }
364
+ }
365
+ // The view a visitor picked before signing in, read once.
366
+ function takePending() {
367
+ var v = read('localStorage', PENDING_KEY);
368
+ try { window.localStorage.removeItem(PENDING_KEY); } catch (_) { /* nothing kept */ }
369
+ return v === 'game' || v === 'classic' ? v : null;
370
+ }
371
+
372
+ // Put the start screen up. `o`: { name, art, done, signIn } — the project's name, the still to
373
+ // stand behind both screens, what to do once the world is showing, and (signed out) how to sign
374
+ // in once a view is picked. True when it is up; false for a classic pick made before sign-in,
375
+ // which goes straight to the hall.
349
376
  function start(o) {
350
377
  if (root || !document.body) return !!root;
378
+ write('sessionStorage', SEEN_KEY, '1');
379
+ var pending = o && o.signIn ? null : takePending();
380
+ if (pending === 'classic') { set(false); return false; }
351
381
  opts = o || {};
352
382
  phase = 'choose';
353
383
  root = document.createElement('div');
@@ -361,6 +391,7 @@
361
391
  document.addEventListener('keydown', onKey, true);
362
392
  document.body.appendChild(root);
363
393
  document.documentElement.classList.add('gs-open');
394
+ if (pending === 'game') { toMenu(); return true; }
364
395
  var first = root.querySelector('.gs__item--lit') || root.querySelector('.gs__item');
365
396
  if (first && first.focus) first.focus();
366
397
  return true;
@@ -522,6 +553,6 @@
522
553
  } catch (_) { /* a href the browser cannot read: leave it alone */ }
523
554
  }, true);
524
555
  }
525
- window.OTBShellGame = { on: on, set: set, hint: hint, start: start, tipAt: tipAt, historySteps: historySteps, whenLabel: whenLabel, segsDone: segsDone, MENU: MENU, TIPS: TIPS,
556
+ window.OTBShellGame = { on: on, set: set, hint: hint, start: start, seen: seen,tipAt: tipAt, historySteps: historySteps, whenLabel: whenLabel, segsDone: segsDone, MENU: MENU, TIPS: TIPS,
526
557
  embedded: function () { return embedded; }, menuPath: menuPath, PLACES: PLACES, pageOf: pageOf, isPlace: isPlace };
527
558
  })();
@@ -14,8 +14,10 @@
14
14
  // flag in localStorage that the return visit consumes) opens on the game's start screen
15
15
  // instead (task 1005143, shell-game.js); without that file it is greeted here as before. Storage that
16
16
  // throws cannot remember, so such a browser is never shown it (it would replay on every visit).
17
- // - A visitor who is not signed in sees the Welcome on Home, every visit, with a "Sign in with
18
- // GitHub" button in place of the role choices. Only on Home, never over a deep link (#/task/N, #/idea/N).
17
+ // - Home opens on the game's Game / Classic chooser once each time the site is opened, signed in
18
+ // or not (task 1005219); signed out, a pick goes on to sign in and the return opens that menu.
19
+ // A visitor who is not signed in sees this Welcome only in a hall without shell-game.js, with a
20
+ // "Sign in with GitHub" button in place of the role choices. Only on Home, never over a deep link (#/task/N, #/idea/N).
19
21
  // - shell.js asks onAccess() BEFORE the craft picker; a load where the welcome showed marks the
20
22
  // picker asked (OTBCraftMode.markAsked), so the two never stack.
21
23
  // - Picking a role also sets the hall's craft mode. Governing is a seat (craft-mode.js), so the
@@ -368,29 +370,43 @@
368
370
  function onAccess(access, builder) {
369
371
  if (shown) return true;
370
372
  if (deepLink()) return false;
373
+ var game = window.OTBShellGame;
374
+ var canChoose = !!(game && typeof game.start === 'function');
371
375
  if (access && access.authed === false) {
372
376
  if (!onHome()) return false;
373
377
  shown = true;
378
+ // The hall's front opens on Game or Classic (task 1005219): the pick is kept, the visitor
379
+ // signs in, and comes back to that menu (shell-game.js). Without that file, the welcome.
380
+ if (canChoose && game.start({
381
+ name: projectName(window.__BRANDING__), art: artFor(timeOfDay(new Date())),
382
+ signIn: function () { markSigningIn(); go(SIGNIN_HREF); },
383
+ })) return true;
374
384
  show(null);
375
385
  return true;
376
386
  }
377
387
  if (!access || access.authed !== true || !builder || builder.id == null) return false;
378
388
  var key = DAY_KEY + builder.id, now = new Date();
389
+ // Every time the site is opened, Home asks Game or Classic first (task 1005219; owner,
390
+ // 2026-11-12: "every visit"), and that stands in for the day's welcome.
391
+ var ask = canChoose && onHome() && typeof game.seen === 'function' && !game.seen();
379
392
  try {
380
393
  // Every fresh sign-in is greeted; otherwise only the day's first visit.
381
394
  var fresh = takeSignedIn();
382
- if (!fresh && !firstVisitToday(window.localStorage.getItem(key), now)) return false;
395
+ if (!fresh && !ask && !firstVisitToday(window.localStorage.getItem(key), now)) return false;
383
396
  window.localStorage.setItem(key, localDay(now));
384
- } catch (_) { return false; }
397
+ } catch (_) { if (!ask) return false; }
385
398
  shown = true;
386
399
  var c = craft();
387
- // A fresh sign-in opens on the game's start screen instead (task 1005143, shell-game.js): the
388
- // same still behind it, and the craft picker asked once the world is showing.
389
- var game = window.OTBShellGame;
390
- if (fresh && game && typeof game.start === 'function' && game.start({
391
- name: projectName(window.__BRANDING__), art: artFor(timeOfDay(now)),
392
- done: function () { if (c && typeof c.onAccess === 'function') c.onAccess(access, builder); },
393
- })) return true;
400
+ // A fresh sign-in, or a new visit to Home, opens on the game's start screen instead (tasks
401
+ // 1005143, 1005219; shell-game.js): the same still behind it, and the craft picker asked once
402
+ // the world is showing. Classic picked before signing in: no screen, straight to the hall.
403
+ if ((fresh || ask) && canChoose) {
404
+ if (game.start({
405
+ name: projectName(window.__BRANDING__), art: artFor(timeOfDay(now)),
406
+ done: function () { if (c && typeof c.onAccess === 'function') c.onAccess(access, builder); },
407
+ })) return true;
408
+ if (fresh) return false;
409
+ }
394
410
  if (c && typeof c.markAsked === 'function') c.markAsked();
395
411
  show(builder);
396
412
  return true;
@@ -29,3 +29,13 @@ Tests: `tests/hall_start_screen.mjs` (the hand-off and pure parts in a vm; both
29
29
  Every sign-in now opens on a chooser before the start screen: **Game** or **Classic**, each with one line saying what it is. The view chosen last time (the browser's `hall-game` copy) is marked "Last time", lit with the accent on its edge, and focused, so Enter keeps it. Game goes on to the start screen as before; Classic closes the screen and the hall is as it always was. Either choice is saved to the account (`PATCH /api/bongos/me/hall-view`). The Extras list lost The docs: it is not a game place. Tests: `tests/hall_start_screen.mjs`.
30
30
 
31
31
  **In the start menu's shape (task 1005206, owner 2026-11-11, from the V3 Helm start-screen mock-up).** The chooser is drawn like the menu it leads to: "How do you want to see it?" is a small label in the name voice, and the view Enter takes (last time's, else Game) is the accent bar with its "Enter" key, exactly as Start is (`.gs__item--lit` shares Start's rules); the other view is a quiet row on a hairline. Each row keeps its one line; on the lit bar the line and the "Last time" tag take `--accent-on`. Focus starts on the lit row.
32
+
33
+ ## Land on the chooser (task 1005219, owner 2026-11-12)
34
+
35
+ The hall's front (Home, `/builders`) now opens on the Game / Classic chooser **every time the site is opened**, signed in or not: one `sessionStorage` mark per tab (`hall-chooser-seen`), so moving about the hall — game mode sends a classic page Home — never asks twice. This supersedes "a new visit keeps the welcome" and "a signed-out visitor keeps the welcome" above; the painted welcome remains only for a hall without `shell-game.js` and for the day's first visit to a page other than Home.
36
+
37
+ - **Signed out:** the pick is kept in `localStorage` (`hall-view-pending`) and the visitor goes to GitHub sign-in (`/api/bongos/auth/web/start`, which returns by the referer). On the return `start()` spends the pick: Game opens straight on the start screen's menu, Classic shows no screen and the hall is as it always was. Either is saved to the account then, as at the chooser.
38
+ - **Signed in:** the pick goes straight to that menu, as at sign-in.
39
+ - The marketing page at the apex root is unchanged (the owner's pick: the hall's front, not the apex).
40
+
41
+ Tests: `tests/hall_start_screen.mjs` (the vm hand-off, and the signed-out round trip for both picks in a real browser).
@@ -276,6 +276,40 @@ async function startFounding(builderId, deps = {}) {
276
276
  return { ok: true, mode };
277
277
  }
278
278
 
279
+ // isTestProject(deps) — is this hall a TEST project? The platform marks one (task 1005036,
280
+ // modules/provisioning/test-project.js) and the mark arrives as branding project.test. Genesis is paused
281
+ // for every other project (owner, 2026-11-01), so only a test project may switch it on by hand.
282
+ function isTestProject(deps = {}) {
283
+ try {
284
+ const b = deps.branding !== undefined ? deps.branding : require('../../src/module-api').branding();
285
+ return !!(b && b.project && b.project.test === 'on');
286
+ } catch (_) { return false; }
287
+ }
288
+
289
+ // ownerStart(builderId, deps) — the owner's own start (POST /founding/start): a test project only.
290
+ async function ownerStart(builderId, deps = {}) {
291
+ if (!isTestProject(deps)) return { ok: false, reason: 'not_a_test_project' };
292
+ return startFounding(builderId, deps);
293
+ }
294
+
295
+ // setEnabled(enabled, builderId, deps) — the Test features panel's genesis switch (task 1005036): a test
296
+ // project only. On starts genesis if it never started (startFounding turns the switch on); if it already
297
+ // began, it just turns the switch back on. Off pauses it — the founding record is kept, the hall opens full.
298
+ async function setEnabled(enabled, builderId, deps = {}) {
299
+ if (!isTestProject(deps)) return { ok: false, reason: 'not_a_test_project' };
300
+ const s = seams(deps);
301
+ if (enabled === true) {
302
+ const started = await s.get(fm.KEYS.STARTED_AT);
303
+ if (!(started && started.value)) return startFounding(builderId, deps);
304
+ const ended = await s.get(fm.KEYS.ENDED_AT);
305
+ if (ended && ended.value) return { ok: false, reason: 'already_alive' };
306
+ await s.set(fm.KEYS.ENABLED, 'true', builderId);
307
+ } else {
308
+ await s.set(fm.KEYS.ENABLED, 'false', builderId);
309
+ }
310
+ return { ok: true, enabled: enabled === true, mode: await fm.foundingModeFor(modeDeps(s)) };
311
+ }
312
+
279
313
  // savePlan — the decide stage's door: record the plan, file the five stages as tasks,
280
314
  // and move to scope. Only while deciding.
281
315
  async function savePlan(body, builderId, deps = {}) {
@@ -508,7 +542,7 @@ async function onTaskShipped(deps = {}) {
508
542
 
509
543
  module.exports = {
510
544
  PLAN_KEY, STAGE_INFO, PROPER, DEMO_PEOPLE, DEMO_HOURS, intIn, parsePlan, shapeGenesis, demoMinutes, demoPlan,
511
- readGenesis, startFounding, savePlan, applyGovernmentChoice, closeStage, comeAlive, skipGenesis, onStagePassed,
545
+ readGenesis, startFounding, ownerStart, setEnabled, savePlan, applyGovernmentChoice, closeStage, comeAlive, skipGenesis, onStagePassed,
512
546
  putFinishedStages, onTaskShipped,
513
547
  // the founding-mode deps this file reads the mode with, for founding-birth.js (task 1004506)
514
548
  modeDepsFor: (deps) => modeDeps(seams(deps)),
@@ -124,7 +124,7 @@ const GENESIS_REFUSALS = {
124
124
  not_at_legislation: 409, legislation_not_passed: 409, stage_tasks_unfinished: 409,
125
125
  no_stage_tasks: 409, unknown_stage: 400, no_board: 503,
126
126
  bad_days: 400, bad_passes: 400, bad_advisors: 400, bad_government: 400, bad_expected_builders: 400,
127
- bad_demo: 400, bad_minutes: 400,
127
+ bad_demo: 400, bad_minutes: 400, not_a_test_project: 403,
128
128
  };
129
129
  let genesisListening = false;
130
130
 
@@ -158,7 +158,12 @@ function mountGenesisRoutes(router) {
158
158
  await foundingBirth.ensureBirth();
159
159
  return { genesis: await genesis.readGenesis(), viewer_id: String(req.builder.id) };
160
160
  }));
161
- router.post('/founding/start', api.requireBuilder, owns, handle((req) => genesis.startFounding(req.builder.id)));
161
+ router.post('/founding/start', api.requireBuilder, owns, handle((req) => genesis.ownerStart(req.builder.id)));
162
+ // The Test features panel's genesis switch (task 1005036): a test project only, the owner's call.
163
+ router.put('/founding/enabled', api.requireBuilder, owns, (req, res, next) => {
164
+ if (api.validateOrRespond(req, res, { enabled: { type: 'boolean' } })) return;
165
+ next();
166
+ }, handle((req) => genesis.setEnabled(req.body && req.body.enabled === true, req.builder.id)));
162
167
  // The plan's ranges and vocabularies are genesis-home.js parsePlan's (one source);
163
168
  // this schema only names the fields so an unknown one is refused.
164
169
  router.post('/founding/plan', api.requireBuilder, owns, (req, res, next) => {
@@ -19,6 +19,7 @@
19
19
 
20
20
  const api = require('../../src/module-api');
21
21
  const { effectiveSettings } = require('./provisioning');
22
+ const { isTestProject } = require('./test-project');
22
23
 
23
24
  const log = api.logger('provisioning');
24
25
 
@@ -57,6 +58,8 @@ function catalogStatusForInstance(row) {
57
58
  }
58
59
 
59
60
  function catalogEntryForInstance(row, { ownerGithubId } = {}) {
61
+ // A test project is never on the public map (task 1005036, ./test-project.js): the honest refusal, not a failure.
62
+ if (isTestProject(row)) return null;
60
63
  const origin = catalogOriginForInstance(row);
61
64
  if (!origin) return null;
62
65
  const owner = Number(ownerGithubId);
@@ -0,0 +1,21 @@
1
+ -- provisioning_037_update_subscribed.sql — whether an instance takes unattended patch upgrades,
2
+ -- stored on the instance row (task 1004150, ADR 0325 decision 5).
3
+ --
4
+ -- WHY. Every other fleet fact lives in provisioning_instances; "which instances the nightly sweep
5
+ -- moves on its own" lived only in config/update-subscriptions.json. That left GET /provisioning/fleet
6
+ -- unable to answer the one question a fleet view exists for, and let a restored control-plane backup
7
+ -- bring back every instance row with an empty roster — the sweep then reported failed=0 while
8
+ -- upgrading nothing. On the row, the subscription and its update_channel (provisioning_029) travel
9
+ -- and restore together.
10
+ --
11
+ -- DEFAULT false: an instance is armed only by an explicit act, as the shipped-empty roster was
12
+ -- (ADR 0136). The roster file stays honoured as an override, so an operator's current arming is
13
+ -- not silently dropped.
14
+ --
15
+ -- Additive and namespaced (ADR 0083): no down-migration, idempotent — safe to re-run.
16
+
17
+ BEGIN;
18
+
19
+ ALTER TABLE provisioning_instances ADD COLUMN IF NOT EXISTS update_subscribed boolean NOT NULL DEFAULT false;
20
+
21
+ COMMIT;