@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
@@ -1,7 +1,7 @@
1
1
  // tests/craft_review_mode.mjs — artist review mode (task 1004686, goal 1000095).
2
2
  //
3
- // Owner ask, 2026-10-09: the artist picks one of the four experiences (Ideator,
4
- // Governing, Building, Artistry) and reviews the hall in that mode's view, with
3
+ // Owner ask, 2026-10-09: the artist picks one of the experiences (since task 1005141
4
+ // the three: Patron, Artisan, Artist — Ideator folded into Artist) and reviews the hall in that mode's view, with
5
5
  // graffiti across the top bar's empty space while it lasts. Since task 1004771
6
6
  // the choice lives in the docked review panel's own Review mode row
7
7
  // (page-notes.js; tests/page_notes_panel.mjs runs that row), not a top-bar menu. These cases run the REAL
@@ -97,30 +97,30 @@ function boot({ session = {}, store = {}, narrow = false } = {}) {
97
97
  tools: () => all(side).filter((n) => n.hasAttribute('data-craft-tool')).map((n) => n.id.replace('craft-tool-', '')),
98
98
  };
99
99
  }
100
- const artist = () => ({ 'hall-craft': 'artistry', 'hall-craft-asked': '1' });
100
+ const artist = () => ({ 'hall-craft': 'artist', 'hall-craft-asked': '1' });
101
101
 
102
- test('NO TOP-BAR REVIEW BUTTON: in Artistry the bar holds Edit words alone, and no review-mode menu', async () => {
102
+ test('NO TOP-BAR REVIEW BUTTON: in Artist the bar holds Edit words and Ideas, and no review-mode menu', async () => {
103
103
  const p = boot({ session: artist() });
104
104
  await flush();
105
- assert.deepEqual(p.tools(), ['paintbrush'], 'the review panel docks at the edge instead (task 1004771)');
105
+ assert.deepEqual(p.tools(), ['paintbrush', 'lightbulb'], 'the review panel docks at the edge instead (task 1004771); the lightbulb joined from Ideating (task 1005141)');
106
106
  assert.equal(p.byId('craft-review'), null, 'no split pill');
107
107
  assert.equal(p.byId('craft-review-menu'), null, 'no menu');
108
108
  assert.deepEqual(p.opened, ['dock']);
109
109
  assert.equal(p.byId('craft-graffiti'), null, 'no wall until a review starts');
110
110
  assert.equal(p.root.getAttribute('data-craft-review'), null);
111
- assert.deepEqual(JSON.parse(JSON.stringify(p.craft.experiences().map((x) => x.label))), ['Ideator experience', 'Governing experience', 'Building experience', 'Artistry experience'], 'the four, in the owner\'s order and words, for the review panel row');
111
+ assert.deepEqual(JSON.parse(JSON.stringify(p.craft.experiences().map((x) => x.label))), ['Patron experience', 'Artisan experience', 'Artist experience'], 'the three, in the owner\'s order (Ideator folded into Artist), for the review panel row');
112
112
  });
113
113
 
114
114
  test('PICKING an experience: the hall wears that mode, the artist keeps the Review tools, and the wall goes up', async () => {
115
115
  const p = boot({ session: artist() });
116
- p.craft.review('governing');
117
- assert.equal(p.root.dataset.craft, 'governing', 'the hall wears Governing: its tabs, look and tallies follow');
118
- assert.equal(p.root.getAttribute('data-craft-review'), 'governing');
119
- assert.equal(p.craft.current(), 'governing');
120
- assert.equal(p.craft.reviewing(), 'governing');
121
- assert.equal(p.session['hall-craft'], 'artistry', 'the artist\'s own mode stays Artistry');
122
- assert.equal(p.session['hall-craft-review'], 'governing', 'the review lasts the session');
123
- assert.deepEqual(p.tools(), ['paintbrush'], 'Edit words stays in the bar');
116
+ p.craft.review('patron');
117
+ assert.equal(p.root.dataset.craft, 'patron', 'the hall wears Patron: its tabs, look and tallies follow');
118
+ assert.equal(p.root.getAttribute('data-craft-review'), 'patron');
119
+ assert.equal(p.craft.current(), 'patron');
120
+ assert.equal(p.craft.reviewing(), 'patron');
121
+ assert.equal(p.session['hall-craft'], 'artist', 'the artist\'s own mode stays Artist');
122
+ assert.equal(p.session['hall-craft-review'], 'patron', 'the review lasts the session');
123
+ assert.deepEqual(p.tools(), ['paintbrush', 'lightbulb'], 'the artist\'s tools stay in the bar');
124
124
  await flush();
125
125
  assert.ok(p.opened.filter((x) => x === 'dock').length >= 2, 'the docked panel is told again, so its mode row repaints');
126
126
  const wall = p.byId('craft-graffiti');
@@ -135,50 +135,55 @@ test('PICKING an experience: the hall wears that mode, the artist keeps the Revi
135
135
  });
136
136
 
137
137
  test('IT LASTS THE SESSION: a reload keeps the review; a new session starts without one', () => {
138
- const session = { ...artist(), 'hall-craft-review': 'ideating' };
138
+ const session = { ...artist(), 'hall-craft-review': 'artisan' };
139
139
  const p = boot({ session });
140
- assert.equal(p.root.dataset.craft, 'ideating', 'a reload wears the experience under review');
141
- assert.match(p.side.innerHTML, /data-craft-opt="ideating" aria-pressed="true"/, 'the switch shows the experience being worn');
140
+ assert.equal(p.root.dataset.craft, 'artisan', 'a reload wears the experience under review');
141
+ assert.match(p.side.innerHTML, /data-craft-opt="artisan" aria-pressed="true"/, 'the switch shows the experience being worn');
142
142
  assert.ok(p.byId('craft-graffiti'), 'the wall goes straight back up');
143
- const fresh = boot({ session: {}, store: { 'hall-craft-keep': 'artistry' } });
144
- assert.equal(fresh.root.dataset.craft, 'artistry', 'a new session is the artist\'s own Artistry');
143
+ const fresh = boot({ session: {}, store: { 'hall-craft-keep': 'artist' } });
144
+ assert.equal(fresh.root.dataset.craft, 'artist', 'a new session is the artist\'s own Artist');
145
145
  assert.equal(fresh.byId('craft-graffiti'), null);
146
- const notArtist = boot({ session: { 'hall-craft': 'building', 'hall-craft-asked': '1', 'hall-craft-review': 'governing' } });
147
- assert.equal(notArtist.root.dataset.craft, 'building', 'a stored review outside Artistry is ignored: only an artist reviews');
146
+ const notArtist = boot({ session: { 'hall-craft': 'artisan', 'hall-craft-asked': '1', 'hall-craft-review': 'patron' } });
147
+ assert.equal(notArtist.root.dataset.craft, 'artisan', 'a stored review outside Artist is ignored: only an artist reviews');
148
148
  assert.equal(notArtist.craft.reviewing(), null);
149
149
  const junk = boot({ session: { ...artist(), 'hall-craft-review': 'legacy' } });
150
- assert.equal(junk.root.dataset.craft, 'artistry', 'a review that is not one of the four reads as none');
150
+ assert.equal(junk.root.dataset.craft, 'artist', 'a review that is not one of the three reads as none');
151
151
  });
152
152
 
153
- test('REVIEWING Ideating shows its own tool too, marked a guest so a tiny phone can fold it away', () => {
153
+ // Ideating folded into Artist (task 1005141): the lightbulb is the artist's OWN tool now,
154
+ // so reviewing another experience brings no guest — Patron and Artisan carry no tool.
155
+ test('REVIEWING another experience keeps the artist\'s own tools, the lightbulb among them, and none is a guest', () => {
154
156
  const p = boot({ session: artist() });
155
- p.craft.review('ideating');
156
- assert.deepEqual(p.tools(), ['paintbrush', 'lightbulb'], 'the artist\'s tool, then the experience\'s own, in order');
157
- assert.ok(p.byId('craft-tool-lightbulb').classList.contains('craft-tool--guest'));
158
- assert.ok(!p.byId('craft-tool-paintbrush').classList.contains('craft-tool--guest'));
159
- p.craft.choose('ideating');
160
- assert.deepEqual(p.tools(), ['lightbulb'], 'choosing Ideating for real drops the artist\'s tools');
161
- assert.ok(!p.byId('craft-tool-lightbulb').classList.contains('craft-tool--guest'), 'and the lightbulb is no guest any more');
157
+ for (const x of ['patron', 'artisan']) {
158
+ p.craft.review(x);
159
+ assert.deepEqual(p.tools(), ['paintbrush', 'lightbulb'], `reviewing ${x}: the artist's tools, in order`);
160
+ for (const t of ['paintbrush', 'lightbulb']) assert.ok(!p.byId('craft-tool-' + t).classList.contains('craft-tool--guest'), `${t} is the artist's own, never a guest`);
161
+ }
162
+ p.craft.choose('artisan');
163
+ assert.deepEqual(p.tools(), [], 'choosing Artisan for real drops the artist\'s tools');
164
+ p.craft.choose('artist');
165
+ assert.deepEqual(p.tools(), ['paintbrush', 'lightbulb'], 'and Artist brings both back');
166
+ assert.ok(!p.byId('craft-tool-lightbulb').classList.contains('craft-tool--guest'));
162
167
  });
163
168
 
164
169
  test('ENDING it: Stop reviewing, or any mode from the switch, puts the artist back and takes the wall down', () => {
165
170
  const p = boot({ session: artist() });
166
- p.craft.review('building');
167
- assert.equal(p.root.dataset.craft, 'building');
171
+ p.craft.review('artisan');
172
+ assert.equal(p.root.dataset.craft, 'artisan');
168
173
  p.craft.stopReview();
169
- assert.equal(p.root.dataset.craft, 'artistry', 'Stop reviewing is back in Artistry');
174
+ assert.equal(p.root.dataset.craft, 'artist', 'Stop reviewing is back in Artist');
170
175
  assert.equal(p.root.getAttribute('data-craft-review'), null);
171
176
  assert.equal(p.byId('craft-graffiti'), null, 'the wall comes down');
172
177
  assert.equal(p.session['hall-craft-review'], undefined, 'and the session forgets it');
173
- p.craft.review('governing');
174
- p.craft.choose('artistry');
175
- assert.equal(p.craft.reviewing(), null, 'choosing a mode from the switch, even Artistry, ends a review');
178
+ p.craft.review('patron');
179
+ p.craft.choose('artist');
180
+ assert.equal(p.craft.reviewing(), null, 'choosing a mode from the switch, even Artist, ends a review');
176
181
  assert.equal(p.byId('craft-graffiti'), null);
177
- p.craft.review('governing');
178
- p.craft.choose('governing');
179
- assert.equal(p.root.dataset.craft, 'governing');
180
- assert.equal(p.root.getAttribute('data-craft-review'), null, 'Governing chosen for real is not a review');
181
- assert.ok(p.opened.includes('close'), 'and the review panel leaves with Artistry');
182
+ p.craft.review('patron');
183
+ p.craft.choose('patron');
184
+ assert.equal(p.root.dataset.craft, 'patron');
185
+ assert.equal(p.root.getAttribute('data-craft-review'), null, 'Patron chosen for real is not a review');
186
+ assert.ok(p.opened.includes('close'), 'and the review panel leaves with Artist');
182
187
  });
183
188
 
184
189
  test('THE WALL is scribbled in CSS only, in ink alone, and stands still under reduced motion', () => {
@@ -0,0 +1,70 @@
1
+ // tests/crafts_vocabulary.mjs
2
+ //
3
+ // Unit test for src/crafts.js (task 1005141, BONGOS-V3 C1) — the ONE place the
4
+ // craft vocabulary lives: artisan (was engineer), artist (absorbed ideator) and
5
+ // patron, plus normalizeCraft() that every input boundary runs an old spelling
6
+ // through. Pure, no database. Run: node tests/crafts_vocabulary.mjs
7
+
8
+ import { strict as assert } from 'node:assert';
9
+ import { createRequire } from 'node:module';
10
+
11
+ const require = createRequire(import.meta.url);
12
+ const crafts = require('../src/crafts.js');
13
+ const { normalizeCraft, normalizeCrafts } = crafts;
14
+
15
+ let passed = 0;
16
+ let failed = 0;
17
+ function test(name, fn) {
18
+ try { fn(); passed++; console.log(` ok ${name}`); }
19
+ catch (err) { failed++; console.log(` FAIL ${name}`); console.log(` ${err.message}`); }
20
+ }
21
+
22
+ test('the module exports only the two functions callers use (knip ratchet)', () => {
23
+ assert.deepEqual(Object.keys(crafts).sort(), ['normalizeCraft', 'normalizeCrafts']);
24
+ });
25
+
26
+ test('normalizeCraft maps every old spelling to today\'s', () => {
27
+ assert.equal(normalizeCraft('engineer'), 'artisan');
28
+ assert.equal(normalizeCraft('builder'), 'artisan', 'the old help-request spelling');
29
+ assert.equal(normalizeCraft('building'), 'artisan', 'the old hall craft-mode id');
30
+ assert.equal(normalizeCraft('ideator'), 'artist', 'ideation folded into artist');
31
+ assert.equal(normalizeCraft('ideating'), 'artist');
32
+ assert.equal(normalizeCraft('artistry'), 'artist');
33
+ assert.equal(normalizeCraft('governing'), 'patron');
34
+ });
35
+
36
+ test('normalizeCraft leaves today\'s words alone and is case/space tolerant', () => {
37
+ for (const c of ['artisan', 'artist', 'patron', 'ui']) assert.equal(normalizeCraft(c), c);
38
+ assert.equal(normalizeCraft(' Engineer '), 'artisan');
39
+ assert.equal(normalizeCraft('IDEATOR'), 'artist');
40
+ });
41
+
42
+ test('an unknown word passes through, so a module-contributed craft is never eaten', () => {
43
+ assert.equal(normalizeCraft('wizard'), 'wizard');
44
+ assert.equal(normalizeCraft('sound-design'), 'sound-design');
45
+ // Inherited Object keys are not legacy spellings.
46
+ assert.equal(normalizeCraft('constructor'), 'constructor');
47
+ assert.equal(normalizeCraft('toString'), 'tostring');
48
+ });
49
+
50
+ test('non-strings pass through untouched for the caller to validate', () => {
51
+ assert.equal(normalizeCraft(null), null);
52
+ assert.equal(normalizeCraft(undefined), undefined);
53
+ assert.equal(normalizeCraft(7), 7);
54
+ });
55
+
56
+ test('normalizeCrafts dedupes the fold in first-seen order', () => {
57
+ assert.deepEqual(normalizeCrafts(['artist', 'ideator']), ['artist'], 'one craft now, not two');
58
+ assert.deepEqual(normalizeCrafts(['ideator', 'engineer', 'artist']), ['artist', 'artisan']);
59
+ assert.deepEqual(normalizeCrafts(['engineer', 'ui', 'artisan']), ['artisan', 'ui']);
60
+ assert.deepEqual(normalizeCrafts([]), []);
61
+ assert.equal(normalizeCrafts(null), null, 'a non-array passes through');
62
+ });
63
+
64
+ test('the doorway exposes the same module', () => {
65
+ const api = require('../src/module-api.js');
66
+ assert.equal(api.crafts, crafts);
67
+ });
68
+
69
+ console.log(`\n${passed} passed, ${failed} failed`);
70
+ process.exit(failed ? 1 : 0);
@@ -40,13 +40,13 @@ const eachImpl = (rows, prefs, limit, check) => {
40
40
  for (const [name, out] of bothRank(rows, prefs, limit)) check(out, name);
41
41
  };
42
42
 
43
- // A claimable row. discipline defaults OUTSIDE the caller's prefs (['engineer'])
43
+ // A claimable row. discipline defaults OUTSIDE the caller's prefs (['artisan'])
44
44
  // so the partition step never eats the fixture; credits/minutes are equal by
45
45
  // default so priority is the only live sort key unless a test says otherwise.
46
46
  const row = (id, priority, extra = {}) => ({
47
47
  id, priority, discipline: 'design', credits_reward: 10, est_minutes: 10, ...extra,
48
48
  });
49
- const PREFS = ['engineer'];
49
+ const PREFS = ['artisan'];
50
50
  const ids = (out) => out.map((t) => t.id);
51
51
 
52
52
  const { test, summary } = makeRunner();
@@ -129,7 +129,7 @@ await test('shorter est_minutes, then id, break the remaining ties', () => {
129
129
 
130
130
  await test('partitioning is unchanged: outside-prefs only, no unclassified, empty prefs → []', () => {
131
131
  const rows = [
132
- row('mine', 1, { discipline: 'engineer' }),
132
+ row('mine', 1, { discipline: 'artisan' }),
133
133
  row('untriaged', 1, { discipline: 'unclassified' }),
134
134
  row('theirs', 4, { discipline: 'design' }),
135
135
  ];
@@ -142,7 +142,7 @@ await test('partitioning is unchanged: outside-prefs only, no unclassified, empt
142
142
  });
143
143
 
144
144
  await test('the two copies agree row-for-row on a mixed corpus', () => {
145
- const disciplines = ['design', 'ops', 'research', 'engineer', 'unclassified'];
145
+ const disciplines = ['design', 'ops', 'research', 'artisan', 'unclassified'];
146
146
  const priorities = [1, 2, 3, 4, 5, null, undefined, '', '2', 'junk'];
147
147
  const rows = [];
148
148
  for (let i = 0; i < 60; i++) {
@@ -152,7 +152,7 @@ await test('the two copies agree row-for-row on a mixed corpus', () => {
152
152
  est_minutes: (i % 4) * 15,
153
153
  }));
154
154
  }
155
- const [[, fromCanonical], [, fromMirror]] = bothRank(rows, ['engineer'], 25);
155
+ const [[, fromCanonical], [, fromMirror]] = bothRank(rows, ['artisan'], 25);
156
156
  assert.ok(fromCanonical.length > 5, 'fixture must actually exercise the ranking');
157
157
  assert.deepEqual(ids(fromMirror), ids(fromCanonical),
158
158
  'start.js is a hand-copied mirror — it must produce the identical order');
package/tests/fitness.mjs CHANGED
@@ -150,18 +150,16 @@ test('role pack guard actually CATCHES a missing pack, a pointerless entry, and
150
150
 
151
151
  // (1) the silent runtime failure: a pack path that resolves to nothing.
152
152
  const missing = rolePacks.checkRolePacks(withRegistry({
153
- engineer: { pack: 'docs/packs/__gone__.md', directive: 'x' },
153
+ artisan: { pack: 'docs/packs/__gone__.md', directive: 'x' },
154
154
  artist: { pack: 'docs/packs/artist.md', directive: 'x' },
155
- ideator: { pack: 'docs/packs/ideator.md', directive: 'x' },
156
155
  }));
157
156
  assert.equal(missing.hardFail, true, 'a pack that is not in the tree must hard-fail');
158
157
  assert.ok(missing.violations.some((v) => /__gone__/.test(v) && /not in the tree/.test(v)), 'the violation must name the unresolvable path');
159
158
 
160
159
  // (2) an entry routing a claim to no playbook at all.
161
160
  const pointerless = rolePacks.checkRolePacks(withRegistry({
162
- engineer: { pack: 'docs/packs/engineer.md', directive: 'x' },
161
+ artisan: { pack: 'docs/packs/artisan.md', directive: 'x' },
163
162
  artist: { pack: 'docs/packs/artist.md', directive: 'x' },
164
- ideator: { pack: 'docs/packs/ideator.md', directive: 'x' },
165
163
  ui: { directive: 'x' },
166
164
  }));
167
165
  assert.equal(pointerless.hardFail, true, 'an entry with neither pack nor skill must hard-fail');
@@ -169,21 +167,19 @@ test('role pack guard actually CATCHES a missing pack, a pointerless entry, and
169
167
 
170
168
  // (3) a craft quietly dropped back into the kernel — the split's own shape.
171
169
  const short = rolePacks.checkRolePacks(withRegistry({
172
- engineer: { pack: 'docs/packs/engineer.md', directive: 'x' },
173
- artist: { pack: 'docs/packs/artist.md', directive: 'x' },
174
- ideator: { skill: 'ideate', directive: 'x' },
170
+ artisan: { pack: 'docs/packs/artisan.md', directive: 'x' },
171
+ artist: { skill: 'ideate', directive: 'x' },
175
172
  }));
176
- assert.equal(short.hardFail, true, 'fewer than three packs must hard-fail');
177
- assert.ok(short.violations.some((v) => /only 2 craft/.test(v)));
173
+ assert.equal(short.hardFail, true, 'fewer than two packs must hard-fail (task 1005141: Artisan + Artist)');
174
+ assert.ok(short.violations.some((v) => /only 1 craft/.test(v)));
178
175
 
179
176
  // (4) an over-budget pack — the "CI budgets each pack" half of the criterion.
180
177
  const fat = rolePacks.checkRolePacks({
181
178
  readFileSync: (p, e) => {
182
179
  if (String(p).includes('discipline-modes.json')) {
183
180
  return JSON.stringify({ modes: {
184
- engineer: { pack: 'docs/packs/engineer.md', directive: 'x' },
181
+ artisan: { pack: 'docs/packs/artisan.md', directive: 'x' },
185
182
  artist: { pack: 'docs/packs/artist.md', directive: 'x' },
186
- ideator: { pack: 'docs/packs/ideator.md', directive: 'x' },
187
183
  } });
188
184
  }
189
185
  return 'x'.repeat(rolePacks.PACK_CHAR_HARD_MAX + 1);
@@ -89,7 +89,7 @@ try {
89
89
  await q(`INSERT INTO builders (id,github_id,github_login,display_name,rank) VALUES ($1,$2,$3,'M07 Owner','archon'), ($4,$5,$6,'Ada','archon')`,
90
90
  [OWNER, `m07-o-${RUN}`, `m07-owner-${RUN}`, ADA, `m07-a-${RUN}`, `m07-ada-${RUN}`]);
91
91
  for (const [id, title] of [[TASK_A, 'Build the paid thing'], [TASK_B, 'Build the second thing'], [TASK_C, 'Build the last thing']]) {
92
- await q(`INSERT INTO tasks (id,version_id,title,status,credits_reward,kind,discipline) VALUES ($1,$2,$3,'ready',10,'feature','engineer')`, [id, VER, title]);
92
+ await q(`INSERT INTO tasks (id,version_id,title,status,credits_reward,kind,discipline) VALUES ($1,$2,$3,'ready',10,'feature','artisan')`, [id, VER, title]);
93
93
  }
94
94
  await cash(TASK_A, '100'); await cash(TASK_B, '80'); await cash(TASK_C, '40');
95
95
  await settings.setSetting(WALL_KEY, 'hold_at_claim', OWNER);
@@ -37,7 +37,7 @@ test('genesis files a task per stage, keyed by a ref that names the step, not it
37
37
  assert.equal(plain[s].length, 1);
38
38
  assert.equal(plain[s][0].ref, `genesis:${s}`);
39
39
  assert.equal(plain[s][0].kind, 'decision');
40
- assert.equal(plain[s][0].discipline, 'ideator');
40
+ assert.equal(plain[s][0].discipline, 'artist');
41
41
  }
42
42
  const existing = genesisChecklist({ existing: true });
43
43
  assert.deepEqual(existing.scope.map((t) => t.ref), ['genesis:scope:existing', 'genesis:scope'],
@@ -229,6 +229,26 @@ test('decide → five stages through the Board Room → alive, and the hall open
229
229
  assert.equal((await gh.startFounding(1, w.deps())).reason, 'already_alive', 'genesis happens once');
230
230
  });
231
231
 
232
+ test('the owner can skip genesis from any stage: no sitting, hall opens, recorded once', async () => {
233
+ const w = world();
234
+ assert.equal((await gh.skipGenesis(1, w.deps())).reason, 'not_founding', 'nothing to skip on a project that is not founding');
235
+ await gh.startFounding(1, w.deps());
236
+ const r = await gh.skipGenesis(1, w.deps());
237
+ assert.deepEqual(r, { ok: true, skipped: true, from_stage: 'decide' }, 'skippable while still deciding, no plan needed');
238
+ assert.equal(await gh.readGenesis(w.deps()), null, 'founding mode is off: the full hall opens');
239
+ assert.ok(w.store.get(fm.KEYS.ENDED_AT), 'the founding window closed');
240
+ assert.equal(JSON.parse(w.store.get(fm.KEYS.SKIPPED_AT)).stage, 'decide', 'the record says it was skipped, and from where');
241
+ assert.equal(w.opened.length, 0, 'the Board Room was never asked');
242
+ assert.equal((await gh.skipGenesis(1, w.deps())).reason, 'already_alive', 'genesis happens once');
243
+ assert.equal((await gh.startFounding(1, w.deps())).reason, 'already_alive');
244
+
245
+ const mid = world();
246
+ await gh.startFounding(1, mid.deps());
247
+ await gh.savePlan({}, 1, mid.deps());
248
+ assert.equal((await gh.skipGenesis(1, mid.deps())).from_stage, 'scope', 'skippable mid-stage with unfinished tasks');
249
+ assert.equal(await gh.readGenesis(mid.deps()), null);
250
+ });
251
+
232
252
  test('only the stage in progress can be put to the board', async () => {
233
253
  const w = world();
234
254
  await gh.startFounding(1, w.deps());
@@ -29,8 +29,8 @@ const BAR = read(path.join(LANDING, 'assets', 'hub-bar.js'));
29
29
  const HUB = read(path.join(LANDING, 'projects.html'));
30
30
  const SHELL = read(path.join(HALL, 'shell.js'));
31
31
  const chip = require(path.join(HALL, 'github-chip.js'));
32
- // the engineer's glyph IS the Building craft's (craft-mode.js ICONS.building)
33
- const BUILDING = /building: '<path d="([^"]+)"\/>'/.exec(read(path.join(HALL, 'craft-mode.js')))[1];
32
+ // the artisan's glyph IS the Artisan craft's (craft-mode.js ICONS.artisan; Building until task 1005141)
33
+ const BUILDING = /artisan: '<path d="([^"]+)"\/>'/.exec(read(path.join(HALL, 'craft-mode.js')))[1];
34
34
 
35
35
  // ── a DOM just big enough for the three copies ──────────────────────────────
36
36
  class El {
@@ -104,7 +104,7 @@ test('the hall, executed: Google only draws one small link with the engineer gly
104
104
  assert.equal(a.href, 'https://example.com/api/bongos/auth/link/github/start');
105
105
  assert.equal(a.className, 'gh-chip');
106
106
  assert.equal(labelOf(a), 'Link GitHub');
107
- assert.equal(svgPath(a), BUILDING, 'the engineer craft’s own glyph');
107
+ assert.equal(svgPath(a), BUILDING, 'the artisan craft’s own glyph');
108
108
  assert.match(a.attrs['aria-label'], /coding/);
109
109
  });
110
110