@bongos/core 1.21.29 → 1.21.31

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 (60) hide show
  1. package/.bongos-core.json +70 -65
  2. package/docs/copy-inventory.md +374 -389
  3. package/docs/copy-registry.json +472 -637
  4. package/docs/module-api-changelog.md +4 -0
  5. package/docs/page-readings.json +873 -883
  6. package/modules/hall-ui/public/atlas.html +3 -3
  7. package/modules/hall-ui/public/blockers.html +3 -3
  8. package/modules/hall-ui/public/board-room.html +3 -3
  9. package/modules/hall-ui/public/city.html +3 -3
  10. package/modules/hall-ui/public/collab.html +3 -3
  11. package/modules/hall-ui/public/commands.html +3 -3
  12. package/modules/hall-ui/public/copy-desk.html +3 -3
  13. package/modules/hall-ui/public/craft-mode.js +57 -3
  14. package/modules/hall-ui/public/deploy.html +3 -3
  15. package/modules/hall-ui/public/diagrams.html +3 -3
  16. package/modules/hall-ui/public/drachmae.html +3 -3
  17. package/modules/hall-ui/public/fleet.html +3 -3
  18. package/modules/hall-ui/public/gate.html +3 -3
  19. package/modules/hall-ui/public/goal-map.html +3 -3
  20. package/modules/hall-ui/public/goals.html +3 -3
  21. package/modules/hall-ui/public/government.html +3 -3
  22. package/modules/hall-ui/public/idea.html +3 -3
  23. package/modules/hall-ui/public/ideas.html +3 -3
  24. package/modules/hall-ui/public/index.html +3 -3
  25. package/modules/hall-ui/public/modules.html +3 -3
  26. package/modules/hall-ui/public/primer.html +3 -3
  27. package/modules/hall-ui/public/profile.html +3 -3
  28. package/modules/hall-ui/public/project-settings.html +3 -3
  29. package/modules/hall-ui/public/ranks.html +3 -3
  30. package/modules/hall-ui/public/roadmap.html +3 -3
  31. package/modules/hall-ui/public/roster.html +3 -3
  32. package/modules/hall-ui/public/sessions.html +3 -3
  33. package/modules/hall-ui/public/settings.html +3 -3
  34. package/modules/hall-ui/public/shell.js +2 -0
  35. package/modules/hall-ui/public/street.html +3 -3
  36. package/modules/hall-ui/public/studio.html +3 -3
  37. package/modules/hall-ui/public/style.css +2 -0
  38. package/modules/hall-ui/public/task.html +3 -3
  39. package/modules/hall-ui/public/thinking.html +3 -3
  40. package/modules/hall-ui/public/tweak-editor.html +3 -3
  41. package/modules/hall-ui/public/watch.html +3 -3
  42. package/modules/hall-ui/public/work.html +3 -3
  43. package/modules/hall-ui/records/craft-mode.md +7 -0
  44. package/modules/public-landing/public/projects-hall-live.states.json +1 -41
  45. package/modules/public-landing/public/projects.html +109 -72
  46. package/modules/public-landing/public/projects.probes.json +5 -5
  47. package/modules/public-landing/public/projects.states.json +7 -7
  48. package/package-lock.json +2 -2
  49. package/package.json +1 -1
  50. package/release-notes.json +12 -0
  51. package/src/module-api.js +1 -1
  52. package/tests/craft_mode_nav.mjs +202 -0
  53. package/tests/hall_tweak_editor.mjs +1 -1
  54. package/tests/nav_permission_atoms.mjs +1 -1
  55. package/tests/projects_hub.mjs +7 -4
  56. package/tests/projects_hub_app_step.mjs +4 -3
  57. package/tests/projects_hub_module_picker.mjs +12 -10
  58. package/tests/projects_hub_pre_uat.mjs +1 -1
  59. package/tests/wizard_physics_more.mjs +58 -34
  60. package/tests/wizard_six_screens.mjs +10 -8
@@ -41,24 +41,24 @@
41
41
  "in-new-terms-refused": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["click","#termsAck"],["wait",200]],"expect":{"visible":["#panel-fork"],"hidden":["#panel-what"],"attr":[["#wizStart","disabled",""],["#wizDemo","disabled",""]]}},
42
42
  "in-new-fork-demo": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300]],"expect":{"visible":["#panel-fork","#demoSetup","#demoFitVerdict","#demoInvites","#wizBack"],"hidden":["#forkIntro"],"attr":[["#wizDemo","aria-expanded","true"]]}},
43
43
  "in-new-demo-what": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300],["fill","#demoIdea","A voting app for movie night"],["click","#demoStart"],["wait",400]],"expect":{"visible":["#panel-what","#demoTag"],"hidden":["#panel-fork"],"text":[["#wizEyebrow","New demo"]]}},
44
- "in-new-demo-review-address": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300],["fill","#demoIdea","A voting app for movie night"],["click","#demoStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-what button[type=submit]"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",300],["fill","#f-slug","mercury"],["wait",900]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"address\"] .opts","#f-slug","#addrConfirm","#f-noaddr"],"hidden":["#panel-checks"],"attr":[["#reviewGo","disabled",""]],"text":[["#wizEyebrow","New demo"]]}},
44
+ "in-new-demo-review-address": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizDemo"],["wait",300],["fill","#demoIdea","A voting app for movie night"],["click","#demoStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-what button[type=submit]"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",300],["fill","#f-slug","mercury"],["wait",900]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"address\"] .opts","#f-slug","#addrConfirm","#f-noaddr"],"hidden":["#panel-checks"],"text":[["#wizEyebrow","New demo"]]}},
45
45
  "in-new-what": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400]],"expect":{"visible":["#panel-what","#newName","#typeCards","#f-desc","#wizBack"],"hidden":["#panel-fork","#demoTag"],"attr":[["#wizRoom","data-screen","what"]]}},
46
46
  "in-new-name-prefilled": {"auth":true,"url":"/projects?view=new&name=Mercury","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400]],"expect":{"visible":["#panel-what","#newName"]}},
47
47
  "in-new-checks": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400]],"expect":{"visible":["#panel-checks","#physCountry","#physIntent"],"hidden":["#physAges","#physForms","#checksStop"],"attr":[["#wizRoom","data-screen","checks"]]}},
48
48
  "in-new-checks-filled": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["select","#physRegion","US-CA"],["click","[data-phys=\"builder_geography\"][data-val=\"us-eu\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"true\"]"],["wait",200]],"expect":{"visible":["#physRegionField","#physAges"]}},
49
49
  "in-new-checks-entity": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["click","#physHaveEntity"],["wait",200],["click","[data-phys=\"entity\"][data-val=\"for-profit\"]"],["wait",100]],"expect":{"visible":["#physForms","#physNoEntity"],"hidden":["#physIntent"]}},
50
- "in-new-review": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]","#reviewGo"],"hidden":["#panel-checks","#reviewRows .setRow[data-row=\"rewards\"]"],"attr":[["#reviewGo","disabled",""],["#wizRoom","data-screen","review"]]}},
50
+ "in-new-review": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700]],"expect":{"visible":["#panel-review","#reviewRows .setRow[data-row=\"code\"] [data-row-chg]","#reviewGo"],"hidden":["#panel-checks","#reviewRows .setRow[data-row=\"rewards\"]"],"text":[["#reviewGo","Looks right, continue"]],"attr":[["#wizRoom","data-screen","review"]]}},
51
51
  "in-new-review-code": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-chg]"],["wait",300],["click","#c-adopt"],["wait",400]],"expect":{"visible":["#reviewRows .setRow[data-row=\"code\"] .opts","#adoptNote","#ghAdopt"]}},
52
52
  "in-new-review-address": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",300],["fill","#f-slug","mercury"],["wait",900]],"expect":{"visible":["#f-slug","#addrConfirm","#f-noaddr"]}},
53
53
  "in-new-review-app": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-chg]"],["wait",300],["click","#appOptRender input"],["wait",300]],"expect":{"visible":["#appOpts","#renderLater"]}},
54
54
  "in-new-review-extras": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-chg]"],["wait",900]],"expect":{"visible":["#modQ","#modCards .tpill","#modCoreNote"]}},
55
55
  "in-new-review-team": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-chg]"],["wait",300],["click","[data-phys-solo=\"many\"]"],["wait",600]],"expect":{"visible":["#physTeam","#physGuessLine","#teamCards","#reviewRows .setRow[data-row=\"rewards\"]"]}},
56
56
  "in-new-review-run": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-chg]"],["wait",300],["click","[data-phys=\"framing\"][data-val=\"hybrid\"]"],["wait",600]],"expect":{"visible":["#physSuite"]}},
57
- "in-new-review-confirmed": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200]],"expect":{"visible":["#reviewGo","#reviewRows .setRow.done"],"text":[["#reviewGo","Continue"]]}},
58
- "in-new-look": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500]],"expect":{"visible":["#panel-look","#wizLook","#wizRecap","#doCreate"],"hidden":["#panel-review"],"attr":[["#wizRoom","data-screen","look"]]}},
59
- "in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#liveProgress .prog","#humanSteps li","#wizInviteLater"],"hidden":["#panel-look","#qWrap","#wizBack","#wizInvite"]}},
60
- "in-new-resume-offer": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]],"expect":{"visible":["#panel-fork","#wizResume","#wizResumeGo"],"hidden":["#panel-done"],"text":[["#wizResumeName","Mercury"]]}},
61
- "in-new-look-custom": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#wizLook-custom"],["wait",200]],"expect":{"visible":["#panel-look","#wizAccent","#wizAccentNote"],"hidden":["#panel-review"]}},
57
+ "in-new-review-confirmed": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200]],"expect":{"visible":["#reviewGo","#reviewRows .setRow.done"],"text":[["#reviewGo","Looks right, continue"]]}},
58
+ "in-new-look": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500]],"expect":{"visible":["#panel-look","#wizLook","#wizRecap","#doCreate"],"hidden":["#panel-review"],"attr":[["#wizRoom","data-screen","look"]]}},
59
+ "in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#liveProgress .prog","#humanSteps li","#wizInviteLater"],"hidden":["#panel-look","#qWrap","#wizBack","#wizInvite"]}},
60
+ "in-new-resume-offer": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]],"expect":{"visible":["#panel-fork","#wizResume","#wizResumeGo"],"hidden":["#panel-done"],"text":[["#wizResumeName","Mercury"]]}},
61
+ "in-new-look-custom": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#wizLook-custom"],["wait",200]],"expect":{"visible":["#panel-look","#wizAccent","#wizAccentNote"],"hidden":["#panel-review"]}},
62
62
  "out-new": {"auth":false,"url":"/projects?view=new","expect":{"visible":["#panel-gate","#wizSignin"],"hidden":["#qWrap","#panel-fork"]}},
63
63
  "out-map-projects": { "auth": false, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "hidden": ["#menuProjects [data-nav=\"mine\"]"] } },
64
64
  "card": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://example.com\"]"], ["wait", 900]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art"]], "visible": ["#cardArt", "#cardFaces", "#cardBuildersFig", "#cardShippedFig", "#cardOpen", "#cardHall"], "text": [["#cardBuilders", "11"], ["#cardShipped", "0"], ["#cardRel", "you build here"], ["#cardRuns", "example.com"], ["#cardFaces .facesMore", "+6"]], "count": [["#cardFaces img", 5]], "href": [["#cardHall", "https://builders.example.com/"]] } },
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.29",
3
+ "version": "1.21.31",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.29",
9
+ "version": "1.21.31",
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.21.29",
3
+ "version": "1.21.31",
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",
@@ -8930,5 +8930,17 @@
8930
8930
  "id": "1004548",
8931
8931
  "text": "After approving GitHub sign-in, founders go straight back to their setup checklist to wait, and can tick a box to have their project open automatically when it's ready."
8932
8932
  }
8933
+ ],
8934
+ "1.21.30": [
8935
+ {
8936
+ "id": "1004493",
8937
+ "text": "The hall's sidebar now follows the mode you pick. Building shows the everyday tabs, Artistry shows Studio, Goals and Your thinking, Ideating shows the Sky, Your thinking and Goals, and Governing is the shortest list: Blockers,"
8938
+ }
8939
+ ],
8940
+ "1.21.31": [
8941
+ {
8942
+ "id": "1004550",
8943
+ "text": "Setting up a new project is quicker: the final check screen now takes one click to accept, with less text and no per-row confirm buttons."
8944
+ }
8933
8945
  ]
8934
8946
  }
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.21.29'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.31'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -0,0 +1,202 @@
1
+ // tests/craft_mode_nav.mjs — the hall nav follows the craft mode (task 1004493,
2
+ // goal 1000095 WA6).
3
+ //
4
+ // Each craft mode shows only the tabs its craft needs; Legacy mode and no mode
5
+ // show every tab, exactly as before. The owner's rule (2026-10-01): some tabs
6
+ // stay in EVERY mode, Builders first among them, written down as ONE shared list
7
+ // that each mode's list adds to. These cases boot the REAL shell-trail.js +
8
+ // craft-mode.js + shell.js and read the sidebar the shell actually rendered, so a
9
+ // tab id that drifts from NAV, or a gate pass that runs after the mode and
10
+ // re-shows a tab, reds here.
11
+ import assert from 'node:assert/strict';
12
+ import { test } from 'node:test';
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import vm from 'node:vm';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
19
+ const HALL = path.join(ROOT, 'modules/hall-ui/public');
20
+ const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8');
21
+ const SHELL = read('shell.js');
22
+ const TRAIL = read('shell-trail.js');
23
+ const CRAFT = read('craft-mode.js');
24
+
25
+ // Every NAV id, and the Government group's, read from shell.js itself.
26
+ const NAV_IDS = [...SHELL.matchAll(/\{ id: '([a-z-]+)', label:/g)].map((m) => m[1]);
27
+ const GOV_BLOCK = SHELL.slice(SHELL.indexOf("{ group: 'Government'"));
28
+ const GOV_IDS = [...GOV_BLOCK.slice(0, GOV_BLOCK.indexOf('] }')).matchAll(/\{ id: '([a-z-]+)'/g)].map((m) => m[1]);
29
+
30
+ // A small element. Elements parsed from the sidebar's markup are built ONCE, so
31
+ // what the gate loop writes (hidden) is what the mode pass reads back.
32
+ function makeEl(tag) {
33
+ const attrs = {};
34
+ return {
35
+ tagName: tag, id: '', dataset: {}, style: {}, hidden: false, children: [], _html: '',
36
+ classList: { _s: new Set(), add(c) { this._s.add(c); }, remove(c) { this._s.delete(c); }, toggle(c, on) { if (on === undefined) on = !this._s.has(c); if (on) this._s.add(c); else this._s.delete(c); return on; }, contains(c) { return this._s.has(c); } },
37
+ setAttribute(k, v) { attrs[k] = String(v); }, getAttribute(k) { return k in attrs ? attrs[k] : null; },
38
+ hasAttribute(k) { return k in attrs; }, removeAttribute(k) { delete attrs[k]; },
39
+ addEventListener() {}, appendChild(c) { this.children.push(c); return c; }, removeChild() {}, insertBefore(c) { return c; },
40
+ focus() {}, closest() { return null; }, querySelector() { return null; }, querySelectorAll() { return []; },
41
+ };
42
+ }
43
+ // The rendered sidebar as groups of { head, items }.
44
+ function parseSidebar(html) {
45
+ return html.split('<div class="nav-group').slice(1).map((chunk) => {
46
+ const group = makeEl('DIV');
47
+ const tag = (m, t) => {
48
+ const el = makeEl(t);
49
+ for (const a of m.matchAll(/([a-z-]+)="([^"]*)"/g)) {
50
+ if (a[1] === 'id') el.id = a[2];
51
+ else if (a[1].startsWith('data-')) el.dataset[a[1].slice(5)] = a[2];
52
+ }
53
+ el.hidden = /\shidden[\s>]/.test(m);
54
+ return el;
55
+ };
56
+ const headM = chunk.match(/<button type="button" class="nav-group__head"[^>]*>/);
57
+ const head = headM ? tag(headM[0], 'BUTTON') : null;
58
+ const items = [...chunk.matchAll(/<a class="nav-item"[^>]*>/g)].map((m) => tag(m[0], 'A'));
59
+ group.querySelector = (sel) => (sel === '.nav-group__head' ? head : null);
60
+ group.querySelectorAll = (sel) => (sel === '.nav-item' ? items : []);
61
+ return { el: group, head, items };
62
+ });
63
+ }
64
+
65
+ function boot({ session = {}, perms = { 'board.vote.cast': true } } = {}) {
66
+ const root = makeEl('HTML');
67
+ const topbar = makeEl('DIV');
68
+ const sidebar = makeEl('NAV');
69
+ const body = makeEl('BODY');
70
+ body.dataset.page = 'work';
71
+ let groups = null;
72
+ const parsed = () => (groups ||= parseSidebar(sidebar._html));
73
+ Object.defineProperty(sidebar, 'innerHTML', { set(v) { sidebar._html = String(v); groups = null; }, get() { return sidebar._html; } });
74
+ sidebar.querySelectorAll = (sel) => {
75
+ if (sel === '.nav-item') return parsed().flatMap((g) => g.items);
76
+ if (sel === '.nav-group') return parsed().map((g) => g.el);
77
+ return [];
78
+ };
79
+ const gated = () => parsed().flatMap((g) => (g.head ? [g.head, ...g.items] : g.items));
80
+ const els = { 'app-topbar': topbar, 'app-sidebar': sidebar };
81
+ const documentObj = {
82
+ body, referrer: '', documentElement: root, activeElement: null,
83
+ getElementById: (id) => els[id] || null,
84
+ querySelector: () => null,
85
+ querySelectorAll: (sel) => (sel === '#app-sidebar [data-gate]' ? gated() : []),
86
+ createElement: (t) => makeEl(t.toUpperCase()),
87
+ addEventListener() {}, removeEventListener() {}, dispatchEvent() { return true; },
88
+ get cookie() { return ''; }, set cookie(_v) {},
89
+ };
90
+ const location = { hostname: 'h', origin: 'https://h', href: 'https://h/work', pathname: '/work', hash: '', search: '', assign() {}, replace() {}, reload() {} };
91
+ const mk = (obj) => ({ getItem: (k) => (k in obj ? obj[k] : null), setItem: (k, v) => { obj[k] = String(v); }, removeItem: (k) => { delete obj[k]; } });
92
+ const windowObj = {
93
+ __BRANDING__: {}, __MODULES__: { enabled: { provisioning: true } },
94
+ BongosClient: { createClient: () => ({ request: () => new Promise(() => {}) }) },
95
+ matchMedia: () => ({ matches: false, addEventListener() {} }),
96
+ localStorage: mk({}), sessionStorage: mk(session), location,
97
+ clearTimeout() {}, setTimeout() { return 0; }, requestAnimationFrame() {}, scrollTo() {},
98
+ addEventListener() {}, dispatchEvent() { return true; },
99
+ };
100
+ const sandbox = {
101
+ window: windowObj, document: documentObj, location, console, URL, URLSearchParams, queueMicrotask,
102
+ setTimeout() { return 0; }, clearTimeout() {},
103
+ Event: class { constructor(t) { this.type = t; } },
104
+ CustomEvent: class { constructor(t, i) { this.type = t; Object.assign(this, i); } },
105
+ };
106
+ sandbox.globalThis = sandbox;
107
+ vm.createContext(sandbox);
108
+ vm.runInContext(TRAIL, sandbox, { filename: 'shell-trail.js' });
109
+ vm.runInContext(CRAFT, sandbox, { filename: 'craft-mode.js' });
110
+ vm.runInContext(SHELL, sandbox, { filename: 'shell.js' });
111
+ const shell = windowObj.OTBShell;
112
+ const craft = windowObj.OTBCraftMode;
113
+ const byId = (id) => parsed().flatMap((g) => g.items).find((a) => a.id === 'nav-' + id);
114
+ return {
115
+ craft, root,
116
+ signIn: (rank = 'archon') => shell.applyAccess({ authed: true, rank, permissions: perms, builder: { id: 7, rank } }),
117
+ // The tabs a reader can see: the gate let them through AND the mode kept them.
118
+ showing: () => parsed().flatMap((g) => g.items).filter((a) => !a.hidden && !a.hasAttribute('data-craft-off')).map((a) => a.id.slice(4)),
119
+ headShowing: (group) => {
120
+ const g = parsed().find((x) => x.head && x.head.dataset.group === group);
121
+ return !!g && !g.head.hidden && !g.head.hasAttribute('data-craft-off');
122
+ },
123
+ byId,
124
+ };
125
+ }
126
+
127
+ const CRAFTS = ['building', 'artistry', 'ideating', 'governing'];
128
+
129
+ test('THE ALWAYS-PRESENT LIST: one shared list, Builders first, inside every mode', () => {
130
+ const { craft } = boot();
131
+ assert.equal(craft.ALWAYS_TABS[0], 'roster', 'Builders (NAV id roster) heads the list (owner, 2026-10-01)');
132
+ for (const m of CRAFTS) {
133
+ const tabs = craft.tabsFor(m);
134
+ assert.ok(Array.isArray(tabs), `${m} has a named tab list`);
135
+ for (const id of craft.ALWAYS_TABS) assert.ok(tabs.includes(id), `${m} keeps ${id}`);
136
+ }
137
+ assert.match(CRAFT, /return own \? ALWAYS_TABS\.concat\(own\) : null;/, 'each list is ALWAYS_TABS plus its own, not a copy of it');
138
+ });
139
+
140
+ test('every tab a mode names is a real NAV item, and every NAV item lives in some mode', () => {
141
+ const { craft } = boot();
142
+ assert.ok(NAV_IDS.length >= 25, `read ${NAV_IDS.length} NAV ids from shell.js`);
143
+ const named = new Set(CRAFTS.flatMap((m) => craft.tabsFor(m)));
144
+ for (const id of named) assert.ok(NAV_IDS.includes(id), `${id} is a NAV id`);
145
+ for (const id of NAV_IDS) assert.ok(named.has(id), `${id} is reachable from some mode's nav`);
146
+ });
147
+
148
+ test('Government shows only in Governing, and Governing is the shortest list', () => {
149
+ const { craft } = boot();
150
+ assert.ok(GOV_IDS.length >= 5, `read ${GOV_IDS.length} Government ids`);
151
+ for (const m of CRAFTS) {
152
+ for (const id of GOV_IDS) assert.equal(craft.tabsFor(m).includes(id), m === 'governing', `${id} in ${m}`);
153
+ }
154
+ const lengths = CRAFTS.map((m) => craft.tabsFor(m).filter((id) => !GOV_IDS.includes(id)).length);
155
+ assert.equal(Math.min(...lengths), lengths[CRAFTS.indexOf('governing')], 'outside its own Government group, Governing shows the fewest tabs');
156
+ });
157
+
158
+ test('NO MODE AND LEGACY ARE TODAY\'S NAV: every tab the gate allows, nothing marked', () => {
159
+ const none = boot();
160
+ none.signIn();
161
+ const all = none.showing();
162
+ assert.deepEqual(all, NAV_IDS, 'an archon with no mode sees every tab');
163
+ const legacy = boot({ session: { 'hall-craft': 'legacy' } });
164
+ legacy.signIn();
165
+ assert.deepEqual(legacy.showing(), all, 'Legacy mode shows exactly the same');
166
+ assert.equal(legacy.craft.tabsFor('legacy'), null);
167
+ });
168
+
169
+ test('each mode shows its own tabs, and switching updates the nav live', () => {
170
+ const p = boot();
171
+ p.signIn();
172
+ for (const m of CRAFTS) {
173
+ p.craft.choose(m);
174
+ assert.equal(p.root.dataset.craft, m);
175
+ assert.deepEqual(new Set(p.showing()), new Set(p.craft.tabsFor(m)), `${m} shows exactly its list`);
176
+ assert.ok(p.showing().includes('roster'), `Builders stays in ${m}`);
177
+ assert.equal(p.headShowing('government'), m === 'governing', `the Government head shows only where its tabs do (${m})`);
178
+ }
179
+ p.craft.choose('legacy');
180
+ assert.deepEqual(p.showing(), NAV_IDS, 'back to Legacy, every tab returns without a reload');
181
+ });
182
+
183
+ test('a mode never shows a tab the gate hid, and a later gate pass keeps the mode', () => {
184
+ const p = boot({ session: { 'hall-craft': 'governing' } });
185
+ p.signIn('thetes');
186
+ assert.ok(!p.showing().includes('watch'), 'a thetes in Governing does not get the metic-floored Watch tab');
187
+ assert.ok(!p.headShowing('government'), 'nor its group head');
188
+ assert.deepEqual(p.showing(), ['blockers', 'board-room', 'roster'], 'what a thetes board member in Governing sees');
189
+ p.signIn('archon');
190
+ assert.ok(p.showing().includes('watch'), 'promoted, the gate lets it through and the mode keeps it');
191
+ assert.ok(p.headShowing('government'), 'and its group head comes back with it, which only a mode pass after the gate can know');
192
+ assert.ok(!p.showing().includes('work'), 'and the gate pass did not bring back a tab the mode left out');
193
+ });
194
+
195
+ test('shell.js applies the mode AFTER its gate loop, and style.css hides what the mode leaves out', () => {
196
+ const body = SHELL.slice(SHELL.indexOf('function applyAccess('));
197
+ const gate = body.indexOf('el.hidden = !ok;');
198
+ const mode = body.indexOf('CRAFT.applyNav()');
199
+ assert.ok(gate > 0 && mode > gate, 'applyNav runs after the gate loop settles `hidden`');
200
+ assert.match(read('style.css'), /#app-sidebar \[data-craft-off\] \{ display: none !important; \}/);
201
+ assert.doesNotMatch(CRAFT, /\.hidden\s*=/, 'the mode never writes `hidden`, which the gate owns');
202
+ });
@@ -635,7 +635,7 @@ try {
635
635
  assert.match(HTML, /<body data-page="tweak-editor"[^>]*data-fullscreen-switch/);
636
636
  assert.equal((HTML.match(/<h1\b/g) || []).length, 1);
637
637
  assert.ok(HTML.indexOf('tweak-editor-lib.js') < HTML.indexOf('tweak-editor.js?'), 'the lib loads first');
638
- assert.match(HTML, /shell\.js\?v=2026-10-02-craft/);
638
+ assert.match(HTML, /shell\.js\?v=2026-10-03-nav/);
639
639
  assert.equal(/fonts\.googleapis\.com\/css/.test(HTML), false);
640
640
  for (const id of ['tw-bar', 'tw-counter', 'tw-open', 'tw-open-more', 'tw-divider', 'tw-openwrap', 'tw-open-menu', 'tw-open-tab', 'tw-open-split', 'tw-frame', 'tw-frame-view', 'tw-split-close', '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']) {
641
641
  assert.ok(new RegExp(`id="${id}"`).test(HTML), `#${id} is in the markup`);
@@ -87,7 +87,7 @@ test('the hall-preview fixture carries every atom /me resolves', () => {
87
87
  // the file and left the query alone, so a returning browser kept the cached copy —
88
88
  // a fix that reaches nobody looks identical to a fix that does not work.
89
89
 
90
- const SHELL_SHA = '8932abeda77f250a';
90
+ const SHELL_SHA = 'af1c61ca87767b44';
91
91
 
92
92
  test('shell.js has not changed without its cache-bust being bumped', () => {
93
93
  const actual = crypto.createHash('sha256').update(SHELL).digest('hex').slice(0, 16);
@@ -2316,8 +2316,11 @@ test('the extras picker ships as real markup on the review\'s Extras row and is
2316
2316
  assert.match(EXTRAS_ROW, /id="modCards"/, 'the band the picker paints its rows into');
2317
2317
  assert.match(EXTRAS_ROW, /class="typeCards"/, 'and it is the SAME band the type and team questions use — no new grammar');
2318
2318
  assert.match(EXTRAS_ROW, /id="modCoreNote"/, 'the always-on core is stated on the row');
2319
- // the suggested set is accepted by its own control — "Looks right" — never inferred
2320
- assert.match(HUB, /<div class="setRow" data-row="extras">[\s\S]*?<button type="button" class="rowOk" data-row-ok>Looks right<\/button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change<\/button>/,
2319
+ // the suggested set is accepted by a control — the page's "Looks right, continue", or
2320
+ // Done on the open row (task 1004550) — never inferred
2321
+ assert.match(HUB, /<div class="setRow" data-row="extras">[\s\S]*?<button type="button" class="rowChg" data-row-chg aria-expanded="false">Edit<\/button>/,
2322
+ 'the row can be opened and changed');
2323
+ assert.match(HUB, /<button type="submit" class="btn" id="reviewGo">Looks right, continue<\/button>/,
2321
2324
  'accepting the suggested set is a control, not an inference');
2322
2325
  // confirming turns the shown set into an answer given; the row's validity never refuses it
2323
2326
  const confirm = (SCRIPT.match(/function confirmRow\(key\) \{([\s\S]*?)\n \}/) || [, ''])[1];
@@ -2397,6 +2400,6 @@ test('the kit stub answers the picker\'s read, so the panel renders in the desig
2397
2400
  assert.match(stub, /rest === '\/provisioning\/starter-bundles'/, 'the stub serves the picker\'s read');
2398
2401
  const probes = JSON.parse(fs.readFileSync(path.join(LANDING, 'projects.probes.json'), 'utf8')).probes;
2399
2402
  assert.ok(probes.some((p) => /the wizard driver \(tasks? 1003346\b/.test(p.name)
2400
- && p.steps.some((st) => st[1] === '#reviewRows .setRow[data-row="extras"] [data-row-ok]')),
2401
- 'and the end-to-end driver confirms the Extras row rather than stopping at it (task 1004504)');
2403
+ && p.steps.some((st) => st[1] === '#reviewGo')),
2404
+ 'and the end-to-end driver confirms the review rather than stopping at it (tasks 1004504, 1004550)');
2402
2405
  });
@@ -1,7 +1,7 @@
1
1
  // tests/projects_hub_app_step.mjs — the create wizard's "where your app runs" question,
2
2
  // EXECUTED (task 1004351, ADR 0345), and its Render setup (task 1004352, ADR 0348). Since
3
3
  // task 1004504 the old App step is the review's "App hosting" row: its controls moved into
4
- // the row whole, and "Use this" runs the check its Next used to.
4
+ // the row whole, and Done (the row's Edit, open) runs the check its Next used to.
5
5
  //
6
6
  // A static regex cannot tell a guard that works from one that is never consulted, so —
7
7
  // as tests/projects_hub_module_picker.mjs does for the picker — this slices the block out
@@ -162,7 +162,7 @@ test('the row is real markup: Render and Decide later, Decide later the default'
162
162
  assert.doesNotMatch(PANEL, /Coming soon/, 'and is no longer marked soon');
163
163
  assert.match(PANEL, /<input type="radio" name="appPlatform" value="later" checked>/, 'Decide later is the default');
164
164
  assert.ok(PANEL.indexOf('value="later"') < PANEL.indexOf('value="render"'), 'Decide later comes first');
165
- assert.match(PANEL, /<button type="button" class="btn small rowUse" data-row-use>Use this<\/button>/, 'Use this confirms the row');
165
+ assert.match(HUB.slice(HUB.indexOf('<div class="setRow" data-row="app">')), /^[\s\S]*?<span class="acts"><button type="button" class="rowChg" data-row-chg aria-expanded="false">Edit<\/button>/, 'Edit opens the row, and reads Done to confirm it (task 1004550)');
166
166
  assert.doesNotMatch(HUB, /id="panel-app"/, 'the old App panel is gone');
167
167
  });
168
168
 
@@ -230,7 +230,8 @@ test('the hall is spelled one way on this page', () => {
230
230
  test('the key field says what the key is used for, and claims no protection it lacks', () => {
231
231
  const setup = (PANEL.match(/<div class="renderSetup"[\s\S]*?<div class="hint" id="h-render"/) || [''])[0];
232
232
  assert.match(setup, /<input type="password" id="f-renderKey" autocomplete="off"/, 'masked, and never autofilled');
233
- assert.match(setup, /used once, to create your app on your own Render account, and then thrown away/);
233
+ assert.match(setup, /Used once to create your app on your Render account, then thrown away — not kept, not even in this browser/);
234
+ assert.match(setup, /A key can act on your whole Render account/, 'what a key can reach is still said (task 1004550 shortened the rest)');
234
235
  assert.match(setup, /Render bills you directly/);
235
236
  assert.match(setup, /href="https:\/\/dashboard\.render\.com\/u\/settings#api-keys"/, 'links to where Render issues keys');
236
237
  assert.doesNotMatch(setup, /encrypt|secure|safe|protected/i, 'no protection claim the design does not give (ADR 0327 §2)');
@@ -1,8 +1,9 @@
1
1
  // tests/projects_hub_module_picker.mjs — the create wizard's module picker,
2
2
  // EXECUTED (BV1.R20, task 1002339; goal 1000046 criterion `proj-module-select`).
3
3
  // Since task 1004504 the picker is the review's Extras row: its controls moved into the
4
- // row whole, "Looks right" / "Use this" confirm the set on screen, and there is no Skip —
5
- // Create waits for every row, this one included.
4
+ // row whole, the page's one "Looks right, continue" (or Done on the open row) confirms
5
+ // the set on screen (task 1004550), and there is no Skip — Create waits for every row,
6
+ // this one included.
6
7
  //
7
8
  // tests/projects_hub.mjs pins this surface statically, on purpose (C7: "real
8
9
  // markup, not JS-built"). But the picker is the one part of the wizard whose
@@ -146,11 +147,11 @@ function makeEl(id) {
146
147
  };
147
148
  }
148
149
 
149
- // One review row, shaped like the markup: its hidden .opts, Looks right and Change.
150
+ // One review row, shaped like the markup: its hidden .opts and its one Edit / Done control.
150
151
  function makeRow(key) {
151
152
  const row = makeEl('row-' + key);
152
153
  row.setAttribute('data-row', key);
153
- const parts = { '.opts': makeEl('opts-' + key), '[data-row-ok]': makeEl('ok-' + key), '[data-row-chg]': makeEl('chg-' + key) };
154
+ const parts = { '.opts': makeEl('opts-' + key), '[data-row-chg]': makeEl('chg-' + key) };
154
155
  parts['.opts'].hidden = true;
155
156
  row.querySelector = (sel) => parts[sel] || null;
156
157
  return row;
@@ -220,7 +221,7 @@ function boot({ state: seed = {}, fetchOk = true, payload = PAYLOAD } = {}) {
220
221
  // a click inside the review, through its one delegated listener, as the browser sends it
221
222
  const rowClick = (attr) => {
222
223
  const row = rowFor('#reviewRows .setRow[data-row="extras"]');
223
- const t = { hasAttribute: (a) => a === attr, closest: (sel) => (sel === '.setRow' ? row : null) };
224
+ const t = { hasAttribute: (a) => a === attr, closest: (sel) => (sel === '.setRow' ? row : null), focus() {} };
224
225
  $('reviewRows')._handlers.click({ target: { closest: () => t } });
225
226
  };
226
227
  return {
@@ -246,10 +247,11 @@ function boot({ state: seed = {}, fetchOk = true, payload = PAYLOAD } = {}) {
246
247
  };
247
248
  band._handlers.click({ target });
248
249
  },
249
- // "Looks right" on the Extras row, "Change" to open it, "Use this" inside it
250
- looksRight: () => rowClick('data-row-ok'),
250
+ // the page's "Looks right, continue" (every row through its own check), Edit to
251
+ // open the Extras row, and Done — the same control, open — to confirm it
252
+ looksRight: () => sandbox.confirmAllRows(),
251
253
  change: () => rowClick('data-row-chg'),
252
- use: () => rowClick('data-row-use'),
254
+ use: () => rowClick('data-row-chg'),
253
255
  confirmed: () => !!(state.confirmed && state.confirmed.extras),
254
256
  unconfirmed: () => [...sandbox.unconfirmedRows()],
255
257
  forCreate: () => sandbox.modulesForCreate(),
@@ -355,7 +357,7 @@ test('an unconfirmed row answers NOTHING, and Create waits for it (no Skip since
355
357
  assert.ok(h.unconfirmed().includes('extras'), 'and the review holds Continue until the row is confirmed');
356
358
  });
357
359
 
358
- test('Change, a toggle, then Use this: the toggle stands as the answer', async () => {
360
+ test('Edit, a toggle, then Done: the toggle stands as the answer', async () => {
359
361
  // A visitor who changed something and then confirmed meant "this", not "throw that away".
360
362
  const h = boot({ state: { projType: 'game' } });
361
363
  await h.open();
@@ -368,7 +370,7 @@ test('Change, a toggle, then Use this: the toggle stands as the answer', async (
368
370
  assert.equal(h.confirmed(), true, 'and the row is confirmed');
369
371
  });
370
372
 
371
- test('Looks right CONFIRMS the set on screen — looking at it and accepting it is not the same as never being asked', async () => {
373
+ test('Looks right, continue CONFIRMS the set on screen — looking at it and accepting it is not the same as never being asked', async () => {
372
374
  const h = boot({ state: { projType: 'game' } });
373
375
  await h.open();
374
376
 
@@ -243,7 +243,7 @@ test('a resumed draft past the code row still probes repo access before Create',
243
243
  'every screen between the review and the done panel re-probes');
244
244
  });
245
245
 
246
- test('a half-typed custom domain says why Use this is dead; a taken handle silences the format cheer', () => {
246
+ test('a half-typed custom domain says why Done refuses it; a taken handle silences the format cheer', () => {
247
247
  assert.match(SCRIPT, /That doesn’t look like a domain yet \(e\.g\. moonbase\.example\.com\) — fix it, or clear the field to use the address above\./);
248
248
  assert.match(SCRIPT, /if \(avail === 'taken' \|\| avail === 'yours' \|\| avail === 'reserved'\) \{ var hs = \$\('h-slug'\); hs\.innerHTML = '&nbsp;'; hs\.className = 'hint'; \}/);
249
249
  });