@bongos/core 1.19.1073 → 1.19.1074

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 (297) hide show
  1. package/.bongos-core.json +320 -465
  2. package/.claude/skills/feedback/SKILL.md +2 -2
  3. package/clients/bongos-client/README.md +1 -1
  4. package/clients/bongos-client/bongos-client.global.js +4 -60
  5. package/clients/bongos-client/index.cjs +4 -60
  6. package/clients/bongos-client/index.d.ts +5 -90
  7. package/clients/bongos-client/index.mjs +4 -60
  8. package/config/branding.neutral.json +1 -5
  9. package/config/modules.neutral.json +2 -3
  10. package/config/scheduled-routines.json +0 -2
  11. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +1 -0
  12. package/docs/adr/0285-a-shared-box-holds-about-twelve-projects-per-gb-and-memory-is-the-wall.md +1 -0
  13. package/docs/adr/0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md +1 -1
  14. package/docs/adr/0327-a-cloud-host-runs-on-the-owners-account-and-the-key-is-borrowed.md +1 -1
  15. package/docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md +2 -0
  16. package/docs/adr/0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md +94 -0
  17. package/docs/adr/README.md +1 -0
  18. package/docs/api/openapi.json +171 -1661
  19. package/docs/api-reference.md +9 -41
  20. package/docs/architecture.md +16 -1
  21. package/docs/branding-contract.md +0 -3
  22. package/docs/copy-inventory.md +454 -479
  23. package/docs/copy-registry.json +746 -978
  24. package/docs/file-map.md +5 -34
  25. package/docs/module-api-changelog.md +2 -0
  26. package/docs/modules-contract.md +13 -1
  27. package/docs/onboarding/diagrams/04-architecture.mmd +11 -22
  28. package/docs/onboarding/diagrams/README.md +3 -3
  29. package/docs/onboarding/diagrams/assertions.json +2 -22
  30. package/docs/onboarding/primer.md +9 -28
  31. package/docs/page-inventory.json +4 -1
  32. package/docs/page-readings.json +1032 -1077
  33. package/docs/recipes/render-pilot.md +29 -10
  34. package/migrations/core_257_module_store_registry.sql +87 -0
  35. package/migrations/core_258_drop_dev_box_tables.sql +55 -0
  36. package/modules/agents/module.json +1 -0
  37. package/modules/autonomy/module.json +1 -0
  38. package/modules/builder-settings/module.json +1 -0
  39. package/modules/builder-settings/render-prefs.js +4 -5
  40. package/modules/copy-desk/docx.js +78 -5
  41. package/modules/copy-desk/module.json +1 -0
  42. package/modules/copy-desk/routes/copy-desk.js +9 -9
  43. package/modules/copy-desk/tests/copy_docx.mjs +107 -1
  44. package/modules/copy-desk/tests/fixtures/word-docx.mjs +30 -0
  45. package/modules/discord/module.json +1 -0
  46. package/modules/economy/module.json +1 -0
  47. package/modules/economy/routes/credits.js +1 -2
  48. package/modules/government/catalog.js +13 -17
  49. package/modules/government/migrations/government_019_retire_box_permissions.sql +33 -0
  50. package/modules/government/module.json +1 -0
  51. package/modules/government/protected-surfaces.json +3 -11
  52. package/modules/government/resolver.js +3 -46
  53. package/modules/government/routes/government.js +0 -2
  54. package/modules/government/session-scopes.js +43 -83
  55. package/modules/government/session-scopes.json +8 -18
  56. package/modules/grading/module.json +1 -0
  57. package/modules/hall-ui/module.json +1 -0
  58. package/modules/hall-ui/public/board-lib.js +2 -2
  59. package/modules/hall-ui/public/builders.js +29 -40
  60. package/modules/hall-ui/public/collab.html +1 -1
  61. package/modules/hall-ui/public/collab.js +1 -1
  62. package/modules/hall-ui/public/diagrams.html +3 -3
  63. package/modules/hall-ui/public/dom-utils.js +1 -1
  64. package/modules/hall-ui/public/gate.js +2 -2
  65. package/modules/hall-ui/public/gate.states.json +1 -1
  66. package/modules/hall-ui/public/hall-render.js +43 -79
  67. package/modules/hall-ui/public/index.html +5 -1
  68. package/modules/hall-ui/public/modules.js +1 -0
  69. package/modules/hall-ui/public/oversight.css +5 -5
  70. package/modules/hall-ui/public/palette.js +0 -11
  71. package/modules/hall-ui/public/primer.js +1 -1
  72. package/modules/hall-ui/public/sessions.html +1 -1
  73. package/modules/hall-ui/public/settings-sessions.js +1 -1
  74. package/modules/hall-ui/public/settings.css +0 -1
  75. package/modules/hall-ui/public/settings.html +4 -33
  76. package/modules/hall-ui/public/settings.js +3 -33
  77. package/modules/hall-ui/public/settings.states.json +2 -2
  78. package/modules/hall-ui/public/studio.html +1 -1
  79. package/modules/hall-ui/public/style.css +10 -13
  80. package/modules/hall-ui/public/task.html +4 -1
  81. package/modules/hall-ui/public/task.js +36 -1
  82. package/modules/hall-ui/public/tweak-editor-lib.js +18 -1
  83. package/modules/hall-ui/public/tweak-editor.css +16 -0
  84. package/modules/hall-ui/public/tweak-editor.html +26 -3
  85. package/modules/hall-ui/public/tweak-editor.js +32 -1
  86. package/modules/hall-ui/public/work.js +1 -1
  87. package/modules/hall-ui/records/tweak-editor.md +1 -0
  88. package/modules/ideas/module.json +1 -0
  89. package/modules/lifecycle/db-claim-reads.js +1 -58
  90. package/modules/lifecycle/db-ship.js +0 -5
  91. package/modules/lifecycle/db.js +0 -4
  92. package/modules/lifecycle/kickoff-checklist.js +0 -4
  93. package/modules/lifecycle/lifecycle.js +0 -8
  94. package/modules/lifecycle/module.json +1 -0
  95. package/modules/lifecycle/routes/tasks.js +5 -7
  96. package/modules/memory/module.json +1 -0
  97. package/modules/npm-release/module.json +1 -0
  98. package/modules/onboarding/module.json +1 -0
  99. package/modules/onboarding/onboarding-state.js +17 -36
  100. package/modules/onboarding/routes/access-requests.js +3 -3
  101. package/modules/platform-identity/module.json +1 -0
  102. package/modules/platform-identity/platform-identity.js +13 -5
  103. package/modules/platform-identity/routes/sso.js +2 -2
  104. package/modules/platform-identity/tests/platform-identity.mjs +17 -3
  105. package/modules/provisioning/module.json +1 -0
  106. package/modules/provisioning/provisioning.js +2 -2
  107. package/modules/provisioning/starter-bundles.js +5 -25
  108. package/modules/public-landing/module.json +1 -0
  109. package/modules/public-landing/public/assets/cosmos.css +1 -1
  110. package/modules/public-landing/public/contact.html +1 -1
  111. package/modules/public-landing/public/privacy.html +1 -1
  112. package/modules/public-landing/public/projects.html +101 -32
  113. package/modules/public-landing/public/projects.probes.json +2 -2
  114. package/modules/public-landing/public/projects.states.json +4 -3
  115. package/modules/public-landing/public/terms.html +1 -1
  116. package/modules/security/module.json +1 -0
  117. package/modules/sessions/module.json +1 -0
  118. package/modules/specialities/module.json +1 -0
  119. package/modules/status-ui/module.json +1 -0
  120. package/modules/ui-design/module.json +1 -0
  121. package/package-lock.json +2 -2
  122. package/package.json +1 -1
  123. package/release-notes.json +78 -0
  124. package/scripts/gds/artifact-format.js +203 -0
  125. package/scripts/gds/audit-authorship.sh +1 -13
  126. package/scripts/gds/claim.js +0 -15
  127. package/scripts/gds/cli-lib.js +8 -8
  128. package/scripts/gds/diagram-facts.js +12 -23
  129. package/scripts/gds/do-api.js +13 -80
  130. package/scripts/gds/doc-cli-guard.js +2 -0
  131. package/scripts/gds/doctor.js +1 -1
  132. package/scripts/gds/feedback-latest.js +139 -9
  133. package/scripts/gds/fitness-checks-identity.js +2 -7
  134. package/scripts/gds/fitness-checks-write-validation.js +4 -0
  135. package/scripts/gds/fitness-ratchets.js +4 -4
  136. package/scripts/gds/fitness.js +10 -10
  137. package/scripts/gds/gen-diagrams.js +49 -71
  138. package/scripts/gds/http-api-client.js +12 -19
  139. package/scripts/gds/init.js +1 -1
  140. package/scripts/gds/lint-infra-exec.js +1 -1
  141. package/scripts/gds/local-preview-lib.js +8 -39
  142. package/scripts/gds/local-preview.js +2 -15
  143. package/scripts/gds/migration-namespace.js +3 -3
  144. package/scripts/gds/module-artifact.js +212 -0
  145. package/scripts/gds/module.js +95 -6
  146. package/scripts/gds/package-core.js +5 -116
  147. package/scripts/gds/provision-config.js +2 -2
  148. package/scripts/gds/provision-net.js +2 -3
  149. package/scripts/gds/provision.js +9 -9
  150. package/scripts/gds/publish-manifest.js +1 -2
  151. package/scripts/gds/regen-instance-docs.js +1 -1
  152. package/scripts/gds/run-unit-tests.js +2 -16
  153. package/scripts/gds/sandbox-stage.js +39 -341
  154. package/scripts/gds/ship-flow.js +6 -6
  155. package/scripts/gds/ship-land.js +3 -4
  156. package/scripts/gds/ship-regen.js +10 -41
  157. package/scripts/gds/ship.js +4 -24
  158. package/scripts/gds/start.js +0 -15
  159. package/scripts/hall-preview/README.md +5 -5
  160. package/scripts/hall-preview/server.js +6 -6
  161. package/scripts/render-diagrams.sh +1 -1
  162. package/src/bongos/api-errors.js +1 -1
  163. package/src/bongos/auth-github.js +2 -3
  164. package/src/bongos/auth.js +51 -126
  165. package/src/bongos/db-kernel.js +2 -3
  166. package/src/bongos/db.js +5 -28
  167. package/src/bongos/module-scope-map.js +14 -29
  168. package/src/bongos/module-store.js +141 -0
  169. package/src/bongos/route-rank-check.js +3 -16
  170. package/src/bongos/routes/auth.js +0 -116
  171. package/src/bongos/routes/builders.js +5 -15
  172. package/src/bongos/routes/instance.js +8 -14
  173. package/src/bongos/routes/me.js +6 -22
  174. package/src/bongos/routes/modules.js +83 -0
  175. package/src/bongos/routes.js +5 -0
  176. package/src/module-api.js +13 -6
  177. package/src/module-loader/catalog.js +3 -0
  178. package/src/module-loader/loader.js +4 -4
  179. package/src/module-loader/manifest-schema.js +15 -7
  180. package/src/module-seams.js +2 -3
  181. package/src/modules.js +42 -14
  182. package/tests/agents_spend_guard.mjs +1 -1
  183. package/tests/api_client.mjs +0 -15
  184. package/tests/auth_page_gate.mjs +10 -10
  185. package/tests/canonical_profile_url.mjs +1 -3
  186. package/tests/claim_action.mjs +4 -1
  187. package/tests/claim_from_task_and_home.mjs +127 -0
  188. package/tests/consumer_layout_boot.mjs +2 -0
  189. package/tests/copy_desk_page_docx.mjs +33 -2
  190. package/tests/core_upgrade_runner.mjs +6 -6
  191. package/tests/deploy_divergence_line.mjs +11 -11
  192. package/tests/diagram_facts_offset.mjs +1 -1
  193. package/tests/do_api.mjs +106 -0
  194. package/tests/feedback_latest.mjs +122 -0
  195. package/tests/fitness.mjs +44 -42
  196. package/tests/gate_approvals.mjs +0 -1
  197. package/tests/go_live.mjs +3 -3
  198. package/tests/government_abuse_matrix.mjs +20 -19
  199. package/tests/government_ownership_scope.mjs +33 -35
  200. package/tests/government_parity.mjs +2 -2
  201. package/tests/government_protected_surfaces.mjs +56 -37
  202. package/tests/government_require_permission.mjs +3 -3
  203. package/tests/government_seed.mjs +37 -4
  204. package/tests/government_session_scope.mjs +92 -366
  205. package/tests/hall_error_envelope.mjs +4 -0
  206. package/tests/hall_landing_boot.mjs +4 -1
  207. package/tests/hall_palette.mjs +1 -16
  208. package/tests/hall_record_world.mjs +6 -0
  209. package/tests/hall_tweak_editor.mjs +52 -1
  210. package/tests/host_topology_skips.mjs +1 -2
  211. package/tests/html_comment_nesting.mjs +137 -0
  212. package/tests/infra_exec_paths.mjs +9 -10
  213. package/tests/init.mjs +4 -5
  214. package/tests/instance_manifest.mjs +18 -25
  215. package/tests/kickoff_checklist.mjs +1 -1
  216. package/tests/lib_sh_resolution.mjs +0 -221
  217. package/tests/local_preview.mjs +0 -12
  218. package/tests/module-scope-map.mjs +9 -9
  219. package/tests/module_catalog.mjs +1 -1
  220. package/tests/module_cli.mjs +15 -15
  221. package/tests/module_contributions.mjs +6 -6
  222. package/tests/module_loader.mjs +6 -6
  223. package/tests/module_manifest.mjs +13 -4
  224. package/tests/module_store_publish.mjs +307 -0
  225. package/tests/module_store_publish_route.mjs +121 -0
  226. package/tests/module_store_registry_migration.mjs +70 -0
  227. package/tests/modules.mjs +56 -26
  228. package/tests/onboarding_route_signals.mjs +21 -31
  229. package/tests/onboarding_state.mjs +35 -49
  230. package/tests/permission_path.mjs +0 -3
  231. package/tests/platform_boot.mjs +1 -2
  232. package/tests/profile_route.mjs +1 -6
  233. package/tests/projects_hub.mjs +5 -5
  234. package/tests/projects_hub_app_step.mjs +138 -0
  235. package/tests/projects_hub_module_picker.mjs +2 -2
  236. package/tests/projects_hub_pre_uat.mjs +4 -5
  237. package/tests/provision.mjs +7 -7
  238. package/tests/provisioning_starter_bundles.mjs +0 -22
  239. package/tests/publish_branch_route.mjs +10 -18
  240. package/tests/publish_manifest.mjs +1 -2
  241. package/tests/rank_tier_single_source.mjs +1 -13
  242. package/tests/repo_map.mjs +3 -4
  243. package/tests/runner_drift.mjs +3 -21
  244. package/tests/sandbox_stage.mjs +57 -390
  245. package/tests/session_rename_fallback.mjs +0 -21
  246. package/tests/ship_error_shape.mjs +1 -1
  247. package/tests/task_visual_slots.mjs +31 -0
  248. package/tests/terms_acceptance.mjs +1 -1
  249. package/tests/upgrade.mjs +4 -4
  250. package/tests/watch_sealed_floor.mjs +0 -1
  251. package/tests/wizard_draft_resume.mjs +2 -2
  252. package/tests/wizard_intent_resume.mjs +21 -10
  253. package/tests/wizard_preselect_why.mjs +18 -18
  254. package/docs/onboarding/browser-terminal-guide.md +0 -73
  255. package/docs/recipes/managed-settings-remote-control.md +0 -416
  256. package/modules/dev-box/CLAUDE.md +0 -15
  257. package/modules/dev-box/box-access.js +0 -623
  258. package/modules/dev-box/box-credential.js +0 -94
  259. package/modules/dev-box/box-onboard.js +0 -486
  260. package/modules/dev-box/boxes.js +0 -1038
  261. package/modules/dev-box/db.js +0 -39
  262. package/modules/dev-box/module.json +0 -19
  263. package/modules/dev-box/routes/box.js +0 -1223
  264. package/scripts/gds/box-auth-check.js +0 -160
  265. package/scripts/gds/box-infra.js +0 -321
  266. package/scripts/gds/box-sync.js +0 -216
  267. package/scripts/gds/box.js +0 -1332
  268. package/scripts/gds/cf-tunnel.js +0 -558
  269. package/scripts/gds/smoke-box.sh +0 -150
  270. package/scripts/gds/tree-preflight.js +0 -241
  271. package/src/bongos/app-pair.js +0 -172
  272. package/tests/app_pair.mjs +0 -168
  273. package/tests/box_access.mjs +0 -578
  274. package/tests/box_auth_check.mjs +0 -114
  275. package/tests/box_code_staleness.mjs +0 -129
  276. package/tests/box_connect_e2e.mjs +0 -196
  277. package/tests/box_cost.mjs +0 -70
  278. package/tests/box_credential.mjs +0 -125
  279. package/tests/box_dns_repoint.mjs +0 -153
  280. package/tests/box_env.mjs +0 -64
  281. package/tests/box_host_keys.mjs +0 -126
  282. package/tests/box_infra_resolve.mjs +0 -246
  283. package/tests/box_onboard.mjs +0 -317
  284. package/tests/box_scope_session.mjs +0 -119
  285. package/tests/box_sweep_docs.mjs +0 -119
  286. package/tests/box_sync.mjs +0 -55
  287. package/tests/box_sync_scope_report.mjs +0 -126
  288. package/tests/box_task_scope.mjs +0 -169
  289. package/tests/box_task_scope_pause.mjs +0 -66
  290. package/tests/box_terminal_ssrf.mjs +0 -96
  291. package/tests/boxes.mjs +0 -1585
  292. package/tests/cf_ruleset_preflight.mjs +0 -142
  293. package/tests/cf_tunnel.mjs +0 -496
  294. package/tests/docs_scrubber_damage.mjs +0 -111
  295. package/tests/module_scope_active_claims.mjs +0 -94
  296. package/tests/ship_api_client_pathspec.mjs +0 -100
  297. package/tests/tree_preflight.mjs +0 -243
@@ -326,6 +326,57 @@ try {
326
326
  assert.match(html, /no longer match the page/);
327
327
  assert.match(html, /hero: “<b>new<\/b> line” \(added\)/);
328
328
  assert.equal(b.el('tw-notice').querySelectorAll('b').length, 0);
329
+ // The panel names the same lines, escaped too.
330
+ assert.equal(b.el('tw-refused').hidden, false);
331
+ assert.match(b.el('tw-refused-list').innerHTML, /hero: “<b>new<\/b> line” \(added\)/);
332
+ assert.equal(b.el('tw-refused-list').querySelectorAll('b').length, 0);
333
+ });
334
+
335
+ // Task 1004354: the owner's Google Docs upload was refused and nothing on
336
+ // the screen said so where they were looking.
337
+ await test('a refused upload opens a panel over the page: what happened, the next step, and a way on', async () => {
338
+ const b = bootEditor({ answers: {
339
+ 'GET /copy-desk/pages/landing:index/draft': ok(editorRead()),
340
+ 'FETCH POST /copy-desk/pages/landing:index/draft.docx': { ok: false, data: { error: { code: 'wrong_page', details: { page_details: false, mismatched: ['title', 'headings'], what_this_means: 'This file is not the download of this page: its title or its section headings are not this page\'s. Nothing was saved.' } } } },
341
+ } });
342
+ await b.settle();
343
+ assert.equal(b.el('tw-refused').hidden, true, 'closed until a refusal');
344
+ b.el('tw-file').fire('change', { target: { files: [{ type: '' }], value: 'x' } });
345
+ await b.settle();
346
+ assert.equal(b.el('tw-refused').hidden, false);
347
+ assert.equal(b.el('tw-refused-title').textContent, 'This file is a different page.');
348
+ assert.equal(boot.focused, b.el('tw-refused-title'), 'focus moves to the panel');
349
+ assert.match(b.el('tw-refused-what').textContent, /not the download of this page/);
350
+ assert.match(b.el('tw-refused-next').textContent, /Pick the file you downloaded from this page/);
351
+ assert.equal(b.el('tw-refused-list').hidden, true, 'no lines to name');
352
+ assert.equal(b.el('tw-refused-download').hidden, false);
353
+ assert.equal(b.el('tw-bar').inert, true);
354
+ assert.equal(b.el('tw-paper').inert, true);
355
+ assert.equal(b.el('tw-upload').textContent, 'upload .docx');
356
+ // Close: the page is back, and the paper still says what happened.
357
+ await b.click('tw-refused-close');
358
+ assert.equal(b.el('tw-refused').hidden, true);
359
+ assert.equal(b.el('tw-bar').inert, false);
360
+ assert.equal(b.el('tw-paper').inert, false);
361
+ assert.equal(boot.focused, b.el('tw-upload'));
362
+ assert.match(b.el('tw-notice').innerHTML, /not the download of this page/);
363
+ // Download again from the panel: it closes, and the page downloads.
364
+ b.el('tw-file').fire('change', { target: { files: [{ type: '' }], value: 'x' } });
365
+ await b.settle();
366
+ await b.click('tw-refused-download');
367
+ await b.settle();
368
+ assert.equal(b.el('tw-refused').hidden, true);
369
+ assert.equal(b.el('tw-download').textContent, 'downloaded .docx');
370
+ });
371
+
372
+ await test('each refusal the upload can answer has its own headline and next step', () => {
373
+ for (const code of ['wrong_page', 'reading_moved', 'structure_changed', 'not_a_docx', 'upload_too_large', 'page_not_held', 'page_in_flight']) {
374
+ const r = lib.uploadRefusal(code);
375
+ assert.ok(r.title && r.title !== 'Your file was not saved.', code);
376
+ assert.ok(r.next.length > 20, code);
377
+ }
378
+ assert.equal(lib.uploadRefusal('something_new').title, 'Your file was not saved.');
379
+ assert.match(lib.uploadRefusal('not_a_docx').next, /Google Docs/);
329
380
  });
330
381
 
331
382
  await test('submit: saves first, then "Home is in.", next up, and take next opens the next page', async () => {
@@ -409,7 +460,7 @@ try {
409
460
  assert.ok(HTML.indexOf('tweak-editor-lib.js') < HTML.indexOf('tweak-editor.js?'), 'the lib loads first');
410
461
  assert.match(HTML, /shell\.js\?v=2026-09-28-archive/);
411
462
  assert.equal(/fonts\.googleapis\.com\/css/.test(HTML), false);
412
- for (const id of ['tw-bar', 'tw-counter', 'tw-open', 'tw-download', 'tw-upload', 'tw-file', 'tw-submit', 'tw-paper', 'tw-doc', 'tw-notice', 'tw-done', 'tw-take', 'tw-pick', 'tw-picks']) {
463
+ for (const id of ['tw-bar', 'tw-counter', 'tw-open', 'tw-download', 'tw-upload', 'tw-file', 'tw-submit', 'tw-paper', 'tw-doc', 'tw-notice', 'tw-done', 'tw-take', 'tw-pick', 'tw-picks', 'tw-refused', 'tw-refused-title', 'tw-refused-list', 'tw-refused-next', 'tw-refused-download', 'tw-refused-close']) {
413
464
  assert.ok(new RegExp(`id="${id}"`).test(HTML), `#${id} is in the markup`);
414
465
  }
415
466
  assert.match(HTML, />← studio</);
@@ -33,7 +33,6 @@ const ALLOWED = {
33
33
  allow_rule_guard: '.claude/hooks/allow-rule-guard.js (package-excluded)',
34
34
  art_stage: 'scripts/gds/art-stage.js (art-pipeline tooling, not in the core)',
35
35
  bongos_downloads: 'config/bongos-cli.json (instance content)',
36
- box_dns_repoint: 'infra/box-dns-cloudflare.sh (host topology)',
37
36
  box_ship_permissions: '.claude/settings.json (package-excluded)',
38
37
  conductor: '.claude/hooks/conductor.js (package-excluded)',
39
38
  deploy_instance: 'scripts/deploy/deploy-instance.sh (host topology)',
@@ -94,5 +93,5 @@ test('the ratchet is non-vacuous and actually detects a skip', () => {
94
93
  assert.ok(actual.length >= 15, `expected the known skip set, found ${actual.length}`);
95
94
  assert.ok(actual.includes('stop_refcheck'), 'the hand-rolled precedent must still be detected');
96
95
  assert.ok(actual.includes('conductor'), 'a requireHostModule caller must be detected');
97
- assert.ok(actual.includes('box_dns_repoint'), 'a skipUnlessPresent caller must be detected');
96
+ assert.ok(actual.includes('deploy_instance'), 'a skipUnlessPresent caller must be detected');
98
97
  });
@@ -0,0 +1,137 @@
1
+ // tests/html_comment_nesting.mjs — no served page's comment may quote comment
2
+ // syntax (task 1004359, BV2.TW24).
3
+ //
4
+ // An HTML comment ends at the FIRST closer, whatever its author meant. The head
5
+ // note of contact, privacy and terms quoted the apex-only sentinel verbatim, so
6
+ // the sentinel's closer ended the note early and the rest of the developer
7
+ // prose rendered as text at the top of all three pages — and the page reader
8
+ // filed it as page copy, so it reached Tweak Mode's editor too.
9
+ //
10
+ // The guard: in every served .html (each modules/<key>/public tree, plus
11
+ // public-docs), no comment's body holds another opener, and no closer stands
12
+ // outside a comment. Raw-text elements (script, style, textarea) are skipped:
13
+ // the browser never parses comments inside them.
14
+ //
15
+ // The file list is walked from disk, not git, so a new untracked page is
16
+ // scanned too, and a floor on the count keeps an emptied list from passing.
17
+ //
18
+ // Run: node --test tests/html_comment_nesting.mjs
19
+
20
+ import assert from 'node:assert/strict';
21
+ import { test } from 'node:test';
22
+ import fs from 'node:fs';
23
+ import path from 'node:path';
24
+ import { fileURLToPath } from 'node:url';
25
+
26
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
27
+ const OPEN = '<!' + '--';
28
+ const CLOSE = '--' + '>';
29
+
30
+ /** Every .html under a served tree: modules/<key>/public/** and public-docs/**. */
31
+ function servedHtml() {
32
+ const roots = [path.join(ROOT, 'public-docs')];
33
+ for (const key of fs.readdirSync(path.join(ROOT, 'modules'))) {
34
+ roots.push(path.join(ROOT, 'modules', key, 'public'));
35
+ }
36
+ const out = [];
37
+ const walk = (dir) => {
38
+ if (!fs.existsSync(dir)) return;
39
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
40
+ const p = path.join(dir, e.name);
41
+ if (e.isDirectory()) { if (e.name !== 'node_modules') walk(p); }
42
+ else if (e.name.endsWith('.html')) out.push(p);
43
+ }
44
+ };
45
+ roots.forEach(walk);
46
+ return out;
47
+ }
48
+
49
+ /** Blank raw-text elements, keeping newlines so line numbers still point true. */
50
+ function blankRawText(html) {
51
+ return html.replace(/<(script|style|textarea)\b[^>]*>[\s\S]*?<\/\1\s*>/gi,
52
+ (m) => m.replace(/[^\n]/g, ' '));
53
+ }
54
+
55
+ const lineAt = (s, i) => s.slice(0, i).split('\n').length;
56
+
57
+ /**
58
+ * The comment traps in one page, as "line N: why" strings (empty when clean).
59
+ * A comment runs from an opener to the FIRST closer after it, as the parser reads it.
60
+ */
61
+ function commentTraps(html) {
62
+ const s = blankRawText(html);
63
+ const traps = [];
64
+ let i = 0;
65
+ for (;;) {
66
+ const o = s.indexOf(OPEN, i);
67
+ const c = s.indexOf(CLOSE, i);
68
+ if (c !== -1 && (o === -1 || c < o)) {
69
+ traps.push(`line ${lineAt(s, c)}: a comment closer outside any comment (text before it has leaked onto the page)`);
70
+ i = c + CLOSE.length;
71
+ continue;
72
+ }
73
+ if (o === -1) break;
74
+ const end = s.indexOf(CLOSE, o + OPEN.length);
75
+ if (end === -1) { traps.push(`line ${lineAt(s, o)}: a comment that never closes`); break; }
76
+ const inner = s.slice(o + OPEN.length, end).indexOf(OPEN);
77
+ if (inner !== -1) {
78
+ traps.push(`line ${lineAt(s, o + OPEN.length + inner)}: a comment that quotes a comment opener (its own closer ends the outer comment early)`);
79
+ }
80
+ i = end + CLOSE.length;
81
+ }
82
+ return traps;
83
+ }
84
+
85
+ // ── the detector, at unit altitude ──────────────────────────────────────────
86
+
87
+ test('detector: the exact TW24 head note is caught', () => {
88
+ const html = `<head>\n${OPEN} Server-stamped: every ${OPEN}apex-only${CLOSE} region is REMOVED off\n the hub. ${CLOSE}\n<title>x</title>`;
89
+ const traps = commentTraps(html);
90
+ assert.equal(traps.length, 2, traps.join('\n'));
91
+ assert.match(traps[0], /^line 2: .*quotes a comment opener/);
92
+ assert.match(traps[1], /^line 3: .*closer outside any comment/);
93
+ });
94
+
95
+ test('detector: the fixed wording and the real sentinels are clean', () => {
96
+ const html = `${OPEN} every apex-only:start/end region is REMOVED off the hub ${CLOSE}\n`
97
+ + `<p>a</p>${OPEN}apex-only:start${CLOSE}<p>b</p>${OPEN}apex-only:end${CLOSE}`;
98
+ assert.deepEqual(commentTraps(html), []);
99
+ });
100
+
101
+ test('detector: a lone opener inside a comment is caught even with no stray closer', () => {
102
+ assert.equal(commentTraps(`${OPEN} see ${OPEN} here ${CLOSE}`).length, 1);
103
+ });
104
+
105
+ test('detector: an unclosed comment is caught', () => {
106
+ assert.match(commentTraps(`<p>a</p>${OPEN} never ends`)[0], /never closes/);
107
+ });
108
+
109
+ test('detector: comment syntax inside script, style and textarea is ignored', () => {
110
+ const html = `<script>const re = /${OPEN}x${CLOSE}/; if (a ${CLOSE} b) {}</script>`
111
+ + `<style>/* ${CLOSE} */</style><textarea>${OPEN}</textarea>`;
112
+ assert.deepEqual(commentTraps(html), []);
113
+ });
114
+
115
+ test('detector: line numbers survive a blanked script above the trap', () => {
116
+ const html = `<script>\n1\n2\n</script>\n${OPEN} a ${OPEN} b ${CLOSE}`;
117
+ assert.match(commentTraps(html)[0], /^line 5: /);
118
+ });
119
+
120
+ // ── every served page ───────────────────────────────────────────────────────
121
+
122
+ test('every served .html page is free of comment traps', () => {
123
+ const files = servedHtml();
124
+ // 39 pages on 2026-09-28 (hall 31, landing 5, status 2, public-docs 1); a
125
+ // floor well under that keeps a broken walk from passing on an empty list.
126
+ assert.ok(files.length >= 30, `expected at least 30 served pages, found ${files.length}`);
127
+ const names = files.map((f) => path.relative(ROOT, f).split(path.sep).join('/'));
128
+ for (const must of ['modules/public-landing/public/contact.html',
129
+ 'modules/public-landing/public/privacy.html', 'modules/public-landing/public/terms.html']) {
130
+ assert.ok(names.includes(must), `${must} is in the scanned set`);
131
+ }
132
+ const bad = [];
133
+ files.forEach((f, k) => {
134
+ for (const t of commentTraps(fs.readFileSync(f, 'utf8'))) bad.push(`${names[k]} ${t}`);
135
+ });
136
+ assert.deepEqual(bad, [], `comment traps:\n${bad.join('\n')}`);
137
+ });
@@ -31,11 +31,10 @@ function test(name, fn) {
31
31
  catch (err) { console.error(` FAIL ${name}`); console.error(' ', err.message); failed++; }
32
32
  }
33
33
 
34
- // Host topology means systemd UNITS, not the bare infra/ directory: the neutral core
35
- // carries no units and no launcher (task 1002470), but it DOES ship the four ON-BOX
36
- // reporter scripts in infra/ (task 1002699 — dev boxes clone this repo and wire them
37
- // as crons). So the real-tree unit lint is gated on *.service files being present,
38
- // and the launcher checks on core-node.sh — the synthetic counterfactuals always run.
34
+ // Host topology means systemd UNITS: the neutral core carries no units, no launcher
35
+ // and no infra/ directory (task 1002470), so the real-tree unit lint is gated on
36
+ // *.service files being present, and the launcher checks on core-node.sh — the
37
+ // synthetic counterfactuals always run.
39
38
  const INFRA_DIR = path.join(REPO, 'infra');
40
39
  const HAS_UNITS = fs.existsSync(INFRA_DIR) &&
41
40
  fs.readdirSync(INFRA_DIR).some((f) => f.endsWith('.service'));
@@ -50,10 +49,10 @@ hostTest('lint-infra-exec: every core-script unit resolves in a consumer layout
50
49
  const r = lintInfraExec({ instanceRoot: REPO, coreRoot: REPO });
51
50
  assert.deepEqual(r.violations, [], `expected no violations, got: ${JSON.stringify(r.violations)}`);
52
51
  assert.ok(r.ok, 'lint should pass');
53
- assert.equal(r.checked.length, 11, `expected all 11 core-script units repointed + resolving, got ${r.checked.length}`);
54
- // The three core CLIs the units invoke are all covered.
52
+ assert.ok(r.checked.length >= 2, `expected the core-script units repointed + resolving, got ${r.checked.length}`);
53
+ // The core CLIs the units invoke are all covered.
55
54
  const rels = new Set(r.checked.map((c) => c.rel));
56
- for (const rel of ['scripts/gds/box.js', 'scripts/gds/provision.js', 'scripts/gds/search-ingest-db.js']) {
55
+ for (const rel of ['scripts/gds/provision.js', 'scripts/gds/search-ingest-db.js']) {
57
56
  assert.ok(rels.has(rel), `expected a unit routing through ${rel}`);
58
57
  }
59
58
  });
@@ -64,9 +63,9 @@ test('lint-infra-exec: a bare repo-relative core ExecStart is flagged (counterfa
64
63
  try {
65
64
  fs.mkdirSync(path.join(tmp, 'infra'), { recursive: true });
66
65
  fs.mkdirSync(path.join(tmp, 'scripts', 'gds'), { recursive: true });
67
- fs.writeFileSync(path.join(tmp, 'scripts', 'gds', 'box.js'), '// probe\n');
66
+ fs.writeFileSync(path.join(tmp, 'scripts', 'gds', 'provision.js'), '// probe\n');
68
67
  fs.writeFileSync(path.join(tmp, 'infra', 'legacy.service'),
69
- '[Service]\nExecStart=/usr/bin/node scripts/gds/box.js run-intents --apply\n');
68
+ '[Service]\nExecStart=/usr/bin/node scripts/gds/provision.js run-intents --apply\n');
70
69
  const r = lintInfraExec({ instanceRoot: tmp, coreRoot: tmp });
71
70
  assert.ok(!r.ok, 'a bare repo-relative core ExecStart must fail the lint');
72
71
  assert.equal(r.violations.length, 1);
package/tests/init.mjs CHANGED
@@ -88,7 +88,6 @@ test('buildModulesConfig: every known module present, enabled ones true', () =>
88
88
  assert.equal(m['art-pipeline'], true);
89
89
  assert.equal(m.game, false);
90
90
  assert.equal(m.discord, false);
91
- assert.equal(m['dev-box'], false);
92
91
  });
93
92
 
94
93
  test('buildModulesConfig: vanilla (no modules) = all off', () => {
@@ -122,8 +121,8 @@ test('kickoffTasks: 7 base steps + 1 per enabled module', () => {
122
121
  const withArt = init.kickoffTasks(fullSpec());
123
122
  assert.equal(withArt.length, 8); // +1 Gemini key task
124
123
  assert.ok(withArt.some((t) => /Gemini/i.test(t.title)));
125
- const all = init.kickoffTasks(init.normalizeSpec({ modules: ['art-pipeline', 'discord', 'dev-box'], firstAdmin: 'x', repo: { owner: 'o', name: 'n' } }));
126
- assert.equal(all.length, 10);
124
+ const all = init.kickoffTasks(init.normalizeSpec({ modules: ['art-pipeline', 'discord'], firstAdmin: 'x', repo: { owner: 'o', name: 'n' } }));
125
+ assert.equal(all.length, 9);
127
126
  });
128
127
 
129
128
  test('kickoffTasks: includes the load-bearing owner-manual steps', () => {
@@ -141,7 +140,7 @@ test('kickoffTasks: includes the load-bearing owner-manual steps', () => {
141
140
  test('kickoffTasks: every kind/discipline satisfies the DB CHECK vocabulary', () => {
142
141
  const { VALID_KINDS, VALID_DISCIPLINES } = require('../modules/lifecycle/task-classifier.js');
143
142
  const all = [
144
- ...init.kickoffTasks(init.normalizeSpec({ modules: ['art-pipeline', 'discord', 'dev-box', 'game'],
143
+ ...init.kickoffTasks(init.normalizeSpec({ modules: ['art-pipeline', 'discord', 'game'],
145
144
  firstAdmin: 'x', repo: { owner: 'o', name: 'n' } })),
146
145
  init.productionTopologyKickoff(),
147
146
  ];
@@ -429,7 +428,7 @@ test('enabledModuleDeps: merges only the ENABLED modules’ declared deps', () =
429
428
  'art-pipeline': { sharp: '^0.34.5' },
430
429
  }[k] || {});
431
430
  const merged = init.enabledModuleDeps(
432
- { modules: { discord: true, game: false, 'art-pipeline': true, 'dev-box': false } },
431
+ { modules: { discord: true, game: false, 'art-pipeline': true } },
433
432
  { readDeps }
434
433
  );
435
434
  assert.deepEqual(merged, { 'discord.js': '^14.26.4', sharp: '^0.34.5' }); // game excluded (off)
@@ -30,17 +30,17 @@ const BRAND_VANILLA = {
30
30
  rankLabels: {},
31
31
  };
32
32
  const MODULES_OTB = {
33
- enabled: { game: true, 'art-pipeline': true, discord: true, 'dev-box': true },
33
+ enabled: { game: true, 'art-pipeline': true, discord: true },
34
34
  disciplines: ['engineer', 'artist'],
35
35
  };
36
36
  const MODULES_VANILLA = {
37
- enabled: { game: false, 'art-pipeline': false, discord: false, 'dev-box': false },
37
+ enabled: { game: false, 'art-pipeline': false, discord: false },
38
38
  disciplines: ['engineer'],
39
39
  };
40
40
  const REPO = { owner: 'example-owner', repo: 'example', branch: 'main', blob_base: 'x', tree_base: 'y' };
41
41
 
42
42
  test('manifest: schema id + headline name + embedded brand', () => {
43
- const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
43
+ const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false });
44
44
  assert.equal(m.schema, MANIFEST_SCHEMA);
45
45
  assert.equal(m.name, 'Example World');
46
46
  assert.equal(m.brand, BRAND_OTB);
@@ -48,13 +48,13 @@ test('manifest: schema id + headline name + embedded brand', () => {
48
48
  });
49
49
 
50
50
  test('manifest: brand.project rides the manifest — the settings read-back a manage page needs (task 1003140 / ADR 0190)', () => {
51
- const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
51
+ const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false });
52
52
  assert.deepEqual(m.brand.project, { platformVisibility: 'gated', joinability: 'invite_only' },
53
53
  'a client reads what the instance RESOLVED from here, never from the platform PATCH response');
54
54
  });
55
55
 
56
56
  test('manifest: rank ladder ordered entry→top, labelled from brand', () => {
57
- const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
57
+ const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false });
58
58
  assert.deepEqual(m.ranks.map((r) => r.key), ['xenos', 'thetes', 'metic', 'archon']);
59
59
  assert.deepEqual(m.ranks.map((r) => r.tier), [1, 2, 3, 4]);
60
60
  assert.equal(m.ranks[0].label, 'Xenos');
@@ -62,46 +62,39 @@ test('manifest: rank ladder ordered entry→top, labelled from brand', () => {
62
62
  });
63
63
 
64
64
  test('manifest: rank label falls back to the key when unlabelled', () => {
65
- const m = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'no_repo_info' }, authConfigured: false, openEnrollment: false, devBoxEnabled: false });
65
+ const m = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'no_repo_info' }, authConfigured: false, openEnrollment: false });
66
66
  assert.equal(m.ranks[0].label, 'xenos');
67
67
  assert.equal(m.ranks[2].label, 'metic');
68
68
  });
69
69
 
70
70
  test('manifest: repo projects to public clone coords; null on error', () => {
71
- const ok = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
71
+ const ok = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false });
72
72
  assert.deepEqual(ok.repo, { owner: 'example-owner', repo: 'example', branch: 'main', url: 'https://github.com/example-owner/example' });
73
- const none = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'no_repo_info' }, authConfigured: false, openEnrollment: false, devBoxEnabled: false });
73
+ const none = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'no_repo_info' }, authConfigured: false, openEnrollment: false });
74
74
  assert.equal(none.repo, null);
75
75
  });
76
76
 
77
77
  test('manifest: auth methods gated on configured', () => {
78
- const on = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
78
+ const on = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false });
79
79
  assert.deepEqual(on.auth.methods, AUTH_METHODS);
80
80
  assert.equal(on.auth.configured, true);
81
- const off = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'x' }, authConfigured: false, openEnrollment: false, devBoxEnabled: false });
81
+ const off = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'x' }, authConfigured: false, openEnrollment: false });
82
82
  assert.deepEqual(off.auth.methods, []);
83
83
  assert.equal(off.auth.configured, false);
84
84
  });
85
85
 
86
86
  test('manifest: admission policy reflects open-enrollment flag', () => {
87
- const invite = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
87
+ const invite = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false });
88
88
  assert.equal(invite.admission.policy, 'invite-only');
89
- const open = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: true, devBoxEnabled: true });
89
+ const open = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: true });
90
90
  assert.equal(open.admission.policy, 'open');
91
91
  });
92
92
 
93
- test('manifest: dev-box availability mirrors the module flag', () => {
94
- const on = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
95
- assert.equal(on.devBox.available, true);
96
- const off = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'x' }, authConfigured: false, openEnrollment: false, devBoxEnabled: false });
97
- assert.equal(off.devBox.available, false);
98
- });
99
-
100
93
  test('manifest: no server-only secrets/topology leak into the body', () => {
101
94
  // The assembler only ever reads the client-safe projections it is handed, so
102
95
  // even if a caller passed a fuller object, these server-only keys must not
103
96
  // appear. We assert on the serialized body (deep, key-name based).
104
- const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
97
+ const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false });
105
98
  const json = JSON.stringify(m);
106
99
  for (const forbidden of ['cookieDomain', 'oauthOrigin', 'publicOrigin', 'envPrefix', 'client_secret', 'dbName', 'instanceDbName', 'token']) {
107
100
  assert.ok(!json.includes(forbidden), `manifest leaked "${forbidden}"`);
@@ -109,8 +102,8 @@ test('manifest: no server-only secrets/topology leak into the body', () => {
109
102
  });
110
103
 
111
104
  test('manifest: vanilla instance is fully populated (every field present)', () => {
112
- const m = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'x' }, authConfigured: false, openEnrollment: false, devBoxEnabled: false });
113
- for (const k of ['schema', 'name', 'brand', 'modules', 'ranks', 'repo', 'auth', 'admission', 'devBox']) {
105
+ const m = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'x' }, authConfigured: false, openEnrollment: false });
106
+ for (const k of ['schema', 'name', 'brand', 'modules', 'ranks', 'repo', 'auth', 'admission']) {
114
107
  assert.ok(k in m, `missing field ${k}`);
115
108
  }
116
109
  assert.equal(m.name, 'Cloud Bongos');
@@ -124,7 +117,7 @@ const buildInstanceRouter = instanceMod;
124
117
  import { strangerManifest } from '../src/bongos/routes/instance.js';
125
118
 
126
119
  const IDP = { origin: 'https://hub.example.com', client_id: 'proj-1' };
127
- const FULL = () => buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: false, openEnrollment: false, devBoxEnabled: true, idp: IDP });
120
+ const FULL = () => buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: false, openEnrollment: false, idp: IDP });
128
121
 
129
122
  test('stranger manifest: schema, name, brand.project, auth (idp kept), admission — and nothing else', () => {
130
123
  const s = strangerManifest(FULL());
@@ -136,13 +129,13 @@ test('stranger manifest: schema, name, brand.project, auth (idp kept), admission
136
129
  assert.deepEqual(s.auth, { methods: AUTH_METHODS, configured: true, idp: IDP });
137
130
  assert.deepEqual(s.admission, { policy: 'invite-only' });
138
131
  const json = JSON.stringify(s);
139
- for (const gone of ['example-owner', 'github.com', 'Straight example world', 'Hermeslines', 'drachmae', 'art-pipeline', 'Archon', 'devBox']) {
132
+ for (const gone of ['example-owner', 'github.com', 'Straight example world', 'Hermeslines', 'drachmae', 'art-pipeline', 'Archon']) {
140
133
  assert.ok(!json.includes(gone), `a stranger must not read "${gone}"`);
141
134
  }
142
135
  });
143
136
 
144
137
  test('stranger manifest: a brand with no project policy projects none', () => {
145
- const s = strangerManifest(buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'x' }, authConfigured: false, openEnrollment: true, devBoxEnabled: false }));
138
+ const s = strangerManifest(buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'x' }, authConfigured: false, openEnrollment: true }));
146
139
  assert.deepEqual(JSON.parse(JSON.stringify(s.brand)), { identity: { productName: 'Cloud Bongos' } });
147
140
  assert.equal(s.admission.policy, 'open');
148
141
  });
@@ -76,7 +76,7 @@ test('firstSignIn: never reads spec.repo or spec.firstAdmin (the steps that need
76
76
  });
77
77
 
78
78
  test('every variant satisfies the DB CHECK vocabulary (tasks_kind_check / tasks_discipline_check)', () => {
79
- const full = { ...spec(), modules: ['art-pipeline', 'discord', 'dev-box'] };
79
+ const full = { ...spec(), modules: ['art-pipeline', 'discord'] };
80
80
  const all = [
81
81
  ...kickoffChecklist(full),
82
82
  ...kickoffChecklist(full, { provisioned: true }),