@bongos/core 1.20.49 → 1.20.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +104 -79
- package/.claude/skills/new-project/SKILL.md +5 -0
- package/clients/bongos-client/index.d.ts +1 -1
- package/docs/api/openapi.json +3 -0
- package/docs/api-reference.md +1 -1
- package/docs/copy-inventory.md +367 -345
- package/docs/copy-registry.json +663 -463
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +10 -1
- package/docs/page-readings.json +986 -947
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-map.html +29 -14
- package/modules/hall-ui/public/goal-map.states.json +12 -0
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell.js +145 -15
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +61 -5
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.css +15 -11
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor.css +6 -0
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/hall-navigation.md +22 -0
- package/modules/hall-ui/records/sky.md +2 -0
- package/modules/platform-identity/routes/sso.js +5 -0
- package/modules/provisioning/migrations/provisioning_033_terms_ack.sql +33 -0
- package/modules/provisioning/onboard-plan.js +5 -0
- package/modules/provisioning/provisioning.js +7 -4
- package/modules/provisioning/routes/provisioning.js +3 -3
- package/modules/provisioning/terms-ack.js +36 -0
- package/modules/public-landing/public/projects.html +226 -77
- package/modules/public-landing/public/projects.probes.json +8 -2
- package/modules/public-landing/public/projects.states.json +16 -12
- package/modules/ui-design/kit/serve.js +6 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/goal_map_page.mjs +10 -7
- package/tests/hall_audit.mjs +3 -4
- package/tests/hall_fullscreen.mjs +99 -14
- package/tests/hall_tweak_editor.mjs +1 -1
- package/tests/nav_permission_atoms.mjs +1 -1
- package/tests/projects_hub.mjs +4 -2
- package/tests/projects_hub_app_status.mjs +1 -1
- package/tests/projects_hub_app_step.mjs +9 -9
- package/tests/projects_hub_module_picker.mjs +7 -7
- package/tests/projects_hub_pre_uat.mjs +4 -1
- package/tests/provisioning_terms_ack.mjs +216 -0
- package/tests/wizard_draft_resume.mjs +4 -4
- package/tests/wizard_front_door.mjs +271 -0
- package/tests/wizard_intent_resume.mjs +31 -19
|
@@ -159,10 +159,16 @@
|
|
|
159
159
|
"steps": [["wait",900],["click","[data-disc-ask]"],["wait",200],["fill","#mDiscName","old-lantern"],["wait",100],["click","[data-disc-yes]"],["wait",900]],
|
|
160
160
|
"expect": {"text":[["#mDisc .addr","This project is being disconnected."],["#mName","old-lantern"]],"hidden":["#mDiscName","[data-disc-ask]"]} },
|
|
161
161
|
{ "name": "the wizard driver (task 1003346): name, type, about, home (fresh; the stub grants repo access, so the new repository is named from the project), address (the handle's verdict), the review as a ledger, then the filing — the done panel with the standup rail and the human steps", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
162
|
-
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]],
|
|
162
|
+
"steps": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]],
|
|
163
163
|
"expect": {"visible":["#panel-done","#liveProgress .prog","#liveProgress .prog .stages li.now","#humanSteps li"],"hidden":["#panel-review","#panel-name"],"text":[["#done-h","The drums are rolling"]],"attr":[["#panel-done","class","panel on"]]} },
|
|
164
|
+
{ "name": "the front door (task 1004417): Proceed is refused until the terms are ticked, the fork's demo door says the demo is not open yet, and Begin planet creation goes on to the name step with the rail back", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
165
|
+
"steps": [["wait",600],["click","#panel-terms button[type=submit]"],["wait",300],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#wizDemo"],["wait",200],["click","#panel-fork button[type=submit]"],["wait",400]],
|
|
166
|
+
"expect": {"visible":["#panel-name","#view-new .steps","#view-new .steps li.now"],"hidden":["#panel-fork","#panel-terms"],"attr":[["#wizDemo","aria-expanded","true"],["#view-new .steps li.now","data-step","3"]]} },
|
|
167
|
+
{ "name": "the front door at 390: the fork's two doors stack inside one pill and the walk still reaches the name step", "auth": true, "width": 390, "url": "/projects?view=new",
|
|
168
|
+
"steps": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400]],
|
|
169
|
+
"expect": {"visible":["#panel-fork",".doorPill","#wizDemo","#panel-fork button[type=submit]"],"hidden":["#view-new .steps"]} },
|
|
164
170
|
{ "name": "the wizard driver at 390: the rail folds to two rows of three and the walk still reaches the review", "auth": true, "width": 390, "url": "/projects?view=new",
|
|
165
|
-
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400]],
|
|
171
|
+
"steps": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400]],
|
|
166
172
|
"expect": {"visible":["#panel-review","#reviewList li","#doCreate"],"hidden":["#panel-address"],"text":[["#rev-h","Ready to roll"]]} }
|
|
167
173
|
]
|
|
168
174
|
}
|
|
@@ -35,19 +35,23 @@
|
|
|
35
35
|
"in-manage-gone": { "auth": true, "url": "/projects?view=manage&id=104", "expect": { "visible": ["#mMain", "#mActs"], "hidden": ["#mErr", "#mAddress"], "text": [["#mName", "old-lantern"]] } },
|
|
36
36
|
"in-manage-missing": { "auth": true, "url": "/projects?view=manage&id=999", "expect": { "visible": ["#mMissing"], "hidden": ["#mMain", "#mGate"] } },
|
|
37
37
|
"out-manage": { "auth": false, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mGate"], "hidden": ["#mMain", "#mMissing"] } },
|
|
38
|
-
"in-new": { "auth": true, "url": "/projects?view=new", "expect": { "hidden": ["#qWrap"] } },
|
|
38
|
+
"in-new": { "auth": true, "url": "/projects?view=new", "expect": { "visible": ["#panel-terms", "#termsAck"], "hidden": ["#qWrap", "#view-new .steps", "#panel-name"] } },
|
|
39
|
+
"in-new-terms-refused": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#panel-terms button[type=submit]"],["wait",300]], "expect": {"visible":["#panel-terms","#h-terms"],"hidden":["#panel-fork"],"text":[["#h-terms","Tick the box to go on. It’s how your project records that you acknowledged the terms."]]} },
|
|
40
|
+
"in-new-fork": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-fork",".doorPill","#wizDemo",".forkStage",".forkRoutes"],"hidden":["#panel-terms","#view-new .steps","#demoSoon",".bongoCell"]} },
|
|
41
|
+
"in-new-fork-demo": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#wizDemo"],["wait",200]], "expect": {"visible":["#panel-fork","#demoSoon"],"attr":[["#wizDemo","aria-expanded","true"]]} },
|
|
42
|
+
"in-new-name": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-name","#newName","#view-new .steps"],"hidden":["#panel-fork","#panel-terms"]} },
|
|
39
43
|
"out-new": { "auth": false, "url": "/projects?view=new", "expect": { "hidden": ["#qWrap"] } },
|
|
40
|
-
"in-new-type": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-type"],"hidden":["#panel-name","#qWrap"]} },
|
|
41
|
-
"in-new-about": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-detail"],"hidden":["#panel-type","#qWrap"]} },
|
|
42
|
-
"in-new-modules": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900]], "expect": {"visible":["#panel-modules","#modQ","#modCards .tpill[aria-pressed=\"true\"]","#modCoreNote"],"hidden":["#panel-detail","#qWrap"]} },
|
|
43
|
-
"in-new-modules-search": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["fill","#modQ","discord"],["wait",200]], "expect": {"visible":["#panel-modules","#modQ"],"value":[["#modQ","discord"]],"count":[["#modCards .tpill",1],["#modCards .tpill[data-mod=\"discord\"]",1]],"text":[["#modFilterNote","Showing 1 of 3. 2 extras are switched on, including any this search is hiding."]]} },
|
|
44
|
-
"in-new-home": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-home"],"hidden":["#panel-detail","#qWrap"]} },
|
|
45
|
-
"in-new-address": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-address"],"hidden":["#panel-home","#qWrap"]} },
|
|
46
|
-
"in-new-app": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-app","#appOpts","#appOptRender"],"hidden":["#panel-address","#panel-review","#qWrap"]} },
|
|
47
|
-
"in-new-app-render": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#appOptRender input"],["wait",300]], "expect": {"visible":["#panel-app","#renderSetup","#f-renderKey","#renderCheck"],"hidden":["#renderWs","#renderSvc","#renderNew"]} },
|
|
48
|
-
"in-new-review": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-review","#reviewList li"],"hidden":["#panel-address","#qWrap"]} },
|
|
49
|
-
"in-new-done": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]], "expect": {"visible":["#panel-done","#liveProgress .prog","#humanSteps li"],"hidden":["#panel-review","#qWrap"]} },
|
|
50
|
-
"in-new-resume-offer": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]], "expect": {"visible":["#panel-
|
|
44
|
+
"in-new-type": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-type"],"hidden":["#panel-name","#qWrap"]} },
|
|
45
|
+
"in-new-about": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-detail"],"hidden":["#panel-type","#qWrap"]} },
|
|
46
|
+
"in-new-modules": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900]], "expect": {"visible":["#panel-modules","#modQ","#modCards .tpill[aria-pressed=\"true\"]","#modCoreNote"],"hidden":["#panel-detail","#qWrap"]} },
|
|
47
|
+
"in-new-modules-search": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["fill","#modQ","discord"],["wait",200]], "expect": {"visible":["#panel-modules","#modQ"],"value":[["#modQ","discord"]],"count":[["#modCards .tpill",1],["#modCards .tpill[data-mod=\"discord\"]",1]],"text":[["#modFilterNote","Showing 1 of 3. 2 extras are switched on, including any this search is hiding."]]} },
|
|
48
|
+
"in-new-home": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-home"],"hidden":["#panel-detail","#qWrap"]} },
|
|
49
|
+
"in-new-address": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-address"],"hidden":["#panel-home","#qWrap"]} },
|
|
50
|
+
"in-new-app": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-app","#appOpts","#appOptRender"],"hidden":["#panel-address","#panel-review","#qWrap"]} },
|
|
51
|
+
"in-new-app-render": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#appOptRender input"],["wait",300]], "expect": {"visible":["#panel-app","#renderSetup","#f-renderKey","#renderCheck"],"hidden":["#renderWs","#renderSvc","#renderNew"]} },
|
|
52
|
+
"in-new-review": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-review","#reviewList li"],"hidden":["#panel-address","#qWrap"]} },
|
|
53
|
+
"in-new-done": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]], "expect": {"visible":["#panel-done","#liveProgress .prog","#humanSteps li"],"hidden":["#panel-review","#qWrap"]} },
|
|
54
|
+
"in-new-resume-offer": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["click","#termsAck"],["click","#panel-terms button[type=submit]"],["wait",400],["click","#panel-fork button[type=submit]"],["wait",400],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#panel-app button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]], "expect": {"visible":["#panel-terms","#wizResume","#wizResumeGo"],"hidden":["#panel-done","#panel-name"],"text":[["#wizResumeName","Mercury"]]} },
|
|
51
55
|
"out-map-projects": { "auth": false, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "hidden": ["#menuProjects [data-nav=\"mine\"]"] } },
|
|
52
56
|
"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/"]] } },
|
|
53
57
|
"card-noart": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]"], ["wait", 600]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art", false]], "hidden": ["#cardArt", "#cardFaces", "#cardHall"], "visible": ["#cardBuildersFig", "#cardShippedFig", "#cardOpen"], "text": [["#cardShipped", "30+"], ["#cardRel", "you build here"]] } },
|
|
@@ -393,6 +393,12 @@ const server = http.createServer((req, res) => {
|
|
|
393
393
|
const all = JSON.parse(fs.readFileSync(path.join(FIXTURES, 'provisioning-instance-render.json'), 'utf8'));
|
|
394
394
|
return json(res, 200, all[appRead[1]] || { ok: true, app: null, env_vars: [], values_held: false });
|
|
395
395
|
}
|
|
396
|
+
// the done panel's invite step (R28, ADR 0251) asks who is worth inviting; the stub
|
|
397
|
+
// gives the route's own "nobody to look for" answer, so a render that lingers on the
|
|
398
|
+
// done panel pictures that branch instead of a 404 (found by task 1004417's render)
|
|
399
|
+
if (/^\/provisioning\/instances\/\d+\/invite-suggestions$/.test(rest) && req.method === 'GET') {
|
|
400
|
+
return AUTH ? json(res, 200, { suggested: [], seeking: null, reason: null }) : json(res, 401, UNAUTH);
|
|
401
|
+
}
|
|
396
402
|
const one = rest.match(/^\/provisioning\/instances\/(\d+)(\/settings)?$/);
|
|
397
403
|
if (one && req.method === 'GET') return AUTH ? instances(res, one[1], !!one[2]) : json(res, 401, UNAUTH);
|
|
398
404
|
// the hub's own cross-project surface (task 1003774): the requested/leave/
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.51",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.20.
|
|
9
|
+
"version": "1.20.51",
|
|
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.20.
|
|
3
|
+
"version": "1.20.51",
|
|
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",
|
package/release-notes.json
CHANGED
|
@@ -8407,5 +8407,17 @@
|
|
|
8407
8407
|
"id": "1004402",
|
|
8408
8408
|
"text": "A goal's page now shows where each checklist item really stands: Awaiting UAT, Satisfied, or Closed before UAT, with a Backend-only tag where there is nothing to record. The old Confirm button is gone. Instead, an item whose w"
|
|
8409
8409
|
}
|
|
8410
|
+
],
|
|
8411
|
+
"1.20.50": [
|
|
8412
|
+
{
|
|
8413
|
+
"id": "1004417",
|
|
8414
|
+
"text": "Starting a new project now begins with two decisions before any setup. First screen, \"anything you want to make\": the founder ticks that they can sign legally for the platform and acknowledge the terms, with a link to read the"
|
|
8415
|
+
}
|
|
8416
|
+
],
|
|
8417
|
+
"1.20.51": [
|
|
8418
|
+
{
|
|
8419
|
+
"id": "1004430",
|
|
8420
|
+
"text": "Hall navigation is more standard. The top bar now has a Back button when you open a page from inside another one. A builder's profile opened from the Builders list shows \"Back to Builders\" and returns you to the same spot in t"
|
|
8421
|
+
}
|
|
8410
8422
|
]
|
|
8411
8423
|
}
|
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.20.
|
|
78
|
+
const CORE_VERSION = '1.20.51'; // 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');
|
package/tests/goal_map_page.mjs
CHANGED
|
@@ -422,18 +422,21 @@ await test('Sky sits directly under Goals in the nav, member-gated, at the URL G
|
|
|
422
422
|
|
|
423
423
|
await test('the page is the sky\'s own surface — the same sheet and the same seven scripts as /thinking, one cache-bust between them, and the old goal map is gone', () => {
|
|
424
424
|
const html = read('goal-map.html'); const thinking = read('thinking.html');
|
|
425
|
-
assert.match(html, /<body data-page="goal-map" data-page-title="The sky" data-sky-lens="all">/);
|
|
425
|
+
assert.match(html, /<body data-page="goal-map" data-page-title="The sky" data-sky-lens="all" data-fullscreen-switch>/);
|
|
426
426
|
assert.match(html, /<title>The sky — \{\{worldName\}\}<\/title>/);
|
|
427
427
|
assert.match(html, /<h1>the sky <em id="lensWord">— the whole project<\/em><\/h1>/, 'the h1 is in static markup, so every state has one');
|
|
428
|
-
|
|
428
|
+
// IN THE HALL (task 1004430): the project sky joined /thinking in the chrome.
|
|
429
|
+
assert.match(html, /<script src="\/builders\/shell\.js\?v=/, 'the shell draws its sidebar and top bar');
|
|
430
|
+
assert.match(html, /<link rel="stylesheet" href="\/builders\/style\.css/, 'and the hall sheet styles them');
|
|
431
|
+
assert.match(html, /<main id="hall-main" class="sky-stage">/, 'the sky is drawn in the stage, the skip link\'s target');
|
|
429
432
|
const scripts = (h) => [...h.matchAll(/<script src="\/builders\/([^"?]+)\?v=([^"]+)"/g)].map((m) => [m[1], m[2]]);
|
|
430
433
|
// criterion-uat-ui.js (task 1004402): the rings' UAT state words, shared with the goal page.
|
|
431
|
-
assert.deepEqual(scripts(html).map((s) => s[0]), ['idea-objects.js', 'criterion-uat-ui.js', 'sky-data.js', 'sky-layout.js', 'sky-draw.js', 'sky-interaction.js', 'sky-panel.js', 'sky-composer.js', 'sky-tour.js']);
|
|
432
|
-
//
|
|
434
|
+
assert.deepEqual(scripts(html).filter((s) => s[0] !== 'shell.js').map((s) => s[0]), ['idea-objects.js', 'criterion-uat-ui.js', 'sky-data.js', 'sky-layout.js', 'sky-draw.js', 'sky-interaction.js', 'sky-panel.js', 'sky-composer.js', 'sky-tour.js']);
|
|
435
|
+
// Both doors load the shell (tasks 1004327, 1004430); the SKY's files are the same.
|
|
433
436
|
const skyScripts = (h) => scripts(h).filter((s) => s[0] !== 'shell.js');
|
|
434
437
|
assert.deepEqual(skyScripts(html), skyScripts(thinking), 'the two doors load the same sky files at the same stamp');
|
|
435
438
|
assert.equal(html.match(/thinking\.css\?v=([^"]+)/)[1], thinking.match(/thinking\.css\?v=([^"]+)/)[1]);
|
|
436
|
-
assert.match(read('thinking.css'), /body\[data-page
|
|
439
|
+
assert.match(read('thinking.css'), /body\[data-page\] \.sky-stage \{/, 'the sheet dresses the one stage both doors draw in inside the hall');
|
|
437
440
|
for (const f of ['goal-map.css', 'goal-map.js', 'goal-map-lib.js']) assert.equal(fs.existsSync(path.join(HALL, f)), false, `${f} is retired`);
|
|
438
441
|
assert.equal(fs.existsSync(path.join(ROOT, 'tests/goal_map_lib.mjs')), false, 'and so is the lib\'s test');
|
|
439
442
|
assert.equal(/goal-map-lib|goal-map\.js|goal-map\.css/.test(html), false);
|
|
@@ -457,11 +460,11 @@ await test('the ledger drawer accessible name matches its visible heading (docs/
|
|
|
457
460
|
// slide the sky again whatever overflows; and the hint now shrinks instead.
|
|
458
461
|
await test('the sky cannot be scrolled sideways: its box clips, it does not hide (task 1004342)', () => {
|
|
459
462
|
const css = read('thinking.css');
|
|
460
|
-
const rule = /body\[data-page
|
|
463
|
+
const rule = /body\[data-page\] \.sky-stage \{ margin: 0;[^}]*\}/.exec(css);
|
|
461
464
|
assert.ok(rule, 'the shared stage rule is still there');
|
|
462
465
|
// hidden first as the fallback for an engine without clip, then clip, which wins where supported
|
|
463
466
|
assert.match(rule[0], /overflow: hidden; overflow: clip;/, 'the stage clips (not scrollable), with hidden only as a fallback');
|
|
464
|
-
assert.match(css, /body\[data-page="thinking"\] \{ overflow: hidden; overflow: clip; \}/, 'and so does
|
|
467
|
+
assert.match(css, /body\[data-page="thinking"\], body\[data-page="goal-map"\] \{ overflow: hidden; overflow: clip; \}/, 'and so does each door\'s page body');
|
|
465
468
|
});
|
|
466
469
|
|
|
467
470
|
await test('the footer hint shrinks with an ellipsis instead of running past the stage (task 1004342)', () => {
|
package/tests/hall_audit.mjs
CHANGED
|
@@ -367,10 +367,9 @@ test('the module doc still records where the 2179 density pass landed', () => {
|
|
|
367
367
|
// tests/hall_sky.mjs against the template. It still stamps the branding token
|
|
368
368
|
// and the pack's font links like every hall page (the server injects them).
|
|
369
369
|
// /thinking left this list in task 1004327 (owner decision 2026-09-27): it sits
|
|
370
|
-
// in the hall chrome now, with the shell's full-screen switch to hide it.
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
};
|
|
370
|
+
// in the hall chrome now, with the shell's full-screen switch to hide it. The
|
|
371
|
+
// project sky (/goal-map) followed in task 1004430, so the list is empty again.
|
|
372
|
+
const STANDALONE_EXEMPT = {};
|
|
374
373
|
|
|
375
374
|
test('every hall page loads style.css and shell.js', () => {
|
|
376
375
|
const missing = [];
|
|
@@ -26,7 +26,12 @@ const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8');
|
|
|
26
26
|
|
|
27
27
|
// Boot the shell once — one page load. `store` is the browser's localStorage and
|
|
28
28
|
// is SHARED across boots on purpose: a second boot over the same store is a reload.
|
|
29
|
-
|
|
29
|
+
// `optIn`: true = the bare attribute (the page lays itself out for full screen),
|
|
30
|
+
// null = no attribute (a plain page — the switch is standard since task
|
|
31
|
+
// 1004430), 'off' = the page opts out. `referrer` / `search` / `session` /
|
|
32
|
+
// `recBack` drive the top bar's Back link (task 1004430).
|
|
33
|
+
function boot({ page = 'thinking', optIn = true, store = {}, storageThrows = false,
|
|
34
|
+
referrer = '', search = '', session = {}, recBack = false, path = null, meId = null } = {}) {
|
|
30
35
|
const els = new Map();
|
|
31
36
|
const windowListeners = {};
|
|
32
37
|
const timers = [];
|
|
@@ -49,25 +54,41 @@ function boot({ page = 'thinking', optIn = true, store = {}, storageThrows = fal
|
|
|
49
54
|
const topbar = makeEl('app-topbar');
|
|
50
55
|
const getElementById = (id) => {
|
|
51
56
|
if (id === 'app-topbar') return topbar;
|
|
52
|
-
if (/^(fullscreen-toggle|theme-toggle|zoom-toggle|topbar-menu|topbar-title|topbar-jump)$/.test(id)
|
|
57
|
+
if (/^(fullscreen-toggle|theme-toggle|zoom-toggle|topbar-menu|topbar-title|topbar-jump|topbar-back)$/.test(id)
|
|
53
58
|
&& !topbar._html.includes(`id="${id}"`)) return null;
|
|
54
59
|
if (!els.has(id)) els.set(id, makeEl(id));
|
|
55
60
|
return els.get(id);
|
|
56
61
|
};
|
|
62
|
+
// Two sidebar items, enough to see which one the shell marks active.
|
|
63
|
+
const navItems = ['/roster', '/profile'].map((href) => {
|
|
64
|
+
const cls = new Set();
|
|
65
|
+
return { href, dataset: {}, getAttribute: (k) => (k === 'href' ? href : null),
|
|
66
|
+
classList: { add: (c) => cls.add(c), remove: (c) => cls.delete(c), contains: (c) => cls.has(c) } };
|
|
67
|
+
});
|
|
68
|
+
const attr = optIn === true ? '' : optIn === false ? null : optIn;
|
|
57
69
|
const body = {
|
|
58
70
|
dataset: { page },
|
|
59
|
-
hasAttribute: (k) =>
|
|
71
|
+
hasAttribute: (k) => k === 'data-fullscreen-switch' && attr !== null,
|
|
72
|
+
getAttribute: (k) => (k === 'data-fullscreen-switch' ? attr : null),
|
|
73
|
+
appendChild() {},
|
|
60
74
|
};
|
|
61
75
|
const documentObj = {
|
|
62
76
|
body,
|
|
77
|
+
referrer,
|
|
63
78
|
documentElement: { dataset: {}, style: {}, classList: { add() {}, remove() {}, toggle() { return false; }, contains() { return false; } } },
|
|
64
79
|
getElementById,
|
|
65
|
-
|
|
80
|
+
querySelector: (sel) => (/\.rec-back/.test(sel) && recBack ? {} : null),
|
|
81
|
+
querySelectorAll: (sel) => (sel === '#app-sidebar .nav-item' ? navItems : []),
|
|
66
82
|
createElement() { return makeEl(null); },
|
|
67
83
|
addEventListener() {}, dispatchEvent() {},
|
|
68
84
|
get cookie() { return ''; }, set cookie(_v) {},
|
|
69
85
|
};
|
|
70
|
-
const location = { hostname: 'h', origin: 'https://h',
|
|
86
|
+
const location = { hostname: 'h', origin: 'https://h', href: 'https://h/' + page + search, pathname: path || '/' + page, hash: '', search, assign() {}, replace() {} };
|
|
87
|
+
const sessionStorage = {
|
|
88
|
+
getItem: (k) => (k in session ? session[k] : null),
|
|
89
|
+
setItem: (k, v) => { session[k] = String(v); },
|
|
90
|
+
removeItem: (k) => { delete session[k]; },
|
|
91
|
+
};
|
|
71
92
|
const localStorage = {
|
|
72
93
|
getItem: (k) => { if (storageThrows) throw new Error('SecurityError'); return k in store ? store[k] : null; },
|
|
73
94
|
setItem: (k, v) => { if (storageThrows) throw new Error('SecurityError'); store[k] = String(v); },
|
|
@@ -75,16 +96,18 @@ function boot({ page = 'thinking', optIn = true, store = {}, storageThrows = fal
|
|
|
75
96
|
let resizes = 0;
|
|
76
97
|
const windowObj = {
|
|
77
98
|
__BRANDING__: {}, __MODULES__: { enabled: {} },
|
|
78
|
-
|
|
99
|
+
// `meId` answers /me once with that builder; otherwise the read never settles.
|
|
100
|
+
BongosClient: { createClient: () => ({ request: () => (meId == null ? new Promise(() => {})
|
|
101
|
+
: Promise.resolve({ status: 200, ok: true, data: { builder: { id: meId, rank: 'thetes' } } })) }) },
|
|
79
102
|
matchMedia: () => ({ matches: false, addEventListener() {} }),
|
|
80
|
-
localStorage, location,
|
|
103
|
+
localStorage, sessionStorage, location, clearTimeout() {},
|
|
81
104
|
requestAnimationFrame() {}, scrollTo() {},
|
|
82
105
|
addEventListener(type, fn) { (windowListeners[type] ||= []).push(fn); },
|
|
83
106
|
dispatchEvent(ev) { if (ev && ev.type === 'resize') resizes += 1; return true; },
|
|
84
107
|
setTimeout: (fn) => { timers.push(fn); return timers.length; },
|
|
85
108
|
};
|
|
86
109
|
const sandbox = {
|
|
87
|
-
window: windowObj, document: documentObj, location, console, clearTimeout() {}, queueMicrotask,
|
|
110
|
+
window: windowObj, document: documentObj, location, console, clearTimeout() {}, queueMicrotask, URL, URLSearchParams,
|
|
88
111
|
setTimeout: windowObj.setTimeout,
|
|
89
112
|
Event: class { constructor(t) { this.type = t; } },
|
|
90
113
|
CustomEvent: class { constructor(t, i) { this.type = t; Object.assign(this, i); } },
|
|
@@ -97,6 +120,10 @@ function boot({ page = 'thinking', optIn = true, store = {}, storageThrows = fal
|
|
|
97
120
|
store,
|
|
98
121
|
button: () => getElementById('fullscreen-toggle'),
|
|
99
122
|
on: () => root.dataset.fullscreen === 'on',
|
|
123
|
+
plain: () => root.dataset.fullscreenPlain === 'on',
|
|
124
|
+
active: () => navItems.filter((a) => a.classList.contains('nav-item--active')).map((a) => a.href),
|
|
125
|
+
topbar: () => topbar._html,
|
|
126
|
+
session,
|
|
100
127
|
resizes: () => resizes,
|
|
101
128
|
// Press a key the way the browser dispatches it: every window listener,
|
|
102
129
|
// then the page may mark the press used, then the queued timers run.
|
|
@@ -109,13 +136,71 @@ function boot({ page = 'thinking', optIn = true, store = {}, storageThrows = fal
|
|
|
109
136
|
};
|
|
110
137
|
}
|
|
111
138
|
|
|
112
|
-
test('a page that
|
|
139
|
+
test('a page that opts OUT gets no switch, and never goes full screen', () => {
|
|
113
140
|
const store = { 'hall-fullscreen:work': 'on' };
|
|
114
|
-
const p = boot({ page: 'work', optIn:
|
|
141
|
+
const p = boot({ page: 'work', optIn: 'off', store });
|
|
115
142
|
assert.equal(p.button(), null, 'no button rendered');
|
|
116
143
|
assert.equal(p.on(), false, 'a stored value for a page with no switch is ignored');
|
|
117
144
|
});
|
|
118
145
|
|
|
146
|
+
test('THE SWITCH IS STANDARD (task 1004430): a page with no attribute gets it too, marked plain', () => {
|
|
147
|
+
const p = boot({ page: 'goals', optIn: false });
|
|
148
|
+
assert.ok(p.button(), 'goals carries the switch without opting in');
|
|
149
|
+
p.button().click();
|
|
150
|
+
assert.equal(p.on(), true);
|
|
151
|
+
assert.equal(p.plain(), true, 'a plain page: style.css steps its content clear of the corner button');
|
|
152
|
+
p.button().click();
|
|
153
|
+
assert.equal(p.plain(), false, 'and the mark leaves with full screen');
|
|
154
|
+
const sky = boot({ page: 'thinking' });
|
|
155
|
+
sky.button().click();
|
|
156
|
+
assert.equal(sky.plain(), false, 'a page that lays itself out for full screen is not plain');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// THE BACK LINK (task 1004430). The owner's case: a profile opened from the
|
|
160
|
+
// Builders list shows "← Builders", and a sidebar hop shows nothing.
|
|
161
|
+
test('a page reached from inside another hall page shows Back to it, by its nav label', () => {
|
|
162
|
+
const p = boot({ page: 'profile', optIn: false, search: '?id=7', referrer: 'https://h/roster' });
|
|
163
|
+
assert.match(p.topbar(), /<a class="topbar-back" id="topbar-back" href="\/roster"/);
|
|
164
|
+
assert.match(p.topbar(), />Builders<\/span><\/a>/);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('Back: ?from= wins over the referrer, and a record origin is named as the record', () => {
|
|
168
|
+
const p = boot({ page: 'profile', optIn: false, search: '?id=7&from=%2Ftask%2F42', referrer: 'https://h/roster' });
|
|
169
|
+
assert.match(p.topbar(), /href="\/task\/42"/);
|
|
170
|
+
assert.match(p.topbar(), />task 42<\/span>/);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('Back: none after a sidebar hop, from another site, from the page itself, or where the page draws its own', () => {
|
|
174
|
+
const hop = boot({ page: 'goals', optIn: false, referrer: 'https://h/work', session: { 'hall-nav-hop': '/goals' } });
|
|
175
|
+
assert.doesNotMatch(hop.topbar(), /topbar-back/, 'the sidebar is not a drill-down');
|
|
176
|
+
assert.equal(hop.session['hall-nav-hop'], undefined, 'the note is one-shot');
|
|
177
|
+
assert.doesNotMatch(boot({ page: 'goals', optIn: false, referrer: 'https://elsewhere.example/roster' }).topbar(), /topbar-back/);
|
|
178
|
+
assert.doesNotMatch(boot({ page: 'goals', optIn: false, referrer: 'https://h/goals' }).topbar(), /topbar-back/);
|
|
179
|
+
assert.doesNotMatch(boot({ page: 'task', optIn: false, path: '/task/9', referrer: 'https://h/goals', recBack: true }).topbar(), /topbar-back/,
|
|
180
|
+
'the task record keeps its own back link, and the bar adds no second one');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('someone else\'s profile highlights Builders; your own hands the mark back to Profile', async () => {
|
|
184
|
+
const other = boot({ page: 'profile', optIn: false, search: '?id=7', meId: 3 });
|
|
185
|
+
assert.deepEqual(other.active(), ['/roster'], 'at boot, before /me answers');
|
|
186
|
+
await new Promise((r) => setImmediate(r));
|
|
187
|
+
assert.deepEqual(other.active(), ['/roster'], 'id 7 is not the viewer (3)');
|
|
188
|
+
const own = boot({ page: 'profile', optIn: false, search: '?id=3', meId: 3 });
|
|
189
|
+
await new Promise((r) => setImmediate(r));
|
|
190
|
+
assert.deepEqual(own.active(), ['/profile'], 'the viewer\'s own id is their Profile');
|
|
191
|
+
assert.deepEqual(boot({ page: 'profile', optIn: false }).active(), ['/profile'], 'no id: your own profile');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('Back: a page\'s own back link (data-own-back, the tweak editor\'s "← studio") suppresses the bar\'s', () => {
|
|
195
|
+
assert.match(read('tweak-editor.html'), /<a class="tw-back" data-own-back href="\/builders\/studio">/);
|
|
196
|
+
assert.match(SHELL, /querySelector\('\.rec-back, \[data-own-back\]'\)/);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('Back: an origin\'s own ?from= is dropped, so one page\'s way back never chains into the next', () => {
|
|
200
|
+
const p = boot({ page: 'profile', optIn: false, search: '?id=7', referrer: 'https://h/roster?q=a&from=%2Fwork' });
|
|
201
|
+
assert.match(p.topbar(), /href="\/roster\?q=a"/);
|
|
202
|
+
});
|
|
203
|
+
|
|
119
204
|
test('an opted-in page renders ONE switch in the top bar, off by default', () => {
|
|
120
205
|
const p = boot();
|
|
121
206
|
const btn = p.button();
|
|
@@ -130,7 +215,8 @@ test('the switch hides the chrome, the same button brings it back', () => {
|
|
|
130
215
|
p.button().click();
|
|
131
216
|
assert.equal(p.on(), true, 'html[data-fullscreen="on"]');
|
|
132
217
|
assert.equal(p.button().getAttribute('aria-pressed'), 'true');
|
|
133
|
-
assert.equal(p.button().getAttribute('aria-label'), '
|
|
218
|
+
assert.equal(p.button().getAttribute('aria-label'), 'Exit full screen');
|
|
219
|
+
assert.match(p.button().innerHTML, />Exit full screen<\/span>/, 'a labelled pill, not a bare glyph (task 1004430)');
|
|
134
220
|
assert.ok(p.resizes() >= 1, 'a page that sizes to its box is told to re-measure');
|
|
135
221
|
p.button().click();
|
|
136
222
|
assert.equal(p.on(), false, 'and back');
|
|
@@ -174,15 +260,14 @@ test('storage that throws (a private window) leaves the chrome showing and the s
|
|
|
174
260
|
assert.equal(p.on(), true, 'the switch still works for this page load');
|
|
175
261
|
});
|
|
176
262
|
|
|
177
|
-
test('the studio
|
|
178
|
-
for (const f of ['studio.html', 'thinking.html']) {
|
|
263
|
+
test('the studio, Your thinking and the project sky lay themselves out for it, inside the hall chrome', () => {
|
|
264
|
+
for (const f of ['studio.html', 'thinking.html', 'goal-map.html']) {
|
|
179
265
|
const html = read(f);
|
|
180
266
|
assert.match(html, /<body[^>]*\bdata-fullscreen-switch\b/, `${f} opts in`);
|
|
181
267
|
for (const id of ['app-sidebar', 'app-topbar']) assert.match(html, new RegExp(`id="${id}"`), `${f} carries #${id}`);
|
|
182
268
|
assert.match(html, /<main id="hall-main"/, `${f}: the skip link's target`);
|
|
183
269
|
assert.equal((html.replace(/<!--[\s\S]*?-->/g, '').match(/<header\b/g) || []).length, 1, `${f}: the top bar is the one <header>`);
|
|
184
270
|
}
|
|
185
|
-
assert.doesNotMatch(read('goal-map.html'), /data-fullscreen-switch|shell\.js/, '/goal-map keeps its standalone form');
|
|
186
271
|
});
|
|
187
272
|
|
|
188
273
|
// SOURCE ASSERTION (the CSS half): full screen hides the sidebar and the foot
|
|
@@ -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-09-30-
|
|
638
|
+
assert.match(HTML, /shell\.js\?v=2026-09-30-navback2/);
|
|
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 = '
|
|
90
|
+
const SHELL_SHA = 'c05e9d87594aca3c';
|
|
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);
|
package/tests/projects_hub.mjs
CHANGED
|
@@ -714,7 +714,9 @@ test('a renumbering of the wizard invalidates old drafts — the key carries the
|
|
|
714
714
|
assert.match(key, /-v\d+$/, 'the draft key is versioned');
|
|
715
715
|
const panels = (SCRIPT.match(/var panels = \{([^}]*)\}/) || [, ''])[1];
|
|
716
716
|
const count = [...panels.matchAll(/\d+:\s*'panel-/g)].length;
|
|
717
|
-
|
|
717
|
+
// task 1004417 put the front door's two screens (terms, fork) in front: the sixth renumbering
|
|
718
|
+
assert.equal(count, 12, 'gate + the front door\'s two + eight numbered steps + done — bump DRAFT_KEY when this changes');
|
|
719
|
+
assert.equal(key, 'cb-create-draft-v6', 'and the key moved with the front door');
|
|
718
720
|
});
|
|
719
721
|
|
|
720
722
|
test('every row lays the same five parts — the ledger has no ragged row', () => {
|
|
@@ -2182,7 +2184,7 @@ test('the wizard\'s own structures wear the world: hairline fields, cells of one
|
|
|
2182
2184
|
assert.match(block, /\.addrBig code\{display:block;font-family:var\(--font-display\);/, 'the address stands in the display voice');
|
|
2183
2185
|
assert.doesNotMatch(block, /linear-gradient/, 'no tinted plates');
|
|
2184
2186
|
assert.doesNotMatch(block, /box-shadow:(?!none)/, 'no box shadow in the wizard block');
|
|
2185
|
-
assert.match(HUB, /<div class="nav">\s*<button type="button" class="btn ghost" data-back="
|
|
2187
|
+
assert.match(HUB, /<div class="nav">\s*<button type="button" class="btn ghost" data-back="9">← Back<\/button>\s*<button type="button" class="btn big" id="doCreate">Create my project<\/button>/, 'the review\'s doors sit on one row, back then the primary');
|
|
2186
2188
|
assert.match(HUB, /<h2 class="stepsHead" id="doneStepsHead">Your steps — the parts only you can play<\/h2>/, 'the steps heading is the label face by class, not an inline style (the id is where a skipped invite step hands focus on, task 1002346)');
|
|
2187
2189
|
// what is enqueued is unchanged (ADR 0111): the filing body the control plane reads
|
|
2188
2190
|
assert.match(SCRIPT, /body: JSON\.stringify\(\{\s*slug: state\.slug,/, 'the filing still carries the slug first');
|
|
@@ -240,7 +240,7 @@ test('a repaint never lands on top of an open Retry — from this watch or a fre
|
|
|
240
240
|
});
|
|
241
241
|
|
|
242
242
|
test('both surfaces use it: the done panel and the project page', () => {
|
|
243
|
-
assert.match(HUB, /if \(box\) watchApp\(instanceId, box, function \(\) \{ return state\.step ===
|
|
243
|
+
assert.match(HUB, /if \(box\) watchApp\(instanceId, box, function \(\) \{ return state\.step === 11; \}, 5000\);/);
|
|
244
244
|
assert.match(HUB, /<div id="doneApp" hidden><\/div>/);
|
|
245
245
|
assert.match(HUB, /<div class="card mSec" id="mAppCard" hidden>\n\s*<h2>Where its app runs<\/h2>\n\s*<div id="mApp"><\/div>/);
|
|
246
246
|
assert.match(HUB, /loadAppStatus\(inst\.id \|\| asked, inst\);/);
|
|
@@ -142,17 +142,17 @@ test('the panel is real markup: Render and Decide later, Decide later the defaul
|
|
|
142
142
|
assert.doesNotMatch(PANEL, /Coming soon/, 'and is no longer marked soon');
|
|
143
143
|
assert.match(PANEL, /<input type="radio" name="appPlatform" value="later" checked>/, 'Decide later is the default');
|
|
144
144
|
assert.match(PANEL, /We host your Builders' hall/, 'the step says the hall is ours to host (ADR 0345 D1), in the page\'s one spelling of it');
|
|
145
|
-
assert.match(PANEL, /<form data-next="
|
|
146
|
-
assert.match(PANEL, /data-back="
|
|
145
|
+
assert.match(PANEL, /<form data-next="10">/, 'Next leads to the review');
|
|
146
|
+
assert.match(PANEL, /data-back="8"/, 'Back returns to the address step');
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
test('the step sits between Address and Review in the panels map and on the rail', () => {
|
|
150
|
-
assert.match(HUB, /
|
|
151
|
-
assert.match(HUB, /<li data-step="
|
|
152
|
-
assert.match(HUB, /<li data-step="
|
|
153
|
-
assert.match(HUB, /data-back="
|
|
154
|
-
assert.match(HUB, /if \(step ===
|
|
155
|
-
assert.match(HUB, /if \(step ===
|
|
150
|
+
assert.match(HUB, /8: 'panel-address', 9: 'panel-app', 10: 'panel-review', 11: 'panel-done'/);
|
|
151
|
+
assert.match(HUB, /<li data-step="9"><span class="snum">7<\/span><span class="slbl">App<\/span><\/li>/);
|
|
152
|
+
assert.match(HUB, /<li data-step="10"><span class="snum">8<\/span><span class="slbl">Create<\/span><\/li>/);
|
|
153
|
+
assert.match(HUB, /data-back="9">← Back<\/button>\n\s*<button type="button" class="btn big" id="doCreate">/, "the review's Back returns to the app step");
|
|
154
|
+
assert.match(HUB, /if \(step === 9\) \{ syncAppStep\(\); syncRenderSetup\(\); \}\n\s*if \(step === 10\) renderReview\(\);/, 'entering the step repaints it, Render setup included');
|
|
155
|
+
assert.match(HUB, /if \(step === 9\) return validateRender\(\);/, 'Next on the step runs the Render check');
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
test('a fresh draft reads "decide later", and the Render setup stays hidden', () => {
|
|
@@ -455,5 +455,5 @@ test('the review names exactly what was chosen, prices included', () => {
|
|
|
455
455
|
});
|
|
456
456
|
|
|
457
457
|
test('Create hands the app to Render straight after the project is filed', () => {
|
|
458
|
-
assert.match(HUB, /show\(
|
|
458
|
+
assert.match(HUB, /show\(11, true\);\n\s*sendRenderSetup\(data\.instance && data\.instance\.id\);/);
|
|
459
459
|
});
|