@bongos/core 1.24.79 → 1.24.80

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 (366) hide show
  1. package/.bongos-core.json +495 -350
  2. package/.claude/skills/ask-for-help/SKILL.md +1 -1
  3. package/.claude/skills/builder-sequence/SKILL.md +1 -1
  4. package/.claude/skills/design/SKILL.md +1 -1
  5. package/.claude/skills/goal-create/SKILL.md +1 -1
  6. package/.claude/skills/planning-session/SKILL.md +1 -1
  7. package/clients/bongos-client/README.md +1 -1
  8. package/clients/bongos-client/bongos-client.global.js +8 -0
  9. package/clients/bongos-client/index.cjs +8 -0
  10. package/clients/bongos-client/index.d.ts +11 -0
  11. package/clients/bongos-client/index.mjs +8 -0
  12. package/docs/adr/0274-one-kernel-three-role-packs.md +2 -2
  13. package/docs/adr/0399-the-visor-is-glass-over-the-scene-and-owns-no-camera.md +91 -0
  14. package/docs/adr/0400-a-chain-reserves-work-and-each-sub-quest-is-still-claimed-alone.md +109 -0
  15. package/docs/adr/0401-one-options-menu-opens-every-settings-screen-in-a-frame.md +53 -0
  16. package/docs/adr/README.md +3 -0
  17. package/docs/api/openapi.json +195 -3
  18. package/docs/api-reference.md +8 -4
  19. package/docs/architecture.md +7 -3
  20. package/docs/copy-inventory.md +390 -370
  21. package/docs/copy-registry.json +719 -518
  22. package/docs/design/characters/character-sprites-source.webp +0 -0
  23. package/docs/file-map.md +7 -8
  24. package/docs/module-api-changelog.md +2 -0
  25. package/docs/modules-contract.md +5 -0
  26. package/docs/onboarding/slash-commands.md +1 -1
  27. package/docs/packs/{engineer.md → artisan.md} +5 -5
  28. package/docs/packs/artist.md +50 -4
  29. package/docs/page-inventory.json +15 -4
  30. package/docs/page-readings.json +3182 -3336
  31. package/docs/{packs/ideator.md → recipes/ideation-playbook.md} +5 -48
  32. package/migrations/core_296_crafts_artisan_artist.sql +72 -0
  33. package/migrations/core_297_builder_character.sql +30 -0
  34. package/modules/copy-desk/proposals.js +1 -1
  35. package/modules/economy/credits-by-craft.js +29 -15
  36. package/modules/hall-ui/public/ai-studio.html +4 -4
  37. package/modules/hall-ui/public/atlas.html +5 -5
  38. package/modules/hall-ui/public/atlas.js +3 -1
  39. package/modules/hall-ui/public/blockers.html +5 -5
  40. package/modules/hall-ui/public/board-room.html +4 -4
  41. package/modules/hall-ui/public/characters/artisan.png +0 -0
  42. package/modules/hall-ui/public/characters/artist.png +0 -0
  43. package/modules/hall-ui/public/characters/patron.png +0 -0
  44. package/modules/hall-ui/public/city.css +2 -1
  45. package/modules/hall-ui/public/city.html +9 -6
  46. package/modules/hall-ui/public/city.js +7 -1
  47. package/modules/hall-ui/public/collab.html +4 -4
  48. package/modules/hall-ui/public/commands.html +4 -4
  49. package/modules/hall-ui/public/copy-desk.html +4 -4
  50. package/modules/hall-ui/public/craft-mode.js +258 -64
  51. package/modules/hall-ui/public/deploy.html +4 -4
  52. package/modules/hall-ui/public/diagram-viewer.js +2 -1
  53. package/modules/hall-ui/public/dom-utils.js +1 -1
  54. package/modules/hall-ui/public/goal-map.html +9 -6
  55. package/modules/hall-ui/public/goals.html +5 -5
  56. package/modules/hall-ui/public/government.html +4 -4
  57. package/modules/hall-ui/public/hall-render.js +3 -1
  58. package/modules/hall-ui/public/home-city-draw.js +3 -3
  59. package/modules/hall-ui/public/home-city.css +4 -0
  60. package/modules/hall-ui/public/home-city.js +17 -9
  61. package/modules/hall-ui/public/home-gallery.js +8 -1
  62. package/modules/hall-ui/public/home-idea-box.css +2 -2
  63. package/modules/hall-ui/public/home-idea-box.js +3 -3
  64. package/modules/hall-ui/public/idea.html +5 -5
  65. package/modules/hall-ui/public/ideas.html +5 -5
  66. package/modules/hall-ui/public/index.html +11 -7
  67. package/modules/hall-ui/public/market-watch-item.html +4 -4
  68. package/modules/hall-ui/public/market-watch-rules.html +4 -4
  69. package/modules/hall-ui/public/market-watch.html +6 -9
  70. package/modules/hall-ui/public/market-watch.js +22 -45
  71. package/modules/hall-ui/public/modules.html +4 -4
  72. package/modules/hall-ui/public/money.html +4 -4
  73. package/modules/hall-ui/public/my-agents.html +4 -4
  74. package/modules/hall-ui/public/options-menu.js +409 -0
  75. package/modules/hall-ui/public/paintbrush.js +3 -3
  76. package/modules/hall-ui/public/palette.js +11 -13
  77. package/modules/hall-ui/public/primer.html +5 -5
  78. package/modules/hall-ui/public/profile-crafts.js +1 -1
  79. package/modules/hall-ui/public/profile-roles.js +23 -19
  80. package/modules/hall-ui/public/profile.css +1 -1
  81. package/modules/hall-ui/public/profile.html +19 -18
  82. package/modules/hall-ui/public/profile.js +19 -18
  83. package/modules/hall-ui/public/profile.states.json +4 -4
  84. package/modules/hall-ui/public/project-settings.html +4 -4
  85. package/modules/hall-ui/public/roster.html +4 -4
  86. package/modules/hall-ui/public/roster.js +3 -3
  87. package/modules/hall-ui/public/settings.css +24 -4
  88. package/modules/hall-ui/public/settings.html +21 -17
  89. package/modules/hall-ui/public/settings.js +44 -44
  90. package/modules/hall-ui/public/shell-game.css +65 -0
  91. package/modules/hall-ui/public/shell-game.js +283 -3
  92. package/modules/hall-ui/public/shell.js +7 -26
  93. package/modules/hall-ui/public/sky-interaction.js +7 -0
  94. package/modules/hall-ui/public/speciality-tools.js +1 -2
  95. package/modules/hall-ui/public/street-inside.js +3 -4
  96. package/modules/hall-ui/public/street.html +4 -4
  97. package/modules/hall-ui/public/studio.html +5 -5
  98. package/modules/hall-ui/public/style.css +73 -47
  99. package/modules/hall-ui/public/task-detail.js +5 -4
  100. package/modules/hall-ui/public/task.html +5 -5
  101. package/modules/hall-ui/public/thinking.css +10 -0
  102. package/modules/hall-ui/public/thinking.html +9 -6
  103. package/modules/hall-ui/public/tweak-editor.html +5 -5
  104. package/modules/hall-ui/public/tweak-editor.js +5 -1
  105. package/modules/hall-ui/public/update-pill.js +91 -2
  106. package/modules/hall-ui/public/visor.css +80 -0
  107. package/modules/hall-ui/public/visor.js +316 -0
  108. package/modules/hall-ui/public/watch-repo-invite.js +159 -0
  109. package/modules/hall-ui/public/watch.html +6 -5
  110. package/modules/hall-ui/public/watch.js +17 -2
  111. package/modules/hall-ui/public/welcome-boot.js +15 -6
  112. package/modules/hall-ui/public/work.html +5 -5
  113. package/modules/hall-ui/records/builders-roster.md +2 -2
  114. package/modules/hall-ui/records/craft-mode.md +13 -11
  115. package/modules/hall-ui/records/game-mode.md +2 -2
  116. package/modules/hall-ui/records/governor-street.md +1 -1
  117. package/modules/hall-ui/records/home-city.md +3 -1
  118. package/modules/hall-ui/records/market-watch-settings.md +2 -0
  119. package/modules/hall-ui/records/options-menu.md +15 -0
  120. package/modules/hall-ui/records/profile-roles.md +11 -11
  121. package/modules/hall-ui/records/sky.md +3 -3
  122. package/modules/hall-ui/records/start-screen.md +21 -0
  123. package/modules/hall-ui/records/tweak-editor.md +1 -1
  124. package/modules/hall-ui/records/update-pill.md +12 -0
  125. package/modules/hall-ui/records/visor.md +23 -0
  126. package/modules/hall-ui/records/welcome-tower.md +5 -5
  127. package/modules/ideas/ideator-stats.js +3 -1
  128. package/modules/ideas/inbox.js +2 -2
  129. package/modules/lifecycle/activity.js +1 -1
  130. package/modules/lifecycle/cascade.js +4 -4
  131. package/modules/lifecycle/goal-spec-task.js +1 -1
  132. package/modules/lifecycle/help-requests.js +19 -15
  133. package/modules/lifecycle/kickoff-checklist.js +3 -3
  134. package/modules/lifecycle/kickoff-seed.js +1 -1
  135. package/modules/lifecycle/migrations/lifecycle_021_crafts_artisan_artist.sql +29 -0
  136. package/modules/lifecycle/mingle.js +4 -4
  137. package/modules/lifecycle/page-tweak-write.js +1 -1
  138. package/modules/lifecycle/routes/task-write-routes.js +3 -1
  139. package/modules/lifecycle/routes/tasks.js +3 -3
  140. package/modules/lifecycle/ship-card.js +2 -3
  141. package/modules/lifecycle/task-classifier.js +39 -29
  142. package/modules/lifecycle/tests/cascade.mjs +7 -7
  143. package/modules/onboarding/founding-mode.js +3 -0
  144. package/modules/onboarding/genesis-home.js +21 -1
  145. package/modules/onboarding/routes/onboarding.js +2 -0
  146. package/modules/page-notes/notes.js +3 -2
  147. package/modules/page-notes/split.js +1 -1
  148. package/modules/platform-identity/craft-rollup.js +35 -7
  149. package/modules/platform-identity/migrations/platform_identity_036_crafts_artisan_artist.sql +45 -0
  150. package/modules/platform-identity/platform-identity.js +2 -2
  151. package/modules/platform-identity/routes/community-leaderboard.js +2 -1
  152. package/modules/platform-identity/routes/community-search.js +2 -1
  153. package/modules/platform-identity/routes/sso.js +2 -1
  154. package/modules/platform-identity/tests/platform-identity.mjs +6 -6
  155. package/modules/public-landing/public/community.html +3 -5
  156. package/modules/public-landing/public/index.html +1 -1
  157. package/modules/public-landing/public/projects.html +1 -1
  158. package/modules/specialities/migrations/specialities_004_crafts_artisan_artist.sql +30 -0
  159. package/modules/specialities/routes/specialities.js +2 -2
  160. package/modules/status-ui/public/status.js +1 -1
  161. package/modules/ui-design/kit/fixtures/community-leaderboard.json +10 -10
  162. package/modules/ui-design/kit/fixtures/community-search.json +2 -2
  163. package/modules/ui-design/kit/fixtures/scouting-recruiter.json +1 -1
  164. package/package-lock.json +2 -2
  165. package/package.json +1 -1
  166. package/release-notes.json +70 -0
  167. package/scripts/bongos/autobongos-loop.js +1 -1
  168. package/scripts/bongos/discipline-modes.json +7 -12
  169. package/scripts/bongos/fitness.js +1 -1
  170. package/scripts/bongos/gen-hall-sitemap.js +7 -7
  171. package/scripts/bongos/grade-smoke.js +2 -2
  172. package/scripts/bongos/init.js +2 -2
  173. package/scripts/bongos/newcomer-floor.config.json +1 -2
  174. package/scripts/bongos/newcomer-floor.js +4 -3
  175. package/scripts/bongos/newcomer-floor.templates.json +10 -12
  176. package/scripts/bongos/newcomer-floor.test.js +47 -38
  177. package/scripts/bongos/role-pack-guard.js +5 -4
  178. package/scripts/bongos/seed-tasks.example.json +3 -3
  179. package/scripts/bongos/setup.js +20 -107
  180. package/scripts/bongos/start.js +2 -2
  181. package/scripts/bongos/task.js +5 -4
  182. package/scripts/bongos/triage.js +1 -1
  183. package/scripts/bongos/worktree.js +16 -4
  184. package/scripts/setup-builder.js +6 -46
  185. package/src/bongos/auth-admission.js +8 -8
  186. package/src/bongos/craft-mode.js +2 -0
  187. package/src/bongos/db.js +39 -1
  188. package/src/bongos/github-repos.js +78 -0
  189. package/src/bongos/module-scope-map.js +22 -0
  190. package/src/bongos/role-stats.js +27 -11
  191. package/src/bongos/routes/github.js +100 -2
  192. package/src/bongos/routes/me.js +33 -0
  193. package/src/bongos/serve-internal.js +26 -9
  194. package/src/crafts.js +66 -0
  195. package/src/module-api.js +6 -1
  196. package/src/module-loader/manifest-schema.js +16 -0
  197. package/src/modules.js +2 -2
  198. package/src/role-responsibilities.js +6 -5
  199. package/tests/acclaim_feed_evidence_db.mjs +1 -1
  200. package/tests/activity_rollup_as_of.mjs +1 -1
  201. package/tests/api_alias_caller_ratchet.mjs +3 -3
  202. package/tests/application_lifecycle.mjs +1 -1
  203. package/tests/artist_review.mjs +1 -1
  204. package/tests/artist_review_goal.mjs +1 -1
  205. package/tests/blocker_hall_live_proof.mjs +1 -1
  206. package/tests/blocker_tasks_named_db.mjs +2 -2
  207. package/tests/canonical_profile_url.mjs +1 -1
  208. package/tests/cap_newcomer_tasks.mjs +13 -13
  209. package/tests/character_creation.mjs +294 -0
  210. package/tests/claim_gate_open_blocker.mjs +2 -2
  211. package/tests/claim_gate_rebase.mjs +6 -6
  212. package/tests/claude_materialize.mjs +1 -1
  213. package/tests/clawback_confirmed_backlog.mjs +3 -3
  214. package/tests/collab_page.mjs +8 -9
  215. package/tests/collab_self_settling.mjs +2 -2
  216. package/tests/community_leaderboard_route.mjs +13 -5
  217. package/tests/community_proof_db.mjs +3 -0
  218. package/tests/community_search_route.mjs +7 -6
  219. package/tests/conductor.mjs +1 -1
  220. package/tests/conductor_main_e2e.mjs +8 -8
  221. package/tests/copy_desk_flags_db.mjs +1 -1
  222. package/tests/copy_desk_page_approve.mjs +2 -2
  223. package/tests/copy_desk_page_claim.mjs +2 -2
  224. package/tests/copy_desk_page_next.mjs +1 -1
  225. package/tests/copy_desk_page_release.mjs +1 -1
  226. package/tests/copy_desk_page_submit.mjs +2 -2
  227. package/tests/core_258_box_session_claims_db.mjs +1 -1
  228. package/tests/craft_mode.mjs +48 -34
  229. package/tests/craft_mode_home.mjs +20 -20
  230. package/tests/craft_mode_looks.mjs +16 -15
  231. package/tests/craft_mode_nav.mjs +31 -29
  232. package/tests/craft_review_mode.mjs +46 -41
  233. package/tests/crafts_vocabulary.mjs +70 -0
  234. package/tests/cross_discipline_priority_direction.mjs +5 -5
  235. package/tests/fitness.mjs +7 -11
  236. package/tests/funded_project_pays_proof_db.mjs +1 -1
  237. package/tests/genesis_home.mjs +21 -1
  238. package/tests/github_chip.mjs +3 -3
  239. package/tests/github_repo_invite.mjs +379 -0
  240. package/tests/goal_criterion_confirm_ui.mjs +1 -1
  241. package/tests/goal_invite_consent.mjs +1 -1
  242. package/tests/goal_map_page.mjs +2 -2
  243. package/tests/goal_sort_staleness.mjs +1 -1
  244. package/tests/goal_spec_task.mjs +1 -1
  245. package/tests/goal_spec_task_db.mjs +3 -3
  246. package/tests/grade_engine.mjs +2 -2
  247. package/tests/grader_no_artifact.mjs +1 -1
  248. package/tests/grader_prompt_fencing.mjs +1 -1
  249. package/tests/grader_prompt_window_bound.mjs +1 -1
  250. package/tests/grader_reader_lens.mjs +2 -2
  251. package/tests/grader_severity_gate.mjs +1 -1
  252. package/tests/grader_unbacked_score.mjs +1 -1
  253. package/tests/hall_approval_queue.mjs +1 -1
  254. package/tests/hall_board_world.mjs +2 -2
  255. package/tests/hall_builders_page_pass.mjs +1 -1
  256. package/tests/hall_city.mjs +1 -1
  257. package/tests/hall_fullscreen.mjs +27 -3
  258. package/tests/hall_kit_charts.mjs +1 -1
  259. package/tests/hall_landing_boot.mjs +1 -1
  260. package/tests/hall_my_agents.mjs +16 -16
  261. package/tests/hall_nav.mjs +22 -15
  262. package/tests/hall_options_menu.mjs +247 -0
  263. package/tests/hall_oversight_world.mjs +1 -1
  264. package/tests/hall_palette.mjs +8 -9
  265. package/tests/hall_profile_world.mjs +3 -3
  266. package/tests/hall_record_world.mjs +2 -2
  267. package/tests/hall_settings_world.mjs +2 -1
  268. package/tests/hall_sitemap.mjs +6 -5
  269. package/tests/hall_start_screen.mjs +244 -0
  270. package/tests/hall_street.mjs +2 -2
  271. package/tests/hall_street_inside.mjs +17 -9
  272. package/tests/hall_studio_world.mjs +3 -3
  273. package/tests/hall_visor.mjs +224 -0
  274. package/tests/help_requests.mjs +47 -15
  275. package/tests/helpers.mjs +4 -4
  276. package/tests/home_city.mjs +6 -4
  277. package/tests/home_city_widget.mjs +33 -14
  278. package/tests/home_gallery_widget.mjs +4 -4
  279. package/tests/home_idea_box.mjs +6 -6
  280. package/tests/hub_community_page.mjs +3 -3
  281. package/tests/hub_craft_rollup.mjs +75 -36
  282. package/tests/hub_recent_ships.mjs +3 -2
  283. package/tests/idea_cascade.mjs +2 -2
  284. package/tests/idea_credit_streams_db.mjs +1 -1
  285. package/tests/idea_detail_page.mjs +1 -1
  286. package/tests/idea_ideate_full.mjs +5 -2
  287. package/tests/idea_spark_hall.mjs +1 -1
  288. package/tests/init.mjs +3 -3
  289. package/tests/instance_manifest.mjs +2 -2
  290. package/tests/kickoff_checklist.mjs +1 -1
  291. package/tests/leaderboard_roles.mjs +34 -25
  292. package/tests/lifecycle_claim_ladder_refs.mjs +55 -0
  293. package/tests/lightbulb.mjs +14 -10
  294. package/tests/main_discipline.mjs +8 -8
  295. package/tests/markdown_line_endings.mjs +1 -1
  296. package/tests/market_watch_pages.mjs +52 -74
  297. package/tests/me_streak_leaderboard_queries.mjs +3 -3
  298. package/tests/migration_allocation.mjs +1 -1
  299. package/tests/mingle.mjs +36 -21
  300. package/tests/module-scope-map.mjs +22 -2
  301. package/tests/module_api.mjs +1 -0
  302. package/tests/module_contributions.mjs +13 -13
  303. package/tests/module_manifest.mjs +29 -0
  304. package/tests/module_skills_hook.mjs +2 -2
  305. package/tests/modules.mjs +14 -7
  306. package/tests/nav_permission_atoms.mjs +2 -1
  307. package/tests/needs_rebase.mjs +1 -1
  308. package/tests/newcomer_restock.mjs +13 -13
  309. package/tests/page_notes_panel.mjs +22 -22
  310. package/tests/page_tweak_no_block.mjs +1 -1
  311. package/tests/paintbrush.mjs +7 -7
  312. package/tests/privacy_matrix.mjs +1 -0
  313. package/tests/profile_activity.mjs +20 -23
  314. package/tests/profile_earnings.mjs +1 -1
  315. package/tests/profile_nudge_prefs.mjs +4 -4
  316. package/tests/profile_role_headline.mjs +73 -46
  317. package/tests/profile_role_stats.mjs +55 -45
  318. package/tests/profile_rollup_consent.mjs +19 -6
  319. package/tests/profile_route.mjs +12 -12
  320. package/tests/profile_ui_cross_project.mjs +4 -4
  321. package/tests/project_invite_suggestions.mjs +2 -2
  322. package/tests/promote_goal_id.mjs +1 -1
  323. package/tests/publish_manifest.mjs +1 -1
  324. package/tests/rank_floor_wiring.mjs +1 -1
  325. package/tests/regrade_actual_minutes.mjs +1 -1
  326. package/tests/removal_proposal.mjs +2 -2
  327. package/tests/reward_gate.mjs +4 -4
  328. package/tests/reward_mode_cost_plus_only.mjs +1 -1
  329. package/tests/role_responsibilities.mjs +18 -12
  330. package/tests/role_stats_db.mjs +17 -16
  331. package/tests/scouting_rollup_fanout.mjs +5 -5
  332. package/tests/seed_tasks.mjs +5 -2
  333. package/tests/session_earnings_mirror_db.mjs +1 -1
  334. package/tests/session_reward_on_land_db.mjs +1 -1
  335. package/tests/session_reward_reconcile_db.mjs +2 -2
  336. package/tests/sessions_kit_pager.mjs +1 -1
  337. package/tests/settings_art_key_absent.mjs +2 -2
  338. package/tests/settings_main_role.mjs +21 -20
  339. package/tests/setup_flow.mjs +3 -3
  340. package/tests/setup_smoke.mjs +20 -0
  341. package/tests/ship_card.mjs +6 -6
  342. package/tests/specialities.mjs +16 -4
  343. package/tests/speciality_db_shape.mjs +6 -6
  344. package/tests/speciality_injection.mjs +17 -17
  345. package/tests/speciality_offer_ui.mjs +5 -5
  346. package/tests/speciality_offered.mjs +6 -5
  347. package/tests/speciality_session_skills.mjs +7 -7
  348. package/tests/speciality_skills.mjs +11 -11
  349. package/tests/speciality_walkthrough_ui.mjs +2 -2
  350. package/tests/sso_checkin_identity_spoof.mjs +2 -1
  351. package/tests/task_classifier_vocab.mjs +10 -0
  352. package/tests/task_cli.mjs +11 -3
  353. package/tests/task_criteria_unlink_db.mjs +2 -2
  354. package/tests/task_discipline_required.mjs +38 -16
  355. package/tests/task_goal_required.mjs +4 -4
  356. package/tests/task_needs_db.mjs +1 -1
  357. package/tests/tweak_replaces_artist_review.mjs +1 -1
  358. package/tests/update_pill.mjs +153 -2
  359. package/tests/upkeep_home.mjs +1 -1
  360. package/tests/v3_c1_options_proof.mjs +145 -0
  361. package/tests/visual_review.mjs +3 -3
  362. package/tests/watch_applications_queue.mjs +8 -5
  363. package/tests/watch_sealed_floor.mjs +3 -1
  364. package/tests/welcome_boot.mjs +12 -9
  365. package/tests/welcome_boot_browser.mjs +13 -13
  366. package/tests/worktree_helper.mjs +16 -1
@@ -8,7 +8,7 @@
8
8
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/builders/style.css?v=2026-09-23-changelist1">
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-10-options">
12
12
  <link rel="stylesheet" href="/builders/oversight.css">
13
13
  <link rel="stylesheet" href="/builders/project-settings.css?v=2026-11-02-outflow">
14
14
  </head>
@@ -80,10 +80,10 @@
80
80
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
81
81
 
82
82
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
83
- <script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
83
+ <script src="/builders/shell-game.js?v=2026-11-10-options"></script>
84
84
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
85
- <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
86
- <script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
85
+ <script src="/builders/craft-mode.js?v=2026-11-10-visor"></script>
86
+ <script src="/builders/shell.js?v=2026-11-10-character"></script>
87
87
  <script src="/builders/hall-dialog.js"></script>
88
88
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
89
89
  <script src="/builders/palette.js" defer></script>
@@ -8,7 +8,7 @@
8
8
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-10-options">
12
12
  <!-- The oversight family's archetype: a titled row with facts and actions, the
13
13
  group head and the foot line (records/oversight-family.md). This page's
14
14
  repeated thing is that row at the desk's one-line density. -->
@@ -82,10 +82,10 @@
82
82
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
83
83
 
84
84
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
85
- <script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
85
+ <script src="/builders/shell-game.js?v=2026-11-10-options"></script>
86
86
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
87
- <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
88
- <script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
87
+ <script src="/builders/craft-mode.js?v=2026-11-10-visor"></script>
88
+ <script src="/builders/shell.js?v=2026-11-10-character"></script>
89
89
  <script src="/builders/hall-dialog.js"></script>
90
90
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
91
91
  <script src="/builders/palette.js" defer></script>
@@ -0,0 +1,409 @@
1
+ // options-menu.js — the ONE options menu (task 1005144, BONGOS-V3 R04, goal 1000141;
2
+ // ADR 0401; docs/specs/bongos-v3-game-conversion.md, criterion game-opens-not-navigates).
3
+ //
4
+ // Every setting and configuration in the hall is reached from one vertical, grouped
5
+ // menu with a search box at the top. The menu IS the Settings page (/settings): its
6
+ // own panels stay where they were (the native groups below), and the six screens that
7
+ // used to be pages of their own — My agents, Project settings, Modules, Permissions,
8
+ // and Market Watch's settings and rules — open INSIDE the menu, in a frame, with
9
+ // `?embed=1` (shell-game.js strips their chrome). A plain visit to one of those old
10
+ // addresses is sent here by the server (serve-internal.js RETIRED_TO_OPTIONS), as
11
+ // `/settings?open=<entry>`, and the browser carries the old #anchor along.
12
+ //
13
+ // Two halves, the my-agents.js shape: the PURE half (the catalogue, who may see an
14
+ // entry, the search, what an address opens) is node-require-safe for
15
+ // tests/hall_options_menu.mjs; the page half draws the rail and the frames.
16
+ //
17
+ // What it does NOT do: it gates nothing. An entry is listed for the viewer the
18
+ // sidebar would have shown its page to (the same rank keys as shell.js gateAllows,
19
+ // cosmetic only); every screen keeps its own server page gate and its own routes,
20
+ // which are the wall (ADR 0016).
21
+
22
+ (function (root) {
23
+ 'use strict';
24
+
25
+ // ---------- the pure half ----------
26
+
27
+ // The menu, top to bottom. A group is a heading; an entry is one line under it.
28
+ // id the entry's address: /settings#<id> or /settings?open=<id>
29
+ // native true = one of Settings' own groups (settings.html data-group=<id>)
30
+ // embed the old page's path, opened in the frame with ?embed=1
31
+ // gate the rank key the old sidebar item used (signed | member | government | archon)
32
+ // module shown only where that module is on (window.OTB.moduleOn)
33
+ // words what a reader might type to find it, beyond its label
34
+ // anchors the places inside it a search can land on: { label, anchor, words }
35
+ var MENU = [
36
+ { head: 'You', items: [
37
+ { id: 'account', label: 'Account', native: true, gate: 'signed',
38
+ words: 'name display connections discord age privacy profile roles craft',
39
+ anchors: [
40
+ { label: 'Display name', anchor: 'display-name', words: 'name' },
41
+ { label: 'Connections', anchor: 'connections', words: 'discord link' },
42
+ { label: 'Your age', anchor: 'age', words: 'birthday' },
43
+ { label: 'Profile and privacy', anchor: 'privacy', words: 'cross-project public' },
44
+ { label: 'Preferred roles', anchor: 'disciplines', words: 'craft artisan artist discipline' },
45
+ ] },
46
+ { id: 'my-agents', label: 'Your agents', embed: '/my-agents', gate: 'signed',
47
+ words: 'claude agent ai interaction wandering rendering session cards models specialities',
48
+ anchors: [
49
+ { label: 'How Claude talks to you', anchor: 'talk', words: 'interaction technical verbosity rendering cards' },
50
+ { label: 'How your agents work', anchor: 'work', words: 'wandering autonomy' },
51
+ { label: 'Which models run', anchor: 'models', words: 'model opus sonnet' },
52
+ { label: 'Skill voices', anchor: 'skills', words: 'voice' },
53
+ { label: 'What it knows', anchor: 'knows', words: 'specialities expertise' },
54
+ ] },
55
+ ] },
56
+ { head: 'Sign-in and keys', items: [
57
+ { id: 'access', label: 'Access and keys', native: true, gate: 'signed',
58
+ words: 'cli token terminal sessions security cards passkeys autobongos runner key api gemini image',
59
+ anchors: [
60
+ { label: 'CLI access', anchor: 'cli', words: 'token terminal command line' },
61
+ { label: 'Active sessions', anchor: 'sessions-scroll', words: 'signed in devices' },
62
+ { label: 'Security cards', anchor: 'passkeys-scroll', words: 'passkey card' },
63
+ { label: 'Your Autobongos runner', anchor: 'autobongos', words: 'runner machine' },
64
+ { label: 'Image-generation key', anchor: 'art-key', words: 'gemini art' },
65
+ { label: 'Your AI keys', anchor: 'ai-keys', words: 'anthropic api key' },
66
+ ] },
67
+ ] },
68
+ { head: 'Preferences', items: [
69
+ { id: 'preferences', label: 'Preferences', native: true, gate: 'signed', words: 'usage reports' },
70
+ { id: 'sound', label: 'Sound', native: true, gate: 'signed', words: 'sounds audio chime event volume mute' },
71
+ ] },
72
+ { head: 'The project', items: [
73
+ { id: 'project-settings', label: 'Project settings', embed: '/project-settings', gate: 'government',
74
+ words: 'fundamentals name ship mode spend cap funding danger zone delete' },
75
+ { id: 'modules', label: 'Modules', embed: '/modules', gate: 'member',
76
+ words: 'module catalog enable disable toggle on off install extension' },
77
+ { id: 'permissions', label: 'Permissions and constitution', embed: '/government', gate: 'government',
78
+ words: 'ranks roles permission assignments outside access machine principals constitution laws',
79
+ anchors: [
80
+ { label: 'Ranks and permissions', anchor: 'ranks', words: 'rank permission assignments' },
81
+ { label: 'Constitution', anchor: 'constitution', words: 'laws charter' },
82
+ ] },
83
+ { id: 'market-watch', label: 'Market Watch settings', embed: '/market-watch', anchor: 'settings', gate: 'archon', module: 'market-watch',
84
+ words: 'market watch cap key switch weekly' },
85
+ { id: 'market-watch-rules', label: 'Market Watch rules', embed: '/market-watch-rules', gate: 'signed', module: 'market-watch',
86
+ words: 'market watch steps search score propose change' },
87
+ { id: 'update', label: 'Software update', native: true, gate: 'signed', words: 'version upgrade release update' },
88
+ { id: 'extensions', label: 'Extensions', native: true, gate: 'signed', contributed: true, words: 'module panels' },
89
+ ] },
90
+ ];
91
+
92
+ // The old addresses that used to be pages: path → entry id (the server's map too).
93
+ var RETIRED = {
94
+ '/my-agents': 'my-agents',
95
+ '/project-settings': 'project-settings',
96
+ '/modules': 'modules',
97
+ '/government': 'permissions',
98
+ '/market-watch-rules': 'market-watch-rules',
99
+ '/market-watch-settings': 'market-watch',
100
+ };
101
+
102
+ function entries() {
103
+ var out = [];
104
+ MENU.forEach(function (g) { g.items.forEach(function (it) { out.push(Object.assign({ head: g.head }, it)); }); });
105
+ return out;
106
+ }
107
+ function entry(id) {
108
+ var all = entries();
109
+ for (var i = 0; i < all.length; i += 1) if (all[i].id === id) return all[i];
110
+ return null;
111
+ }
112
+
113
+ // The same rank ladder as shell.js gateAllows. viewer = { authed, rank }.
114
+ function gateAllows(gate, viewer) {
115
+ var v = viewer || {};
116
+ var authed = v.authed === true;
117
+ var rank = String(v.rank || '').toLowerCase();
118
+ switch (gate) {
119
+ case 'signed': return authed;
120
+ case 'member': return authed && rank !== 'xenos' && rank !== '';
121
+ case 'government': return authed && (rank === 'metic' || rank === 'archon');
122
+ case 'archon': return authed && rank === 'archon';
123
+ default: return false;
124
+ }
125
+ }
126
+
127
+ // The menu this viewer sees: groups with at least one entry they may open.
128
+ // opts.moduleOn(key) answers module gates; opts.contributed says whether a module
129
+ // contributed a Settings panel (the Extensions entry shows only then).
130
+ function visibleMenu(viewer, opts) {
131
+ var o = opts || {};
132
+ var moduleOn = typeof o.moduleOn === 'function' ? o.moduleOn : function () { return true; };
133
+ return MENU.map(function (g) {
134
+ var items = g.items.filter(function (it) {
135
+ if (!gateAllows(it.gate, viewer)) return false;
136
+ if (it.module && !moduleOn(it.module)) return false;
137
+ if (it.contributed && !o.contributed) return false;
138
+ return true;
139
+ });
140
+ return { head: g.head, items: items };
141
+ }).filter(function (g) { return g.items.length > 0; });
142
+ }
143
+
144
+ function norm(s) { return String(s || '').toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim(); }
145
+
146
+ // The smart search: every word typed must appear in the line's label, its words,
147
+ // its entry's label or its group's heading. Ranked so the first line is the one
148
+ // Enter should open: a hit in the line's own label first; then a hit in its own
149
+ // words; and when nothing hit a label, the exact place (CLI access) above the
150
+ // group that merely holds it (Access and keys), because "token" means the token.
151
+ // Returns [{ id, anchor, label, where }] — `where` names the entry for a sub-line.
152
+ function search(query, menu) {
153
+ var words = norm(query).split(' ').filter(Boolean);
154
+ if (!words.length) return [];
155
+ var lines = [];
156
+ (menu || []).forEach(function (g) {
157
+ g.items.forEach(function (it) {
158
+ lines.push({ id: it.id, anchor: it.anchor || null, label: it.label, where: g.head, sub: false,
159
+ label_n: norm(it.label), own: norm([it.label, it.words].join(' ')), hay: norm([it.label, it.words, g.head].join(' ')) });
160
+ (it.anchors || []).forEach(function (a) {
161
+ lines.push({ id: it.id, anchor: a.anchor, label: a.label, where: it.label, sub: true,
162
+ label_n: norm(a.label), own: norm([a.label, a.words].join(' ')), hay: norm([a.label, a.words, it.label, g.head].join(' ')) });
163
+ });
164
+ });
165
+ });
166
+ var hits = [];
167
+ var count = function (s) { return words.filter(function (w) { return s.indexOf(w) !== -1; }).length; };
168
+ lines.forEach(function (l, i) {
169
+ if (!words.every(function (w) { return l.hay.indexOf(w) !== -1; })) return;
170
+ hits.push({ l: l, label: count(l.label_n), own: count(l.own), i: i });
171
+ });
172
+ hits.sort(function (a, b) {
173
+ return (b.label - a.label) || (b.own - a.own)
174
+ || (a.label === 0 && a.l.sub !== b.l.sub ? (a.l.sub ? -1 : 1) : 0) || (a.i - b.i);
175
+ });
176
+ return hits.map(function (h) { return { id: h.l.id, anchor: h.l.anchor, label: h.l.label, where: h.l.where }; });
177
+ }
178
+
179
+ // What an address opens: ?open=<id> with an optional #anchor, or a bare #<id>,
180
+ // or a settings panel anchor (#cli) the page itself resolves. Returns
181
+ // { id, anchor } or null (null = the page's own hash handling decides).
182
+ function target(search, hash) {
183
+ var params;
184
+ try { params = new URLSearchParams(search || ''); } catch (_) { params = null; }
185
+ var open = params ? params.get('open') : null;
186
+ var h = String(hash || '').replace(/^#/, '');
187
+ if (open && entry(open)) {
188
+ var e = entry(open);
189
+ return { id: open, anchor: h || e.anchor || null };
190
+ }
191
+ if (h && entry(h)) return { id: h, anchor: entry(h).anchor || null };
192
+ return null;
193
+ }
194
+
195
+ // The frame's address for an embedded entry: same-host path + ?embed=1 + #anchor.
196
+ // `base` is '' on the builders host, '/builders' on the apex.
197
+ function frameSrc(e, anchor, base) {
198
+ if (!e || !e.embed) return null;
199
+ var a = anchor || e.anchor || '';
200
+ return (base || '') + e.embed + '?embed=1' + (a ? '#' + encodeURIComponent(a) : '');
201
+ }
202
+
203
+ // Where closing the menu goes: ?from= when it is a same-site path, else null
204
+ // (the caller then steps back, or goes home).
205
+ function closeTo(search) {
206
+ var from = null;
207
+ try { from = new URLSearchParams(search || '').get('from'); } catch (_) { from = null; }
208
+ if (!from || from.charAt(0) !== '/' || from.charAt(1) === '/' || from.charAt(1) === '\\') return null;
209
+ return from;
210
+ }
211
+
212
+ var pure = { MENU: MENU, RETIRED: RETIRED, entries: entries, entry: entry, gateAllows: gateAllows,
213
+ visibleMenu: visibleMenu, search: search, target: target, frameSrc: frameSrc, closeTo: closeTo };
214
+ if (typeof module !== 'undefined' && module.exports) { module.exports = pure; return; }
215
+ if (typeof document === 'undefined') { root.OTBOptionsMenu = pure; return; }
216
+
217
+ // ---------- the page half ----------
218
+
219
+ // The hall's one escaper (dom-utils.js, loaded first on every page; task 1004876).
220
+ var esc = function (s) { return window.OTB.escapeHtml(s); };
221
+ var base = /^\/builders(\/|$)/.test(window.location.pathname) ? '/builders' : '';
222
+ var state = { menu: [], active: null, onNative: null, query: '', from: null };
223
+ var railEl = null;
224
+ var groupsEl = null;
225
+
226
+ // An embedded entry's section: created the first time it opens, so a screen's
227
+ // reads are paid only when someone asks for it.
228
+ function frameFor(e, anchor) {
229
+ var id = 'opt-frame-' + e.id;
230
+ var sec = document.getElementById(id);
231
+ var src = frameSrc(e, anchor, base);
232
+ if (!sec) {
233
+ sec = document.createElement('section');
234
+ sec.id = id;
235
+ sec.className = 'opt-embed';
236
+ sec.setAttribute('data-group', e.id);
237
+ sec.setAttribute('aria-label', e.label);
238
+ sec.innerHTML = '<iframe class="opt-embed__frame" title="' + esc(e.label) + '"></iframe>';
239
+ groupsEl.appendChild(sec);
240
+ sec.querySelector('iframe').src = src;
241
+ sec.dataset.src = src;
242
+ } else if (anchor && sec.dataset.src !== src) {
243
+ // A new place in a screen already open: move its hash, no reload.
244
+ try { sec.querySelector('iframe').contentWindow.location.hash = anchor; } catch (_) { sec.querySelector('iframe').src = src; }
245
+ sec.dataset.src = src;
246
+ }
247
+ return sec;
248
+ }
249
+
250
+ function activate(id, anchor, opts) {
251
+ var e = entry(id);
252
+ if (!e || !groupsEl) return false;
253
+ if (!state.menu.some(function (g) { return g.items.some(function (it) { return it.id === id; }); })) return false;
254
+ state.active = id;
255
+ groupsEl.dataset.active = id;
256
+ Array.prototype.forEach.call(groupsEl.querySelectorAll('.opt-embed'), function (s) { s.classList.remove('opt-embed--on'); });
257
+ if (e.embed) frameFor(e, anchor).classList.add('opt-embed--on');
258
+ else if (state.onNative) state.onNative(id, anchor);
259
+ markActive();
260
+ if (!opts || opts.url !== false) {
261
+ // The address says what is open, so it can be shared; ?from= stays, so
262
+ // closing still goes back to where the menu was opened.
263
+ var q = [];
264
+ if (e.embed) q.push('open=' + encodeURIComponent(id));
265
+ if (state.from) q.push('from=' + encodeURIComponent(state.from));
266
+ var frag = e.embed ? (anchor || '') : (anchor || id);
267
+ var url = window.location.pathname + (q.length ? '?' + q.join('&') : '') + (frag ? '#' + encodeURIComponent(frag) : '');
268
+ try { history.replaceState(null, '', url); } catch (_) { /* file:// */ }
269
+ }
270
+ return true;
271
+ }
272
+
273
+ function markActive() {
274
+ if (!railEl) return;
275
+ Array.prototype.forEach.call(railEl.querySelectorAll('.opt-link'), function (a) {
276
+ var on = a.dataset.key === state.active && !a.dataset.anchor;
277
+ a.classList.toggle('opt-link--active', on);
278
+ if (on) a.setAttribute('aria-current', 'page'); else a.removeAttribute('aria-current');
279
+ });
280
+ }
281
+
282
+ function linkHtml(id, label, anchor, where) {
283
+ var href = anchor ? '?open=' + encodeURIComponent(id) + '#' + encodeURIComponent(anchor) : '#' + encodeURIComponent(id);
284
+ return '<a class="opt-link" href="' + esc(href) + '" data-key="' + esc(id) + '"' + (anchor ? ' data-anchor="' + esc(anchor) + '"' : '') + '>' +
285
+ '<span class="opt-link__label">' + esc(label) + '</span>' +
286
+ (where ? '<span class="opt-link__where">' + esc(where) + '</span>' : '') + '</a>';
287
+ }
288
+
289
+ function renderList() {
290
+ var list = railEl.querySelector('.opt-list');
291
+ if (!list) return;
292
+ if (state.query) {
293
+ var hits = search(state.query, state.menu);
294
+ list.innerHTML = hits.length
295
+ ? '<p class="opt-head">Matches</p>' + hits.map(function (h) { return linkHtml(h.id, h.label, h.anchor, h.where); }).join('')
296
+ : '<p class="opt-none">Nothing matches “' + esc(state.query) + '”. Try another word.</p>';
297
+ } else {
298
+ list.innerHTML = state.menu.map(function (g) {
299
+ return '<p class="opt-head">' + esc(g.head) + '</p>' + g.items.map(function (it) { return linkHtml(it.id, it.label); }).join('');
300
+ }).join('');
301
+ }
302
+ markActive();
303
+ }
304
+
305
+ // The rail: the search box, then the groups. Typing filters; Enter opens the first match.
306
+ function draw() {
307
+ railEl.classList.add('opt-rail');
308
+ railEl.innerHTML =
309
+ '<label class="opt-search"><span class="sr-only">Search settings</span>' +
310
+ '<input type="search" id="opt-search" placeholder="Search settings" autocomplete="off" spellcheck="false"></label>' +
311
+ '<div class="opt-list"></div>';
312
+ var input = railEl.querySelector('#opt-search');
313
+ input.addEventListener('input', function () { state.query = input.value; renderList(); });
314
+ input.addEventListener('keydown', function (e) {
315
+ if (e.key === 'Enter') {
316
+ var first = railEl.querySelector('.opt-list .opt-link');
317
+ if (first) { e.preventDefault(); first.click(); }
318
+ } else if (e.key === 'Escape' && input.value) {
319
+ // The field's own Escape: clear the search (the press stays the field's).
320
+ e.preventDefault();
321
+ input.value = ''; state.query = ''; renderList();
322
+ }
323
+ });
324
+ railEl.addEventListener('click', function (e) {
325
+ var a = e.target.closest ? e.target.closest('.opt-link') : null;
326
+ if (!a) return;
327
+ e.preventDefault();
328
+ activate(a.dataset.key, a.dataset.anchor || null);
329
+ // On a phone the menu sits above the screen it opened, so bring that screen up —
330
+ // after the opened group's own scroll to the top, which would otherwise win, and
331
+ // clear of the sticky top bar.
332
+ if (window.matchMedia && window.matchMedia('(max-width: 860px)').matches) {
333
+ window.setTimeout(function () {
334
+ var bar = document.getElementById('app-topbar');
335
+ var clear = bar ? bar.getBoundingClientRect().height + 8 : 8;
336
+ window.scrollTo({ top: Math.max(0, groupsEl.getBoundingClientRect().top + window.scrollY - clear) });
337
+ }, 60);
338
+ }
339
+ });
340
+ renderList();
341
+ }
342
+
343
+ // Close the menu (game mode's Escape): back where it was opened from.
344
+ function close() {
345
+ var to = state.from || closeTo(window.location.search);
346
+ if (to) { window.location.assign(to); return; }
347
+ var ref = '';
348
+ try { ref = document.referrer ? new URL(document.referrer).origin : ''; } catch (_) { ref = ''; }
349
+ if (ref === window.location.origin && window.history.length > 1) { window.history.back(); return; }
350
+ window.location.assign(base + '/');
351
+ }
352
+
353
+ // Game mode hands the menu its last Escape (shell-game.js); outside it, the
354
+ // menu is a page like any other and the press is the shell's.
355
+ function escape() {
356
+ var game = window.OTBShellGame;
357
+ if (game && game.on && game.on()) close();
358
+ }
359
+
360
+ // settings.js calls this once it knows the viewer.
361
+ // rail, groups the two mounts
362
+ // viewer { authed, rank }
363
+ // contributed whether a module contributed a Settings panel
364
+ // onNative(id, anchor) shows one of Settings' own groups
365
+ function mount(o) {
366
+ railEl = o.rail; groupsEl = o.groups;
367
+ state.onNative = o.onNative || null;
368
+ var moduleOn = window.OTB && typeof window.OTB.moduleOn === 'function' ? window.OTB.moduleOn : function () { return true; };
369
+ state.menu = visibleMenu(o.viewer, { moduleOn: moduleOn, contributed: !!o.contributed });
370
+ state.from = closeTo(window.location.search);
371
+ if (railEl && groupsEl) draw();
372
+ return state.menu;
373
+ }
374
+ // Re-read the viewer's menu (a module contributed a panel after load).
375
+ function refresh(o) {
376
+ var moduleOn = window.OTB && typeof window.OTB.moduleOn === 'function' ? window.OTB.moduleOn : function () { return true; };
377
+ state.menu = visibleMenu(o.viewer, { moduleOn: moduleOn, contributed: !!o.contributed });
378
+ if (railEl) renderList();
379
+ }
380
+
381
+ // A link on the menu's own panels to an old page (Settings' "these moved to My agents"
382
+ // lines, a docs link) opens that entry in place instead of a round trip through the
383
+ // server's redirect back to this same page.
384
+ if (typeof document !== 'undefined' && document.addEventListener) {
385
+ document.addEventListener('click', function (e) {
386
+ if (e.defaultPrevented || e.button || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
387
+ var a = e.target && e.target.closest ? e.target.closest('a[href]') : null;
388
+ if (!a || !groupsEl || !groupsEl.contains(a) || a.getAttribute('target')) return;
389
+ var to;
390
+ try { to = new URL(a.getAttribute('href'), window.location.href); } catch (_) { return; }
391
+ if (to.origin !== window.location.origin) return;
392
+ var id = RETIRED[to.pathname.replace(/^\/builders/, '').replace(/\.html$/, '')];
393
+ if (!id) return;
394
+ var anchor = to.hash ? decodeURIComponent(to.hash.slice(1)) : null;
395
+ if (activate(id, anchor)) e.preventDefault();
396
+ });
397
+ }
398
+
399
+ // Open what the address asks for. true = it was a menu entry; false = the page decides.
400
+ function openFromUrl() {
401
+ var t = target(window.location.search, window.location.hash);
402
+ if (!t) return false;
403
+ return activate(t.id, t.anchor, { url: false });
404
+ }
405
+
406
+ root.OTBOptionsMenu = pure;
407
+ root.OTBOptions = { mount: mount, refresh: refresh, activate: activate, openFromUrl: openFromUrl,
408
+ escape: escape, close: close, active: function () { return state.active; } };
409
+ }(typeof window !== 'undefined' ? window : globalThis));
@@ -161,8 +161,8 @@
161
161
  '<span class="pb__now"><span class="pb__tag">Now</span> ' + esc(c.now) + '</span>' +
162
162
  '</li>';
163
163
  }).join('') + '</ol>' +
164
- '<p class="pb__hint">Sending submits the page: an engineer applies the new words, and they come back to you to approve before anyone sees them.' +
165
- (rows.some(function (c) { return c.remove; }) ? ' A line you remove goes to an engineer to take out.' : '') + '</p>' +
164
+ '<p class="pb__hint">Sending submits the page: an artisan applies the new words, and they come back to you to approve before anyone sees them.' +
165
+ (rows.some(function (c) { return c.remove; }) ? ' A line you remove goes to an artisan to take out.' : '') + '</p>' +
166
166
  '<p class="pb__error" role="alert">' + esc(err || '') + '</p>' +
167
167
  '<div class="pb__actions">' +
168
168
  '<button class="pb__btn" type="button" data-pb-back>Keep editing</button>' +
@@ -172,7 +172,7 @@
172
172
 
173
173
  // After sending: the words are on their way, and the look is one press away.
174
174
  function sentHtml(n) {
175
- return '<p class="pb__line" role="status">Sent for approval: ' + esc(plural(n, 'change', 'changes')) + '. An engineer applies the new words, and they come back to you in the studio to approve.</p>' +
175
+ return '<p class="pb__line" role="status">Sent for approval: ' + esc(plural(n, 'change', 'changes')) + '. An artisan applies the new words, and they come back to you in the studio to approve.</p>' +
176
176
  '<p class="pb__line"><strong>Review how this page looks too?</strong> Write what you would change about it, and each point becomes an idea under your name.</p>' +
177
177
  '<p class="pb__error" role="alert">' + esc(st.notesError || '') + '</p>' +
178
178
  '<div class="pb__actions">' +
@@ -51,10 +51,9 @@
51
51
  // Anchors must stay in step with their source of truth; the test asserts it:
52
52
  // profile → profile.js RAIL_ITEMS (Accomplishments folded in, task 1005050)
53
53
  // work → work.js makeTabs ids
54
- // government → government.js makeTabs ids (two rooms since ADR 0266)
55
54
  // watch → watch.js makeTabs ids (Sessions and Autobongos since task 1005049)
56
55
  // settings → settings.js RAIL_GROUPS
57
- // my-agents → my-agents.js ANCHORS (the agent settings left Settings, task 1005044)
56
+ // my-agents → my-agents.js ANCHORS, opened in the options menu (task 1005144)
58
57
  // primer → primer.md's h2 slugs + primer-live.js SECTIONS (task 1005046)
59
58
  var SECTIONS = [
60
59
  // task 1005050: your numbers are one page. Accomplishments' private half is
@@ -74,10 +73,8 @@
74
73
  // read live from the sidebar like every other page and must not be declared
75
74
  // here as well — a sub-destination and a page of the same name would offer
76
75
  // the reader two rows to the same room.
77
- { parent: 'Government', gate: 'nav-government', items: [
78
- { label: 'Constitution', href: '/government#constitution' },
79
- { label: 'Ranks & Permissions', href: '/government#ranks' },
80
- ] },
76
+ // task 1005144: Permissions and the Constitution are an options menu entry now, shown
77
+ // to the ranks that may open it; the menu's own search finds them, so no row here.
81
78
  // task 1005049: Sessions and Autobongos were pages of their own; they are
82
79
  // Watch's tabs now, so typing either name still finds them.
83
80
  { parent: 'Watch', gate: 'nav-watch', items: [
@@ -95,12 +92,13 @@
95
92
  { label: 'Treasury', href: '/money#the-treasury' },
96
93
  { label: 'Hosting costs (Fleet)', href: '/money#hosting' },
97
94
  ] },
98
- { parent: 'My agents', gate: 'nav-my-agents', items: [
99
- { label: 'How it talks to you', href: '/my-agents#talk' },
100
- { label: 'How your agents work', href: '/my-agents#work' },
101
- { label: 'Which models run', href: '/my-agents#models' },
102
- { label: 'Skill voices', href: '/my-agents#skills' },
103
- { label: 'What it knows', href: '/my-agents#knows' },
95
+ // task 1005144: My agents opens inside the options menu (the profile chip).
96
+ { parent: 'Your agents', gate: 'shell-user', items: [
97
+ { label: 'How it talks to you', href: '/settings?open=my-agents#talk' },
98
+ { label: 'How your agents work', href: '/settings?open=my-agents#work' },
99
+ { label: 'Which models run', href: '/settings?open=my-agents#models' },
100
+ { label: 'Skill voices', href: '/settings?open=my-agents#skills' },
101
+ { label: 'What it knows', href: '/settings?open=my-agents#knows' },
104
102
  ] },
105
103
  // task 1005046: the Primer is the one reading room, so the rank ladder, the
106
104
  // economy and the diagrams that had pages of their own are its chapters
@@ -115,7 +113,7 @@
115
113
  { label: 'Security bounty rates', href: '/primer#security-bounty-rates' },
116
114
  { label: 'Versions', href: '/primer#how-versions-organise-the-work' },
117
115
  ] },
118
- { parent: 'Settings', gate: 'shell-user', items: [
116
+ { parent: 'Options', gate: 'shell-user', items: [
119
117
  { label: 'Account', href: '/settings#account' },
120
118
  { label: 'Access', href: '/settings#access' },
121
119
  { label: 'Extensions', href: '/settings#extensions' },
@@ -8,7 +8,7 @@
8
8
  <link rel="icon" href="/modules/ui-design/public/brand/favicon.svg" type="image/svg+xml">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
11
+ <link rel="stylesheet" href="/builders/style.css?v=2026-11-10-options">
12
12
  <link rel="stylesheet" href="/builders/room.css">
13
13
  <link rel="stylesheet" href="/builders/primer.css?v=2026-07-02-shell1">
14
14
  <link rel="stylesheet" href="/builders/diagram-viewer.css?v=2026-11-04-reading-room">
@@ -111,10 +111,10 @@
111
111
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
112
112
 
113
113
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
114
- <script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
114
+ <script src="/builders/shell-game.js?v=2026-11-10-options"></script>
115
115
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
116
- <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
117
- <script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
116
+ <script src="/builders/craft-mode.js?v=2026-11-10-visor"></script>
117
+ <script src="/builders/shell.js?v=2026-11-10-character"></script>
118
118
  <script src="/builders/hall-dialog.js"></script>
119
119
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
120
120
  <script src="/builders/palette.js" defer></script>
@@ -126,7 +126,7 @@
126
126
  kit went with the treasury to the money page (task 1005047). -->
127
127
  <script src="/builders/primer-live.js?v=2026-11-04-money" defer></script>
128
128
  <script src="/builders/primer.js?v=2026-11-04-money" defer></script>
129
- <script src="/builders/diagram-viewer.js?v=2026-11-04-reading-room" defer></script>
129
+ <script src="/builders/diagram-viewer.js?v=2026-11-10-options" defer></script>
130
130
  <script src="/builders/copy-on-click.js?v=2026-06-01-copy-1" defer></script>
131
131
  </body>
132
132
  </html>
@@ -2,7 +2,7 @@
2
2
  // Artist tabs draw (task 1004435 / WA6.RP05, goal 1000095, criterion
3
3
  // wa6-roles-read-true-in-public).
4
4
  //
5
- // WHY. The Ideator tab listed tasks whose discipline happened to be 'ideator',
5
+ // WHY. The Ideator tab listed tasks whose discipline happened to be 'artist',
6
6
  // as if an ideator's record were engineering work; the Artist tab listed only
7
7
  // artist-labelled tasks the person had shipped themselves. Neither is that
8
8
  // craft's record. The owner-approved mock (docs/design/mocks/profile-roles/,