@bongos/core 1.24.79 → 1.24.81

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 (413) hide show
  1. package/.bongos-core.json +554 -394
  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/0402-helm-is-the-halls-look-and-the-hall-is-dark-only.md +142 -0
  17. package/docs/adr/README.md +4 -0
  18. package/docs/api/openapi.json +195 -3
  19. package/docs/api-reference.md +8 -4
  20. package/docs/architecture.md +7 -3
  21. package/docs/copy-inventory.md +393 -378
  22. package/docs/copy-registry.json +722 -578
  23. package/docs/design/characters/character-sprites-source.webp +0 -0
  24. package/docs/file-map.md +7 -8
  25. package/docs/module-api-changelog.md +4 -0
  26. package/docs/modules-contract.md +5 -0
  27. package/docs/onboarding/slash-commands.md +1 -1
  28. package/docs/packs/{engineer.md → artisan.md} +5 -5
  29. package/docs/packs/artist.md +50 -4
  30. package/docs/page-inventory.json +16 -7
  31. package/docs/page-readings.json +3184 -3338
  32. package/docs/{packs/ideator.md → recipes/ideation-playbook.md} +5 -48
  33. package/migrations/core_296_crafts_artisan_artist.sql +72 -0
  34. package/migrations/core_297_builder_character.sql +30 -0
  35. package/modules/copy-desk/proposals.js +1 -1
  36. package/modules/economy/credits-by-craft.js +29 -15
  37. package/modules/hall-ui/public/ai-studio.html +6 -4
  38. package/modules/hall-ui/public/ai-studio.states.json +1 -0
  39. package/modules/hall-ui/public/atlas.html +5 -5
  40. package/modules/hall-ui/public/atlas.js +3 -1
  41. package/modules/hall-ui/public/atlas.states.json +1 -0
  42. package/modules/hall-ui/public/blockers.html +5 -5
  43. package/modules/hall-ui/public/board-room.html +4 -4
  44. package/modules/hall-ui/public/board-room.states.json +1 -0
  45. package/modules/hall-ui/public/characters/artisan.png +0 -0
  46. package/modules/hall-ui/public/characters/artist.png +0 -0
  47. package/modules/hall-ui/public/characters/patron.png +0 -0
  48. package/modules/hall-ui/public/city.css +3 -4
  49. package/modules/hall-ui/public/city.html +9 -9
  50. package/modules/hall-ui/public/city.js +17 -56
  51. package/modules/hall-ui/public/city.states.json +7 -32
  52. package/modules/hall-ui/public/collab.html +4 -4
  53. package/modules/hall-ui/public/collab.states.json +1 -0
  54. package/modules/hall-ui/public/commands.html +4 -4
  55. package/modules/hall-ui/public/commands.states.json +1 -0
  56. package/modules/hall-ui/public/controls.css +63 -11
  57. package/modules/hall-ui/public/copy-desk.html +4 -4
  58. package/modules/hall-ui/public/copy-desk.states.json +1 -0
  59. package/modules/hall-ui/public/craft-mode.js +258 -64
  60. package/modules/hall-ui/public/deploy.html +4 -4
  61. package/modules/hall-ui/public/deploy.states.json +1 -0
  62. package/modules/hall-ui/public/diagram-viewer.js +2 -1
  63. package/modules/hall-ui/public/dom-utils.js +1 -1
  64. package/modules/hall-ui/public/goal-map.html +9 -6
  65. package/modules/hall-ui/public/goals.html +5 -5
  66. package/modules/hall-ui/public/goals.states.json +1 -0
  67. package/modules/hall-ui/public/government.html +4 -4
  68. package/modules/hall-ui/public/government.states.json +1 -0
  69. package/modules/hall-ui/public/hall-render.js +3 -1
  70. package/modules/hall-ui/public/home-city-draw.js +3 -3
  71. package/modules/hall-ui/public/home-city.css +4 -0
  72. package/modules/hall-ui/public/home-city.js +17 -9
  73. package/modules/hall-ui/public/home-gallery.js +8 -1
  74. package/modules/hall-ui/public/home-idea-box.css +2 -2
  75. package/modules/hall-ui/public/home-idea-box.js +3 -3
  76. package/modules/hall-ui/public/idea.html +5 -5
  77. package/modules/hall-ui/public/idea.states.json +1 -0
  78. package/modules/hall-ui/public/ideas.html +5 -5
  79. package/modules/hall-ui/public/ideas.states.json +1 -0
  80. package/modules/hall-ui/public/index-founding.states.json +1 -0
  81. package/modules/hall-ui/public/index-genesis-decide.states.json +1 -0
  82. package/modules/hall-ui/public/index-genesis-demo.states.json +1 -0
  83. package/modules/hall-ui/public/index-genesis.states.json +1 -0
  84. package/modules/hall-ui/public/index.html +11 -7
  85. package/modules/hall-ui/public/index.states.json +1 -0
  86. package/modules/hall-ui/public/market-watch-item.html +4 -4
  87. package/modules/hall-ui/public/market-watch-item.states.json +1 -0
  88. package/modules/hall-ui/public/market-watch-rules.html +4 -4
  89. package/modules/hall-ui/public/market-watch-rules.states.json +1 -0
  90. package/modules/hall-ui/public/market-watch.html +6 -9
  91. package/modules/hall-ui/public/market-watch.js +22 -45
  92. package/modules/hall-ui/public/market-watch.states.json +1 -0
  93. package/modules/hall-ui/public/modules.html +4 -4
  94. package/modules/hall-ui/public/modules.states.json +1 -0
  95. package/modules/hall-ui/public/money.html +4 -4
  96. package/modules/hall-ui/public/money.states.json +1 -0
  97. package/modules/hall-ui/public/my-agents.html +4 -4
  98. package/modules/hall-ui/public/my-agents.states.json +1 -0
  99. package/modules/hall-ui/public/options-menu.js +409 -0
  100. package/modules/hall-ui/public/paintbrush.js +3 -3
  101. package/modules/hall-ui/public/palette.js +11 -13
  102. package/modules/hall-ui/public/primer.html +5 -5
  103. package/modules/hall-ui/public/primer.states.json +1 -0
  104. package/modules/hall-ui/public/profile-crafts.js +1 -1
  105. package/modules/hall-ui/public/profile-roles.js +23 -19
  106. package/modules/hall-ui/public/profile.css +1 -1
  107. package/modules/hall-ui/public/profile.html +19 -18
  108. package/modules/hall-ui/public/profile.js +19 -18
  109. package/modules/hall-ui/public/profile.states.json +5 -4
  110. package/modules/hall-ui/public/project-settings.html +4 -4
  111. package/modules/hall-ui/public/roster.html +4 -4
  112. package/modules/hall-ui/public/roster.js +3 -3
  113. package/modules/hall-ui/public/roster.states.json +1 -0
  114. package/modules/hall-ui/public/settings.css +24 -4
  115. package/modules/hall-ui/public/settings.html +21 -17
  116. package/modules/hall-ui/public/settings.js +44 -44
  117. package/modules/hall-ui/public/settings.states.json +1 -0
  118. package/modules/hall-ui/public/shell-game.css +110 -0
  119. package/modules/hall-ui/public/shell-game.js +316 -3
  120. package/modules/hall-ui/public/shell.js +15 -66
  121. package/modules/hall-ui/public/sky-interaction.js +7 -0
  122. package/modules/hall-ui/public/speciality-tools.js +1 -2
  123. package/modules/hall-ui/public/street-card.states.json +1 -0
  124. package/modules/hall-ui/public/street-inside.js +3 -4
  125. package/modules/hall-ui/public/street.css +1 -5
  126. package/modules/hall-ui/public/street.html +6 -10
  127. package/modules/hall-ui/public/street.js +8 -59
  128. package/modules/hall-ui/public/street.states.json +7 -32
  129. package/modules/hall-ui/public/studio.html +5 -5
  130. package/modules/hall-ui/public/studio.states.json +1 -0
  131. package/modules/hall-ui/public/style.css +197 -112
  132. package/modules/hall-ui/public/task-detail.js +5 -4
  133. package/modules/hall-ui/public/task.html +5 -5
  134. package/modules/hall-ui/public/task.states.json +1 -0
  135. package/modules/hall-ui/public/thinking.css +10 -0
  136. package/modules/hall-ui/public/thinking.html +9 -6
  137. package/modules/hall-ui/public/thinking.states.json +1 -0
  138. package/modules/hall-ui/public/tweak-editor.html +5 -5
  139. package/modules/hall-ui/public/tweak-editor.js +5 -1
  140. package/modules/hall-ui/public/tweak-editor.states.json +1 -0
  141. package/modules/hall-ui/public/update-pill.js +91 -2
  142. package/modules/hall-ui/public/visor.css +80 -0
  143. package/modules/hall-ui/public/visor.js +316 -0
  144. package/modules/hall-ui/public/watch-repo-invite.js +159 -0
  145. package/modules/hall-ui/public/watch.html +6 -5
  146. package/modules/hall-ui/public/watch.js +17 -2
  147. package/modules/hall-ui/public/watch.states.json +1 -0
  148. package/modules/hall-ui/public/welcome-boot.js +15 -6
  149. package/modules/hall-ui/public/work-founding.states.json +1 -0
  150. package/modules/hall-ui/public/work.html +5 -5
  151. package/modules/hall-ui/public/work.states.json +1 -0
  152. package/modules/hall-ui/records/builders-roster.md +2 -2
  153. package/modules/hall-ui/records/craft-mode.md +13 -11
  154. package/modules/hall-ui/records/game-mode.md +2 -2
  155. package/modules/hall-ui/records/governor-street.md +1 -1
  156. package/modules/hall-ui/records/home-city.md +3 -1
  157. package/modules/hall-ui/records/market-watch-settings.md +2 -0
  158. package/modules/hall-ui/records/options-menu.md +15 -0
  159. package/modules/hall-ui/records/profile-roles.md +11 -11
  160. package/modules/hall-ui/records/sky.md +3 -3
  161. package/modules/hall-ui/records/start-screen.md +25 -0
  162. package/modules/hall-ui/records/tweak-editor.md +1 -1
  163. package/modules/hall-ui/records/update-pill.md +12 -0
  164. package/modules/hall-ui/records/visor.md +23 -0
  165. package/modules/hall-ui/records/welcome-tower.md +5 -5
  166. package/modules/hall-ui/specimens/components.states.json +2 -1
  167. package/modules/ideas/ideator-stats.js +3 -1
  168. package/modules/ideas/inbox.js +2 -2
  169. package/modules/lifecycle/activity.js +1 -1
  170. package/modules/lifecycle/cascade.js +4 -4
  171. package/modules/lifecycle/goal-spec-task.js +1 -1
  172. package/modules/lifecycle/help-requests.js +19 -15
  173. package/modules/lifecycle/kickoff-checklist.js +3 -3
  174. package/modules/lifecycle/kickoff-seed.js +1 -1
  175. package/modules/lifecycle/migrations/lifecycle_021_crafts_artisan_artist.sql +29 -0
  176. package/modules/lifecycle/mingle.js +4 -4
  177. package/modules/lifecycle/page-tweak-write.js +1 -1
  178. package/modules/lifecycle/routes/task-write-routes.js +3 -1
  179. package/modules/lifecycle/routes/tasks.js +3 -3
  180. package/modules/lifecycle/ship-card.js +2 -3
  181. package/modules/lifecycle/task-classifier.js +39 -29
  182. package/modules/lifecycle/tests/cascade.mjs +7 -7
  183. package/modules/onboarding/founding-mode.js +3 -0
  184. package/modules/onboarding/genesis-home.js +21 -1
  185. package/modules/onboarding/routes/onboarding.js +2 -0
  186. package/modules/page-notes/notes.js +3 -2
  187. package/modules/page-notes/split.js +1 -1
  188. package/modules/platform-identity/craft-rollup.js +35 -7
  189. package/modules/platform-identity/migrations/platform_identity_036_crafts_artisan_artist.sql +45 -0
  190. package/modules/platform-identity/platform-identity.js +2 -2
  191. package/modules/platform-identity/routes/community-leaderboard.js +2 -1
  192. package/modules/platform-identity/routes/community-search.js +2 -1
  193. package/modules/platform-identity/routes/sso.js +2 -1
  194. package/modules/platform-identity/tests/platform-identity.mjs +6 -6
  195. package/modules/public-landing/public/community.html +3 -5
  196. package/modules/public-landing/public/index.html +1 -1
  197. package/modules/public-landing/public/projects.html +1 -1
  198. package/modules/specialities/migrations/specialities_004_crafts_artisan_artist.sql +30 -0
  199. package/modules/specialities/routes/specialities.js +2 -2
  200. package/modules/status-ui/public/status.js +1 -1
  201. package/modules/ui-design/kit/fixtures/community-leaderboard.json +10 -10
  202. package/modules/ui-design/kit/fixtures/community-search.json +2 -2
  203. package/modules/ui-design/kit/fixtures/scouting-recruiter.json +1 -1
  204. package/modules/ui-design/public/brand.html +2 -2
  205. package/package-lock.json +2 -2
  206. package/package.json +1 -1
  207. package/release-notes.json +76 -0
  208. package/scripts/bongos/autobongos-loop.js +1 -1
  209. package/scripts/bongos/discipline-modes.json +7 -12
  210. package/scripts/bongos/fitness.js +1 -1
  211. package/scripts/bongos/gen-hall-sitemap.js +7 -7
  212. package/scripts/bongos/grade-smoke.js +2 -2
  213. package/scripts/bongos/init.js +2 -2
  214. package/scripts/bongos/newcomer-floor.config.json +1 -2
  215. package/scripts/bongos/newcomer-floor.js +4 -3
  216. package/scripts/bongos/newcomer-floor.templates.json +10 -12
  217. package/scripts/bongos/newcomer-floor.test.js +47 -38
  218. package/scripts/bongos/role-pack-guard.js +5 -4
  219. package/scripts/bongos/seed-tasks.example.json +3 -3
  220. package/scripts/bongos/setup.js +20 -107
  221. package/scripts/bongos/start.js +2 -2
  222. package/scripts/bongos/task.js +5 -4
  223. package/scripts/bongos/triage.js +1 -1
  224. package/scripts/bongos/worktree.js +16 -4
  225. package/scripts/hall-preview/README.md +2 -3
  226. package/scripts/setup-builder.js +6 -46
  227. package/src/bongos/auth-admission.js +8 -8
  228. package/src/bongos/craft-mode.js +2 -0
  229. package/src/bongos/db.js +39 -1
  230. package/src/bongos/github-repos.js +78 -0
  231. package/src/bongos/module-scope-map.js +22 -0
  232. package/src/bongos/role-stats.js +27 -11
  233. package/src/bongos/routes/github.js +100 -2
  234. package/src/bongos/routes/me.js +33 -0
  235. package/src/bongos/serve-internal.js +33 -11
  236. package/src/crafts.js +66 -0
  237. package/src/hall-helm.js +79 -0
  238. package/src/module-api.js +6 -1
  239. package/src/module-loader/manifest-schema.js +16 -0
  240. package/src/modules.js +2 -2
  241. package/src/role-responsibilities.js +6 -5
  242. package/tests/acclaim_feed_evidence_db.mjs +1 -1
  243. package/tests/activity_rollup_as_of.mjs +1 -1
  244. package/tests/api_alias_caller_ratchet.mjs +3 -3
  245. package/tests/application_lifecycle.mjs +1 -1
  246. package/tests/artist_review.mjs +1 -1
  247. package/tests/artist_review_goal.mjs +1 -1
  248. package/tests/blocker_hall_live_proof.mjs +1 -1
  249. package/tests/blocker_tasks_named_db.mjs +2 -2
  250. package/tests/canonical_profile_url.mjs +1 -1
  251. package/tests/cap_newcomer_tasks.mjs +13 -13
  252. package/tests/character_creation.mjs +294 -0
  253. package/tests/claim_gate_open_blocker.mjs +2 -2
  254. package/tests/claim_gate_rebase.mjs +6 -6
  255. package/tests/claude_materialize.mjs +1 -1
  256. package/tests/clawback_confirmed_backlog.mjs +3 -3
  257. package/tests/collab_page.mjs +8 -9
  258. package/tests/collab_self_settling.mjs +2 -2
  259. package/tests/community_leaderboard_route.mjs +13 -5
  260. package/tests/community_proof_db.mjs +3 -0
  261. package/tests/community_search_route.mjs +7 -6
  262. package/tests/conductor.mjs +1 -1
  263. package/tests/conductor_main_e2e.mjs +8 -8
  264. package/tests/copy_desk_flags_db.mjs +1 -1
  265. package/tests/copy_desk_page_approve.mjs +2 -2
  266. package/tests/copy_desk_page_claim.mjs +2 -2
  267. package/tests/copy_desk_page_next.mjs +1 -1
  268. package/tests/copy_desk_page_release.mjs +1 -1
  269. package/tests/copy_desk_page_submit.mjs +2 -2
  270. package/tests/core_258_box_session_claims_db.mjs +1 -1
  271. package/tests/craft_mode.mjs +48 -34
  272. package/tests/craft_mode_home.mjs +20 -20
  273. package/tests/craft_mode_looks.mjs +16 -15
  274. package/tests/craft_mode_nav.mjs +31 -29
  275. package/tests/craft_review_mode.mjs +46 -41
  276. package/tests/crafts_vocabulary.mjs +70 -0
  277. package/tests/cross_discipline_priority_direction.mjs +5 -5
  278. package/tests/design_reference.mjs +1 -1
  279. package/tests/fitness.mjs +7 -11
  280. package/tests/funded_project_pays_proof_db.mjs +1 -1
  281. package/tests/genesis_home.mjs +21 -1
  282. package/tests/github_chip.mjs +3 -3
  283. package/tests/github_repo_invite.mjs +379 -0
  284. package/tests/goal_criterion_confirm_ui.mjs +1 -1
  285. package/tests/goal_invite_consent.mjs +1 -1
  286. package/tests/goal_map_page.mjs +2 -2
  287. package/tests/goal_sort_staleness.mjs +1 -1
  288. package/tests/goal_spec_task.mjs +1 -1
  289. package/tests/goal_spec_task_db.mjs +3 -3
  290. package/tests/grade_engine.mjs +2 -2
  291. package/tests/grader_no_artifact.mjs +1 -1
  292. package/tests/grader_prompt_fencing.mjs +1 -1
  293. package/tests/grader_prompt_window_bound.mjs +1 -1
  294. package/tests/grader_reader_lens.mjs +2 -2
  295. package/tests/grader_severity_gate.mjs +1 -1
  296. package/tests/grader_unbacked_score.mjs +1 -1
  297. package/tests/hall_approval_queue.mjs +1 -1
  298. package/tests/hall_board_world.mjs +2 -2
  299. package/tests/hall_builders_page_pass.mjs +3 -2
  300. package/tests/hall_city.mjs +16 -30
  301. package/tests/hall_controls.mjs +52 -4
  302. package/tests/hall_fullscreen.mjs +27 -3
  303. package/tests/hall_helm.mjs +187 -0
  304. package/tests/hall_kit_charts.mjs +1 -1
  305. package/tests/hall_landing_boot.mjs +1 -1
  306. package/tests/hall_my_agents.mjs +16 -16
  307. package/tests/hall_nav.mjs +22 -15
  308. package/tests/hall_options_menu.mjs +247 -0
  309. package/tests/hall_oversight_world.mjs +1 -1
  310. package/tests/hall_palette.mjs +8 -9
  311. package/tests/hall_profile_world.mjs +3 -3
  312. package/tests/hall_record_world.mjs +6 -4
  313. package/tests/hall_settings_world.mjs +2 -1
  314. package/tests/hall_sitemap.mjs +6 -5
  315. package/tests/hall_start_screen.mjs +307 -0
  316. package/tests/hall_street.mjs +15 -28
  317. package/tests/hall_street_inside.mjs +17 -9
  318. package/tests/hall_studio_world.mjs +3 -3
  319. package/tests/hall_topbar_320.mjs +4 -2
  320. package/tests/hall_visor.mjs +224 -0
  321. package/tests/help_requests.mjs +47 -15
  322. package/tests/helpers.mjs +4 -4
  323. package/tests/home_city.mjs +6 -4
  324. package/tests/home_city_widget.mjs +33 -14
  325. package/tests/home_gallery_widget.mjs +4 -4
  326. package/tests/home_idea_box.mjs +6 -6
  327. package/tests/hub_community_page.mjs +3 -3
  328. package/tests/hub_craft_rollup.mjs +75 -36
  329. package/tests/hub_recent_ships.mjs +3 -2
  330. package/tests/idea_cascade.mjs +2 -2
  331. package/tests/idea_credit_streams_db.mjs +1 -1
  332. package/tests/idea_detail_page.mjs +1 -1
  333. package/tests/idea_ideate_full.mjs +5 -2
  334. package/tests/idea_spark_hall.mjs +1 -1
  335. package/tests/init.mjs +3 -3
  336. package/tests/instance_manifest.mjs +2 -2
  337. package/tests/kickoff_checklist.mjs +1 -1
  338. package/tests/leaderboard_roles.mjs +34 -25
  339. package/tests/lifecycle_claim_ladder_refs.mjs +55 -0
  340. package/tests/lightbulb.mjs +14 -10
  341. package/tests/main_discipline.mjs +8 -8
  342. package/tests/markdown_line_endings.mjs +1 -1
  343. package/tests/market_watch_pages.mjs +52 -74
  344. package/tests/me_streak_leaderboard_queries.mjs +3 -3
  345. package/tests/migration_allocation.mjs +1 -1
  346. package/tests/mingle.mjs +36 -21
  347. package/tests/module-scope-map.mjs +22 -2
  348. package/tests/module_api.mjs +1 -0
  349. package/tests/module_contributions.mjs +13 -13
  350. package/tests/module_manifest.mjs +29 -0
  351. package/tests/module_skills_hook.mjs +2 -2
  352. package/tests/modules.mjs +14 -7
  353. package/tests/nav_permission_atoms.mjs +2 -1
  354. package/tests/needs_rebase.mjs +1 -1
  355. package/tests/newcomer_restock.mjs +13 -13
  356. package/tests/page_notes_panel.mjs +22 -22
  357. package/tests/page_tweak_no_block.mjs +1 -1
  358. package/tests/paintbrush.mjs +7 -7
  359. package/tests/privacy_matrix.mjs +1 -0
  360. package/tests/profile_activity.mjs +20 -23
  361. package/tests/profile_earnings.mjs +1 -1
  362. package/tests/profile_nudge_prefs.mjs +4 -4
  363. package/tests/profile_role_headline.mjs +73 -46
  364. package/tests/profile_role_stats.mjs +55 -45
  365. package/tests/profile_rollup_consent.mjs +19 -6
  366. package/tests/profile_route.mjs +12 -12
  367. package/tests/profile_ui_cross_project.mjs +4 -4
  368. package/tests/project_invite_suggestions.mjs +2 -2
  369. package/tests/promote_goal_id.mjs +1 -1
  370. package/tests/publish_manifest.mjs +1 -1
  371. package/tests/rank_floor_wiring.mjs +1 -1
  372. package/tests/regrade_actual_minutes.mjs +1 -1
  373. package/tests/removal_proposal.mjs +2 -2
  374. package/tests/reward_gate.mjs +4 -4
  375. package/tests/reward_mode_cost_plus_only.mjs +1 -1
  376. package/tests/role_responsibilities.mjs +18 -12
  377. package/tests/role_stats_db.mjs +17 -16
  378. package/tests/scouting_rollup_fanout.mjs +5 -5
  379. package/tests/seed_tasks.mjs +5 -2
  380. package/tests/session_earnings_mirror_db.mjs +1 -1
  381. package/tests/session_reward_on_land_db.mjs +1 -1
  382. package/tests/session_reward_reconcile_db.mjs +2 -2
  383. package/tests/sessions_kit_pager.mjs +1 -1
  384. package/tests/settings_art_key_absent.mjs +2 -2
  385. package/tests/settings_main_role.mjs +21 -20
  386. package/tests/setup_flow.mjs +3 -3
  387. package/tests/setup_smoke.mjs +20 -0
  388. package/tests/ship_card.mjs +6 -6
  389. package/tests/specialities.mjs +16 -4
  390. package/tests/speciality_db_shape.mjs +6 -6
  391. package/tests/speciality_injection.mjs +17 -17
  392. package/tests/speciality_offer_ui.mjs +5 -5
  393. package/tests/speciality_offered.mjs +6 -5
  394. package/tests/speciality_session_skills.mjs +7 -7
  395. package/tests/speciality_skills.mjs +11 -11
  396. package/tests/speciality_walkthrough_ui.mjs +2 -2
  397. package/tests/sso_checkin_identity_spoof.mjs +2 -1
  398. package/tests/task_classifier_vocab.mjs +10 -0
  399. package/tests/task_cli.mjs +11 -3
  400. package/tests/task_criteria_unlink_db.mjs +2 -2
  401. package/tests/task_discipline_required.mjs +38 -16
  402. package/tests/task_goal_required.mjs +4 -4
  403. package/tests/task_needs_db.mjs +1 -1
  404. package/tests/tweak_replaces_artist_review.mjs +1 -1
  405. package/tests/update_pill.mjs +153 -2
  406. package/tests/upkeep_home.mjs +1 -1
  407. package/tests/v3_c1_options_proof.mjs +145 -0
  408. package/tests/visual_review.mjs +3 -3
  409. package/tests/watch_applications_queue.mjs +8 -5
  410. package/tests/watch_sealed_floor.mjs +3 -1
  411. package/tests/welcome_boot.mjs +12 -9
  412. package/tests/welcome_boot_browser.mjs +13 -13
  413. package/tests/worktree_helper.mjs +16 -1
@@ -1,16 +1,16 @@
1
1
  {
2
- "_doc": "The evergreen newcomer chores, grouped BY DISCIPLINE. Each discipline maps to a LIST of chore templates (was a single object before task 1488 — a discipline can now offer several first-builder chores so newcomers are not funnelled into one). The deterministic replenisher (scripts/bongos/newcomer-floor.js) and the on-claim restock (modules/onboarding/newcomer-restock.js) stamp disposable claimable COPIES from each template; the TEMPLATE is never claimed and never ships. Each copy's source_ref is tmpl:<discipline>:<template_id>:<seq> (monotonic), so identical-title copies never collide with the dedup. Per-template `floor` sets that chore’s standing pool depth; if a template omits it, the discipline default in newcomer-floor.config.json applies. credits_reward, est_minutes and floor all live here (per chore). version_id is per-template so each chore lands on the correct track; it MUST point at an open (non-shipped) version — update it at version close, or the replenisher's open-version guard refuses to stamp and the floor silently stops refilling (task 1002958: the engineer pins still read the long-shipped GDS-V4 and artist+ideator read V2, a version this ladder never had, so NO discipline could restock). touches[] is intentionally empty so multiple same-discipline newcomers can claim copies concurrently; git-merge is the real collision detector. To ADD a chore: append an entry to the discipline's array. To CHANGE one: edit it here — no code change needed. UI offers two browser-free chores (a token-drift fix measured by validate-design.js, and an accessible-name fix) so a newcomer whose only craft is ui is never starved for work on a machine with no Chrome/Edge for the kit — task 1003487. Engineer offers a SAFE coding chore (add a unit test — touches only tests/, which auto-ships) and the docs-hygiene sweep (constrained to non-protected docs so a sub-Metic ship does not stall on the authority core).",
3
- "engineer": [
2
+ "_doc": "The evergreen newcomer chores, grouped BY DISCIPLINE. Each discipline maps to a LIST of chore templates (was a single object before task 1488 — a discipline can now offer several first-builder chores so newcomers are not funnelled into one). The deterministic replenisher (scripts/bongos/newcomer-floor.js) and the on-claim restock (modules/onboarding/newcomer-restock.js) stamp disposable claimable COPIES from each template; the TEMPLATE is never claimed and never ships. Each copy's source_ref is tmpl:<discipline>:<template_id>:<seq> (monotonic), so identical-title copies never collide with the dedup. Per-template `floor` sets that chore’s standing pool depth; if a template omits it, the discipline default in newcomer-floor.config.json applies. credits_reward, est_minutes and floor all live here (per chore). version_id is per-template so each chore lands on the correct track; it MUST point at an open (non-shipped) version — update it at version close, or the replenisher's open-version guard refuses to stamp and the floor silently stops refilling (task 1002958: the engineer pins still read the long-shipped GDS-V4 and artist+ideator read V2, a version this ladder never had, so NO discipline could restock). touches[] is intentionally empty so multiple same-discipline newcomers can claim copies concurrently; git-merge is the real collision detector. To ADD a chore: append an entry to the discipline's array. To CHANGE one: edit it here — no code change needed. UI offers two browser-free chores (a token-drift fix measured by validate-design.js, and an accessible-name fix) so a newcomer whose only craft is ui is never starved for work on a machine with no Chrome/Edge for the kit — task 1003487. Artisan offers a SAFE coding chore (add a unit test — touches only tests/, which auto-ships) and the docs-hygiene sweep (constrained to non-protected docs so a sub-Metic ship does not stall on the authority core).",
3
+ "artisan": [
4
4
  {
5
5
  "template_id": "unit-test-pass",
6
6
  "version_id": "BONGOS-V1",
7
- "discipline": "engineer",
7
+ "discipline": "artisan",
8
8
  "kind": "feature",
9
9
  "est_minutes": 45,
10
10
  "credits_reward": 15,
11
11
  "floor": 2,
12
12
  "title": "Add a unit test for an untested pure exported helper (assert its real behaviour)",
13
- "description": "## What you're doing\n\nThe repo's safety net is its **unit suite** — every `tests/*.mjs` file, run by `scripts/bongos/run-unit-tests.js` in CI on every PR. New pure tests are **auto-discovered** (there is no allowlist to edit), so the moment you add one it joins the gate. Your first ship as an engineer: pick ONE exported helper that has thin or no test coverage and add a focused, DB-free unit test for it.\n\nIt is identical for every newcomer and **always valuable** — coverage is never \"done\", the codebase has hundreds of small pure helpers, and a good test both guards behaviour and teaches you how a corner of the system works. It is also **safe to auto-ship**: you add ONE file under `tests/`, which is not a protected surface, so a clean test merges on green with no owner approval.\n\n## How to pick a target (a PURE helper)\n\nPick a **pure** function — deterministic, with no database, network, filesystem, or live-server dependency — so the test runs standalone. Do not hunt by hand: run the finder, which reads the live tree every time (so this task never goes stale the way a written list does):\n\n```\nnode scripts/bongos/find-untested.js\n```\n\nIt lists modules that **no test file names**, best candidates first — ones no test reaches at all, then ones that look pure, then the smallest. Its purity column is a heuristic: open the module and confirm the helper you pick really needs no database, network or files. If the list is empty, run it with `--all` and pick a module that still exports a helper you can test DB-free.\n\n## How to write it (mirror an existing test — there is no framework)\n\nThese tests are plain Node scripts using `node:assert`; there is no test runner. Open a small existing one as your model — for example `tests/criterion_ref_parse.mjs` or `tests/task_classifier_vocab.mjs` — and copy the shape:\n1. `import` / `require` the helper from its module.\n2. Assert **several** cases, including at least one **edge / boundary** case (empty input, null, a tricky value).\n3. Print a one-line success message at the end (an assert that throws exits non-zero, which is how the gate sees a failure).\n\nName the file `tests/<helper-or-module>.mjs`.\n\n## Verify + ship\n\nRun BOTH and confirm both are green:\n- `node tests/<your-file>.mjs` — your test alone, exits 0.\n- `node scripts/bongos/run-unit-tests.js` — the whole gate, with your test auto-included.\n\nThen `/builder-ship`. The ship notes must name the helper and file you tested.\n\n## Stay safe (off-limits)\n\nAdd exactly ONE new file under `tests/`. Do NOT edit `scripts/bongos/run-unit-tests.js` (it auto-discovers your test, and it is a protected file you may not touch), and do NOT change the helper you are testing. Your test must be **DB-free**: anything needing Postgres, the network, or a live server belongs in the integration gate and will fail standalone here. Touch no permission, pipeline, migration, or infra file.",
13
+ "description": "## What you're doing\n\nThe repo's safety net is its **unit suite** — every `tests/*.mjs` file, run by `scripts/bongos/run-unit-tests.js` in CI on every PR. New pure tests are **auto-discovered** (there is no allowlist to edit), so the moment you add one it joins the gate. Your first ship as an artisan: pick ONE exported helper that has thin or no test coverage and add a focused, DB-free unit test for it.\n\nIt is identical for every newcomer and **always valuable** — coverage is never \"done\", the codebase has hundreds of small pure helpers, and a good test both guards behaviour and teaches you how a corner of the system works. It is also **safe to auto-ship**: you add ONE file under `tests/`, which is not a protected surface, so a clean test merges on green with no owner approval.\n\n## How to pick a target (a PURE helper)\n\nPick a **pure** function — deterministic, with no database, network, filesystem, or live-server dependency — so the test runs standalone. Do not hunt by hand: run the finder, which reads the live tree every time (so this task never goes stale the way a written list does):\n\n```\nnode scripts/bongos/find-untested.js\n```\n\nIt lists modules that **no test file names**, best candidates first — ones no test reaches at all, then ones that look pure, then the smallest. Its purity column is a heuristic: open the module and confirm the helper you pick really needs no database, network or files. If the list is empty, run it with `--all` and pick a module that still exports a helper you can test DB-free.\n\n## How to write it (mirror an existing test — there is no framework)\n\nThese tests are plain Node scripts using `node:assert`; there is no test runner. Open a small existing one as your model — for example `tests/criterion_ref_parse.mjs` or `tests/task_classifier_vocab.mjs` — and copy the shape:\n1. `import` / `require` the helper from its module.\n2. Assert **several** cases, including at least one **edge / boundary** case (empty input, null, a tricky value).\n3. Print a one-line success message at the end (an assert that throws exits non-zero, which is how the gate sees a failure).\n\nName the file `tests/<helper-or-module>.mjs`.\n\n## Verify + ship\n\nRun BOTH and confirm both are green:\n- `node tests/<your-file>.mjs` — your test alone, exits 0.\n- `node scripts/bongos/run-unit-tests.js` — the whole gate, with your test auto-included.\n\nThen `/builder-ship`. The ship notes must name the helper and file you tested.\n\n## Stay safe (off-limits)\n\nAdd exactly ONE new file under `tests/`. Do NOT edit `scripts/bongos/run-unit-tests.js` (it auto-discovers your test, and it is a protected file you may not touch), and do NOT change the helper you are testing. Your test must be **DB-free**: anything needing Postgres, the network, or a live server belongs in the integration gate and will fail standalone here. Touch no permission, pipeline, migration, or infra file.",
14
14
  "done_when": [
15
15
  "A NEW tests/<name>.mjs file was added (it did not exist before) that unit-tests a PURE exported helper — no Postgres, network, filesystem, or live server.",
16
16
  "`node tests/<name>.mjs` exits 0 and the test makes real assertions about the helper's behaviour — at least two cases including one edge/boundary case — not a trivial always-true assertion.",
@@ -23,12 +23,12 @@
23
23
  {
24
24
  "template_id": "docs-hygiene-pass",
25
25
  "version_id": "BONGOS-V1",
26
- "discipline": "engineer",
26
+ "discipline": "artisan",
27
27
  "kind": "cleanup",
28
28
  "est_minutes": 40,
29
29
  "credits_reward": 15,
30
30
  "title": "Docs-hygiene sweep: fix one piece of real drift (broken link, dead anchor, or bare #NNN ref) in a Markdown doc",
31
- "description": "## What you're doing\n\nThe project's docs drift constantly — links break when files are renamed, heading anchors go dead, formatting breaks, and bare `#NNN` refs creep back (a bare `#123` autolinks to GitHub's issue space and 404s — see CLAUDE.md §8). Your job: find **one** genuine piece of drift in **one** SAFE doc and ship the fix.\n\n## First — do NOT open these (they will stall your ship)\n\nA newcomer (Xenos / Thetes, sub-Metic) may not modify the authority / pipeline core. Picking one of these as your target will **fail your grade or block your ship**, so don't even go looking for drift in them: `CLAUDE.md`, `docs/canonical-permissions.md`, the authority ADRs (`docs/adr/0016-*`, `docs/adr/0043-*`), and anything under `migrations/`, `infra/`, `.github/`, `.husky/`, `.claude/hooks/`, plus `package.json` and the ship / grade / deploy scripts. (The authoritative list is the glob set in `src/bongos/permission-path-check.js`.) The most *visible* drift often lives in `CLAUDE.md` itself — resist it; it is off-limits to you.\n\n## Pick from the SAFE docs only\n\nAll fair game: a recipe under `docs/recipes/`, a session log under `docs/session-logs/`, `docs/architecture.md`, `docs/file-map.md`, `docs/TRACKS.md`, a **non-authority** ADR, `README.md`, or `art/README.md`.\n\n## How to find a real target (don't ship a no-op)\n\n1. **Bare `#NNN` refs (easiest):** run `node scripts/bongos/linkify-refs.js` (read-only) — it lists docs with upgradeable refs. Pick one from the SAFE list above and fix it with `node scripts/bongos/linkify-refs.js --apply <safe-file.md>`.\n2. **Broken / renamed links:** open a safe doc and check its `](path)` links resolve to a real file (cross-check `docs/file-map.md`).\n3. **Dead anchors:** `](#heading)` where that heading no longer exists in the target doc.\n4. **Formatting drift:** a broken list nest, an unclosed code fence, a mangled table.\n\n## Verify + ship\n\nRun `node scripts/bongos/linkify-refs.js --check <your-file.md>` (scoped to YOUR one safe file — exit 0) and confirm any link you touched resolves, then `/builder-ship`. The ship notes must name the specific drift you fixed and the one file.\n\n_Prefer writing code to prose? Claim the \"add a unit test\" engineer chore instead — it's the other first-builder engineer task._",
31
+ "description": "## What you're doing\n\nThe project's docs drift constantly — links break when files are renamed, heading anchors go dead, formatting breaks, and bare `#NNN` refs creep back (a bare `#123` autolinks to GitHub's issue space and 404s — see CLAUDE.md §8). Your job: find **one** genuine piece of drift in **one** SAFE doc and ship the fix.\n\n## First — do NOT open these (they will stall your ship)\n\nA newcomer (Xenos / Thetes, sub-Metic) may not modify the authority / pipeline core. Picking one of these as your target will **fail your grade or block your ship**, so don't even go looking for drift in them: `CLAUDE.md`, `docs/canonical-permissions.md`, the authority ADRs (`docs/adr/0016-*`, `docs/adr/0043-*`), and anything under `migrations/`, `infra/`, `.github/`, `.husky/`, `.claude/hooks/`, plus `package.json` and the ship / grade / deploy scripts. (The authoritative list is the glob set in `src/bongos/permission-path-check.js`.) The most *visible* drift often lives in `CLAUDE.md` itself — resist it; it is off-limits to you.\n\n## Pick from the SAFE docs only\n\nAll fair game: a recipe under `docs/recipes/`, a session log under `docs/session-logs/`, `docs/architecture.md`, `docs/file-map.md`, `docs/TRACKS.md`, a **non-authority** ADR, `README.md`, or `art/README.md`.\n\n## How to find a real target (don't ship a no-op)\n\n1. **Bare `#NNN` refs (easiest):** run `node scripts/bongos/linkify-refs.js` (read-only) — it lists docs with upgradeable refs. Pick one from the SAFE list above and fix it with `node scripts/bongos/linkify-refs.js --apply <safe-file.md>`.\n2. **Broken / renamed links:** open a safe doc and check its `](path)` links resolve to a real file (cross-check `docs/file-map.md`).\n3. **Dead anchors:** `](#heading)` where that heading no longer exists in the target doc.\n4. **Formatting drift:** a broken list nest, an unclosed code fence, a mangled table.\n\n## Verify + ship\n\nRun `node scripts/bongos/linkify-refs.js --check <your-file.md>` (scoped to YOUR one safe file — exit 0) and confirm any link you touched resolves, then `/builder-ship`. The ship notes must name the specific drift you fixed and the one file.\n\n_Prefer writing code to prose? Claim the \"add a unit test\" artisan chore instead — it's the other first-builder artisan task._",
32
32
  "done_when": [
33
33
  "Exactly one non-protected Markdown file was changed (NOT CLAUDE.md, canonical-permissions.md, an authority ADR, or anything under migrations/ infra/ .github/ .husky/ .claude/hooks/ — see src/bongos/permission-path-check.js), and the change is a genuine hygiene fix (a broken/renamed link path, a dead heading anchor, a bare `#NNN` ref linkified, or formatting drift) — not a content/meaning edit and not a cosmetic no-op.",
34
34
  "`node scripts/bongos/linkify-refs.js --check <your-file.md>` (scoped to the one file you changed) exits 0.",
@@ -49,7 +49,7 @@
49
49
  "est_minutes": 55,
50
50
  "credits_reward": 15,
51
51
  "title": "Generate one new in-world art asset of your choice (a prop, plant, animal, or accessory) via the pixel-art pipeline",
52
- "description": "## What you're doing\n\nThis instance's world always needs more to look at. Your first ship as an artist is to **generate ONE new in-world asset of your own choosing** and add it to the world. You pick the subject — that's the fun part. Good candidates, all in-world (Mediterranean coast just south of Athens, mythic 'discovered secret' tone):\n- **Props**: amphora, fishing net, basket, crate, urn, torch, market awning, oar, clay pot.\n- **Plants**: olive tree, cypress, reeds, grapevine, shrub, wild herbs.\n- **Animals**: goat, chicken, duck, donkey, fish, gull, lizard.\n- **Accessories**: a sandal, a sun hat, a length of rope, a string of beads.\n\nIt is identical for every newcomer and **always valuable**, because a growing world has effectively unbounded demand for new props, flora, fauna, and detail — every newcomer picks a different subject, so two runs never collide.\n\n## How to find your way in\n\n1. Read `art/README.md` and `docs/recipes/pixel-art-pipeline.md`, plus `modules/art-pipeline/template/style_guide.md`, `modules/art-pipeline/template/rubric.json`, and `modules/art-pipeline/template/palette.json` — that's the look you must hit: 16-bit pixel art, early-Pokémon (FireRed) touchstone, top-down, 32×32, locked 64-colour Mediterranean palette (olive greens, warm tans, turquoise sea, weathered marble — NOT spring green, NOT navy, NOT pine).\n2. **Pick ONE subject.** Keep it to a SINGLE 32×32 tile/sprite — not a whole spritesheet or family (that's a bigger task).\n3. **Check the budget first**: `python3 modules/art-pipeline/pipeline/cost_ledger.py status` (the pipeline hard-stops at $80; one asset is ~$0.05–0.45).\n4. **Generate via the pipeline — do not hand-draw or freelance.** Use the `/otb-tile-generate` skill: it appends your new entry to `modules/art-pipeline/template/vocabulary.json`, runs the generate → post-process-to-32×32 → score-against-rubric → retry orchestrator, saves the final PNG to `modules/art-pipeline/generated/tiles/<id>.png`, and logs cost. (This needs the art pipeline's `GOOGLE_AI_STUDIO_KEY`, which resolves from `~/.config/otb/env`; most builder setups have it. If yours doesn't, claim the engineer or ideator newcomer chore instead, or ask an Archon to enable it.)\n5. **Confirm it PASSED** the rubric (`modules/art-pipeline/iterations/run_summary.json` shows `status: \"pass\"`). If it only reached best-effort, regenerate with the fix_hint — never paper over a fail.\n6. **See it in-game**: `node scripts/bongos/art-stage.js` rebuilds the atlas and surfaces it on your sandbox preview (`sandbox-<login>.<instance-apex>`).\n7. Ship via `/builder-ship`; the ship notes name your asset and its rubric score.\n\n## Stay clear of (READ-ONLY)\n\nNever modify the locked palette (`modules/art-pipeline/template/palette.json`) or the rubric (`modules/art-pipeline/template/rubric.json`) — those are theme-defining and changing them needs an ADR (out of scope for a first ship). Touch only art assets/vocabulary under `art/` (and, only if your asset is a genuinely new base-tile TYPE, the `public/game/world/tilePalette.js` mapping). Do not touch any permission/pipeline-core/migration/infra file.",
52
+ "description": "## What you're doing\n\nThis instance's world always needs more to look at. Your first ship as an artist is to **generate ONE new in-world asset of your own choosing** and add it to the world. You pick the subject — that's the fun part. Good candidates, all in-world (Mediterranean coast just south of Athens, mythic 'discovered secret' tone):\n- **Props**: amphora, fishing net, basket, crate, urn, torch, market awning, oar, clay pot.\n- **Plants**: olive tree, cypress, reeds, grapevine, shrub, wild herbs.\n- **Animals**: goat, chicken, duck, donkey, fish, gull, lizard.\n- **Accessories**: a sandal, a sun hat, a length of rope, a string of beads.\n\nIt is identical for every newcomer and **always valuable**, because a growing world has effectively unbounded demand for new props, flora, fauna, and detail — every newcomer picks a different subject, so two runs never collide.\n\n## How to find your way in\n\n1. Read `art/README.md` and `docs/recipes/pixel-art-pipeline.md`, plus `modules/art-pipeline/template/style_guide.md`, `modules/art-pipeline/template/rubric.json`, and `modules/art-pipeline/template/palette.json` — that's the look you must hit: 16-bit pixel art, early-Pokémon (FireRed) touchstone, top-down, 32×32, locked 64-colour Mediterranean palette (olive greens, warm tans, turquoise sea, weathered marble — NOT spring green, NOT navy, NOT pine).\n2. **Pick ONE subject.** Keep it to a SINGLE 32×32 tile/sprite — not a whole spritesheet or family (that's a bigger task).\n3. **Check the budget first**: `python3 modules/art-pipeline/pipeline/cost_ledger.py status` (the pipeline hard-stops at $80; one asset is ~$0.05–0.45).\n4. **Generate via the pipeline — do not hand-draw or freelance.** Use the `/otb-tile-generate` skill: it appends your new entry to `modules/art-pipeline/template/vocabulary.json`, runs the generate → post-process-to-32×32 → score-against-rubric → retry orchestrator, saves the final PNG to `modules/art-pipeline/generated/tiles/<id>.png`, and logs cost. (This needs the art pipeline's `GOOGLE_AI_STUDIO_KEY`, which resolves from `~/.config/otb/env`; most builder setups have it. If yours doesn't, claim the artisan newcomer chore or the artist idea-seed chore instead, or ask an Archon to enable it.)\n5. **Confirm it PASSED** the rubric (`modules/art-pipeline/iterations/run_summary.json` shows `status: \"pass\"`). If it only reached best-effort, regenerate with the fix_hint — never paper over a fail.\n6. **See it in-game**: `node scripts/bongos/art-stage.js` rebuilds the atlas and surfaces it on your sandbox preview (`sandbox-<login>.<instance-apex>`).\n7. Ship via `/builder-ship`; the ship notes name your asset and its rubric score.\n\n## Stay clear of (READ-ONLY)\n\nNever modify the locked palette (`modules/art-pipeline/template/palette.json`) or the rubric (`modules/art-pipeline/template/rubric.json`) — those are theme-defining and changing them needs an ADR (out of scope for a first ship). Touch only art assets/vocabulary under `art/` (and, only if your asset is a genuinely new base-tile TYPE, the `public/game/world/tilePalette.js` mapping). Do not touch any permission/pipeline-core/migration/infra file.",
53
53
  "done_when": [
54
54
  "Exactly one NEW in-world asset (your chosen subject) was generated via the pixel-art pipeline (`/otb-tile-generate` / orchestrator.py); the final PNG exists at modules/art-pipeline/generated/tiles/<id>.png.",
55
55
  "It PASSED the rubric (modules/art-pipeline/iterations/run_summary.json status 'pass', or /otb-design-review numbers reported at/above the bar) and every pixel is on the locked palette (modules/art-pipeline/template/palette.json).",
@@ -59,18 +59,16 @@
59
59
  ],
60
60
  "touches": [],
61
61
  "floor": 3
62
- }
63
- ],
64
- "ideator": [
62
+ },
65
63
  {
66
64
  "template_id": "idea-seed-pass",
67
65
  "version_id": "BONGOS-V1",
68
- "discipline": "ideator",
66
+ "discipline": "artist",
69
67
  "kind": "feature",
70
68
  "est_minutes": 45,
71
69
  "credits_reward": 12,
72
70
  "title": "Idea-seed pass: file 5-8 fresh in-world ideas (names, lore, or builder-UX) into the idea inbox",
73
- "description": "## What you'll do\n\nThe seed of almost everything built here is the **idea_inbox** — a queue anyone can drop ideas into, which trusted builders later triage into real tasks. Your first ship as an ideator is simple and concrete: **add a small batch of fresh ideas to that inbox.** You write no code, touch no game, change no system — you think, write a few short well-shaped ideas, and file them with one command each.\n\nIt is identical for every newcomer and **always valuable**: the inbox is never \"done\" — every triage pass consumes ideas (promote/discard/merge), so it is permanently hungry for fresh input, and a growing world always wants more names, more lore, more texture. Your batch is brand-new content, so it can never duplicate \"the work\" — the supply IS the work.\n\n## Pick ONE branch\n\n- **Branch A — PRODUCT flavour (default).** Ideas that grow the world players see: **names + one line of lore** for a character, structure, landmark, district, ship, or festival; or a world-background snippet (a custom, a superstition, a line a named NPC says). Tone: mythic, mysterious, a \"discovered secret\"; Mediterranean ancient-Greek coast (just south of Athens); NOT modern, NOT tongue-in-cheek-modern. Ground it in CLAUDE.md §3/§6/§7.\n- **Branch B — INTERNAL / builder-UX.** Small user-experience improvement ideas for the GDS / builders' hall (the surfaces builders use, not the game): a clearer label, a confusing onboarding message, a quality-of-life tweak to a status page or a slash-command's output. See `docs/TRACKS.md` for product vs internal.\n\n## How to find your way in\n\n1. (Branch A) Read CLAUDE.md §3/§6/§7 for tone. (Branch B) Skim `docs/TRACKS.md` and click around your instance's /builders hall for the rough edge you'll write up.\n2. See what's already filed so you don't repeat it: `node scripts/bongos/api.js GET /api/bongos/inbox`.\n3. File each idea with `node scripts/bongos/capture.js \"<title>\" --body-file <path>` (filing an idea is the open-to-all path — a Xenos/Thetes session is allowed to do it; you just can't triage them). Put one line of detail + a `track: product` or `track: internal` line in each body file.",
71
+ "description": "## What you'll do\n\nThe seed of almost everything built here is the **idea_inbox** — a queue anyone can drop ideas into, which trusted builders later triage into real tasks. Your first ship here is simple and concrete: **add a small batch of fresh ideas to that inbox.** You write no code, touch no game, change no system — you think, write a few short well-shaped ideas, and file them with one command each.\n\nIt is identical for every newcomer and **always valuable**: the inbox is never \"done\" — every triage pass consumes ideas (promote/discard/merge), so it is permanently hungry for fresh input, and a growing world always wants more names, more lore, more texture. Your batch is brand-new content, so it can never duplicate \"the work\" — the supply IS the work.\n\n## Pick ONE branch\n\n- **Branch A — PRODUCT flavour (default).** Ideas that grow the world players see: **names + one line of lore** for a character, structure, landmark, district, ship, or festival; or a world-background snippet (a custom, a superstition, a line a named NPC says). Tone: mythic, mysterious, a \"discovered secret\"; Mediterranean ancient-Greek coast (just south of Athens); NOT modern, NOT tongue-in-cheek-modern. Ground it in CLAUDE.md §3/§6/§7.\n- **Branch B — INTERNAL / builder-UX.** Small user-experience improvement ideas for the GDS / builders' hall (the surfaces builders use, not the game): a clearer label, a confusing onboarding message, a quality-of-life tweak to a status page or a slash-command's output. See `docs/TRACKS.md` for product vs internal.\n\n## How to find your way in\n\n1. (Branch A) Read CLAUDE.md §3/§6/§7 for tone. (Branch B) Skim `docs/TRACKS.md` and click around your instance's /builders hall for the rough edge you'll write up.\n2. See what's already filed so you don't repeat it: `node scripts/bongos/api.js GET /api/bongos/inbox`.\n3. File each idea with `node scripts/bongos/capture.js \"<title>\" --body-file <path>` (filing an idea is the open-to-all path — a Xenos/Thetes session is allowed to do it; you just can't triage them). Put one line of detail + a `track: product` or `track: internal` line in each body file.",
74
72
  "done_when": [
75
73
  "You filed 5–8 fresh ideas into the idea_inbox via `node scripts/bongos/capture.js` (each prints a `Captured idea #N` confirmation), all in ONE branch (A product, or B internal/UX).",
76
74
  "Each idea has a clear title and a body with at least one sentence of detail — for Branch A, one line of in-world lore (who/what it is); for Branch B, what the rough edge is and what 'better' looks like.",
@@ -24,13 +24,13 @@ function test(name, fn) {
24
24
  }
25
25
 
26
26
  // per-discipline DEFAULT floors (the fallback when a template omits its own).
27
- const FLOOR_DEFAULTS = { engineer: 2, artist: 3, ideator: 2 };
27
+ const FLOOR_DEFAULTS = { artisan: 2, artist: 3 };
28
28
 
29
29
  // templates-by-discipline fixture (each discipline -> LIST of chores).
30
30
  const TEMPLATES = {
31
- engineer: [
32
- { template_id: 'unit-test-pass', version_id: 'GDS-V4', discipline: 'engineer', floor: 2, title: 'Add a unit test', done_when: ['a', 'b'], est_minutes: 45 },
33
- { template_id: 'docs-hygiene-pass', version_id: 'GDS-V4', discipline: 'engineer', floor: 1, title: 'Docs sweep', done_when: ['a', 'b'], est_minutes: 40 },
31
+ artisan: [
32
+ { template_id: 'unit-test-pass', version_id: 'GDS-V4', discipline: 'artisan', floor: 2, title: 'Add a unit test', done_when: ['a', 'b'], est_minutes: 45 },
33
+ { template_id: 'docs-hygiene-pass', version_id: 'GDS-V4', discipline: 'artisan', floor: 1, title: 'Docs sweep', done_when: ['a', 'b'], est_minutes: 40 },
34
34
  ],
35
35
  artist: [
36
36
  { template_id: 'asset-generate-pass', version_id: 'V2', discipline: 'artist', title: 'Asset', done_when: ['a', 'b'], est_minutes: 55 }, // no own floor -> default
@@ -40,7 +40,7 @@ const TEMPLATES = {
40
40
  // ---- templateFloor ----
41
41
 
42
42
  test('templateFloor: own floor wins over the discipline default', () => {
43
- assert.strictEqual(nf.templateFloor({ floor: 5 }, 'engineer', FLOOR_DEFAULTS), 5);
43
+ assert.strictEqual(nf.templateFloor({ floor: 5 }, 'artisan', FLOOR_DEFAULTS), 5);
44
44
  });
45
45
 
46
46
  test('templateFloor: falls back to the discipline default when omitted', () => {
@@ -48,7 +48,7 @@ test('templateFloor: falls back to the discipline default when omitted', () => {
48
48
  });
49
49
 
50
50
  test('templateFloor: 0 is a valid explicit floor (not treated as missing)', () => {
51
- assert.strictEqual(nf.templateFloor({ floor: 0 }, 'engineer', FLOOR_DEFAULTS), 0);
51
+ assert.strictEqual(nf.templateFloor({ floor: 0 }, 'artisan', FLOOR_DEFAULTS), 0);
52
52
  });
53
53
 
54
54
  test('templateFloor: unknown discipline with no default -> 0', () => {
@@ -69,7 +69,7 @@ test('computeTemplateDeficits: empty supply -> each chore needs its full floor',
69
69
  });
70
70
 
71
71
  test('computeTemplateDeficits: ready + backlog both count as supply (idempotency)', () => {
72
- const supply = { 'engineer:unit-test-pass': { ready: 1, backlog: 1 } }; // fills floor 2
72
+ const supply = { 'artisan:unit-test-pass': { ready: 1, backlog: 1 } }; // fills floor 2
73
73
  const rows = nf.computeTemplateDeficits(supply, TEMPLATES, FLOOR_DEFAULTS);
74
74
  assert.strictEqual(deficitFor(rows, 'unit-test-pass').needed, 0);
75
75
  assert.strictEqual(deficitFor(rows, 'docs-hygiene-pass').needed, 1, 'sibling chore is independent');
@@ -77,7 +77,7 @@ test('computeTemplateDeficits: ready + backlog both count as supply (idempotency
77
77
 
78
78
  test('computeTemplateDeficits: supply is per-template, not shared across a discipline', () => {
79
79
  // over-supplying one engineer chore must NOT satisfy the other.
80
- const supply = { 'engineer:unit-test-pass': { ready: 9, backlog: 0 } };
80
+ const supply = { 'artisan:unit-test-pass': { ready: 9, backlog: 0 } };
81
81
  const rows = nf.computeTemplateDeficits(supply, TEMPLATES, FLOOR_DEFAULTS);
82
82
  assert.strictEqual(deficitFor(rows, 'unit-test-pass').needed, 0);
83
83
  assert.strictEqual(deficitFor(rows, 'docs-hygiene-pass').needed, 1);
@@ -102,48 +102,48 @@ test('computeTemplateDeficits: ignores non-floor disciplines', () => {
102
102
  // ---- instanceSourceRef + parseSeq + parseRef ----
103
103
 
104
104
  test('instanceSourceRef: tmpl:<disc>:<template_id>:<seq>', () => {
105
- assert.strictEqual(nf.instanceSourceRef('engineer', 'unit-test-pass', 7), 'tmpl:engineer:unit-test-pass:7');
105
+ assert.strictEqual(nf.instanceSourceRef('artisan', 'unit-test-pass', 7), 'tmpl:artisan:unit-test-pass:7');
106
106
  });
107
107
 
108
108
  test('parseSeq: returns seq only for the matching discipline+template', () => {
109
- assert.strictEqual(nf.parseSeq('tmpl:engineer:unit-test-pass:4', 'engineer', 'unit-test-pass'), 4);
110
- assert.strictEqual(nf.parseSeq('tmpl:engineer:unit-test-pass:4', 'artist', 'unit-test-pass'), null, 'wrong discipline');
111
- assert.strictEqual(nf.parseSeq('tmpl:engineer:other:4', 'engineer', 'unit-test-pass'), null, 'wrong template');
112
- assert.strictEqual(nf.parseSeq('manual:engineer:some-title', 'engineer', 'unit-test-pass'), null, 'foreign ref');
113
- assert.strictEqual(nf.parseSeq('tmpl:engineer:unit-test-pass:abc', 'engineer', 'unit-test-pass'), null, 'malformed seq');
109
+ assert.strictEqual(nf.parseSeq('tmpl:artisan:unit-test-pass:4', 'artisan', 'unit-test-pass'), 4);
110
+ assert.strictEqual(nf.parseSeq('tmpl:artisan:unit-test-pass:4', 'artist', 'unit-test-pass'), null, 'wrong discipline');
111
+ assert.strictEqual(nf.parseSeq('tmpl:artisan:other:4', 'artisan', 'unit-test-pass'), null, 'wrong template');
112
+ assert.strictEqual(nf.parseSeq('manual:artisan:some-title', 'artisan', 'unit-test-pass'), null, 'foreign ref');
113
+ assert.strictEqual(nf.parseSeq('tmpl:artisan:unit-test-pass:abc', 'artisan', 'unit-test-pass'), null, 'malformed seq');
114
114
  });
115
115
 
116
116
  test('parseRef: decomposes a chore ref into discipline/template/seq', () => {
117
- assert.deepStrictEqual(nf.parseRef('tmpl:engineer:unit-test-pass:7'), { discipline: 'engineer', templateId: 'unit-test-pass', seq: 7 });
118
- assert.strictEqual(nf.parseRef('manual:engineer:x'), null, 'not a chore ref');
119
- assert.strictEqual(nf.parseRef('tmpl:engineer:unit-test-pass:0'), null, 'seq must be positive');
120
- assert.strictEqual(nf.parseRef('tmpl:engineer:unit-test-pass'), null, 'too few parts');
117
+ assert.deepStrictEqual(nf.parseRef('tmpl:artisan:unit-test-pass:7'), { discipline: 'artisan', templateId: 'unit-test-pass', seq: 7 });
118
+ assert.strictEqual(nf.parseRef('manual:artisan:x'), null, 'not a chore ref');
119
+ assert.strictEqual(nf.parseRef('tmpl:artisan:unit-test-pass:0'), null, 'seq must be positive');
120
+ assert.strictEqual(nf.parseRef('tmpl:artisan:unit-test-pass'), null, 'too few parts');
121
121
  assert.strictEqual(nf.parseRef('tmpl:a:b:c:d'), null, 'too many parts');
122
122
  });
123
123
 
124
124
  // ---- nextSeq (monotonic across all refs) ----
125
125
 
126
126
  test('nextSeq: empty set -> 1', () => {
127
- assert.strictEqual(nf.nextSeq(new Set(), 'engineer', 'unit-test-pass'), 1);
127
+ assert.strictEqual(nf.nextSeq(new Set(), 'artisan', 'unit-test-pass'), 1);
128
128
  });
129
129
 
130
130
  test('nextSeq: 1 + max matching seq, ignoring other templates/disciplines', () => {
131
131
  const refs = new Set([
132
- 'tmpl:engineer:unit-test-pass:1',
133
- 'tmpl:engineer:unit-test-pass:3', // gap is fine; max is 3
134
- 'tmpl:engineer:unit-test-pass:2',
135
- 'tmpl:engineer:docs-hygiene-pass:9', // sibling chore — ignored
132
+ 'tmpl:artisan:unit-test-pass:1',
133
+ 'tmpl:artisan:unit-test-pass:3', // gap is fine; max is 3
134
+ 'tmpl:artisan:unit-test-pass:2',
135
+ 'tmpl:artisan:docs-hygiene-pass:9', // sibling chore — ignored
136
136
  'tmpl:artist:asset-generate-pass:5', // different discipline — ignored
137
- 'manual:engineer:something', // foreign — ignored
137
+ 'manual:artisan:something', // foreign — ignored
138
138
  ]);
139
- assert.strictEqual(nf.nextSeq(refs, 'engineer', 'unit-test-pass'), 4);
140
- assert.strictEqual(nf.nextSeq(refs, 'engineer', 'docs-hygiene-pass'), 10, 'sibling seq is independent');
141
- assert.strictEqual(nf.nextSeq(refs, 'ideator', 'idea-seed-pass'), 1);
139
+ assert.strictEqual(nf.nextSeq(refs, 'artisan', 'unit-test-pass'), 4);
140
+ assert.strictEqual(nf.nextSeq(refs, 'artisan', 'docs-hygiene-pass'), 10, 'sibling seq is independent');
141
+ assert.strictEqual(nf.nextSeq(refs, 'artist', 'idea-seed-pass'), 1);
142
142
  });
143
143
 
144
144
  test('nextSeq: never recycles a shipped/abandoned seq (max over ALL statuses)', () => {
145
- const refs = new Set(['tmpl:ideator:idea-seed-pass:1', 'tmpl:ideator:idea-seed-pass:2']);
146
- assert.strictEqual(nf.nextSeq(refs, 'ideator', 'idea-seed-pass'), 3);
145
+ const refs = new Set(['tmpl:artist:idea-seed-pass:1', 'tmpl:artist:idea-seed-pass:2']);
146
+ assert.strictEqual(nf.nextSeq(refs, 'artist', 'idea-seed-pass'), 3);
147
147
  });
148
148
 
149
149
  // ---- buildProposalFromTemplate ----
@@ -151,7 +151,7 @@ test('nextSeq: never recycles a shipped/abandoned seq (max over ALL statuses)',
151
151
  const SAMPLE_TEMPLATE = {
152
152
  template_id: 'docs-hygiene-pass',
153
153
  version_id: 'GDS-V4',
154
- discipline: 'engineer',
154
+ discipline: 'artisan',
155
155
  kind: 'cleanup',
156
156
  est_minutes: 40,
157
157
  credits_reward: 15,
@@ -163,14 +163,14 @@ const SAMPLE_TEMPLATE = {
163
163
  };
164
164
 
165
165
  test('buildProposalFromTemplate: carries fields, normalizes touches, sets seq ref', () => {
166
- const p = nf.buildProposalFromTemplate(SAMPLE_TEMPLATE, 5, { discipline: 'engineer' });
166
+ const p = nf.buildProposalFromTemplate(SAMPLE_TEMPLATE, 5, { discipline: 'artisan' });
167
167
  assert.strictEqual(p.title, 'Docs-hygiene sweep', 'trimmed');
168
168
  assert.strictEqual(p.version_id, 'GDS-V4');
169
169
  assert.strictEqual(p.kind, 'cleanup');
170
170
  assert.strictEqual(p.credits_reward, 15);
171
171
  assert.strictEqual(p.est_minutes, 40);
172
172
  assert.deepStrictEqual(p.touches, ['docs/', '42'], 'empties dropped, coerced to strings');
173
- assert.strictEqual(p.source_ref, 'tmpl:engineer:docs-hygiene-pass:5');
173
+ assert.strictEqual(p.source_ref, 'tmpl:artisan:docs-hygiene-pass:5');
174
174
  assert.deepStrictEqual(p.done_when, ['a', 'b']);
175
175
  });
176
176
 
@@ -191,20 +191,22 @@ test('buildProposalFromTemplate: null on a titleless template', () => {
191
191
  test('buildDescription: folds done-when + evergreen footer', () => {
192
192
  const desc = nf.buildDescription(
193
193
  { title: 't', description: 'Body text.', done_when: ['x', 'y'], est_minutes: 30 },
194
- 'engineer'
194
+ 'artisan'
195
195
  );
196
196
  assert.ok(desc.includes('Body text.'));
197
197
  assert.ok(desc.includes('**Done when:**'));
198
198
  assert.ok(desc.includes('- x') && desc.includes('- y'));
199
199
  assert.ok(desc.includes('Evergreen newcomer chore'), 'footer present');
200
- assert.ok(desc.includes('engineer'), 'discipline in footer');
200
+ assert.ok(desc.includes('artisan'), 'discipline in footer');
201
201
  });
202
202
 
203
203
  // ---- loadTemplates: the REAL templates.json is well-formed (multi-chore shape) ----
204
204
 
205
205
  test('loadTemplates: real templates.json is a valid per-discipline list', () => {
206
206
  const templates = nf.loadTemplates(path.join(__dirname, 'newcomer-floor.templates.json'));
207
- for (const disc of ['engineer', 'artist', 'ideator']) {
207
+ // task 1005141: two crafts, not three — ideator folded into artist, engineer is artisan.
208
+ assert.ok(!('engineer' in templates) && !('ideator' in templates), 'no old-spelling keys remain');
209
+ for (const disc of ['artisan', 'artist']) {
208
210
  const list = templates[disc];
209
211
  assert.ok(Array.isArray(list) && list.length >= 1, `${disc} is a non-empty list`);
210
212
  for (const t of list) {
@@ -219,15 +221,22 @@ test('loadTemplates: real templates.json is a valid per-discipline list', () =>
219
221
  }
220
222
  });
221
223
 
222
- test('loadTemplates: engineer now offers BOTH the unit-test chore and the docs sweep', () => {
224
+ test('loadTemplates: artist holds BOTH the asset chore and the (former ideator) idea-seed chore', () => {
223
225
  const templates = nf.loadTemplates(path.join(__dirname, 'newcomer-floor.templates.json'));
224
- const ids = templates.engineer.map((t) => t.template_id);
226
+ const ids = templates.artist.map((t) => t.template_id);
227
+ assert.ok(ids.includes('asset-generate-pass'), 'the asset chore is present');
228
+ assert.ok(ids.includes('idea-seed-pass'), 'the idea chore moved under artist');
229
+ });
230
+
231
+ test('loadTemplates: artisan offers BOTH the unit-test chore and the docs sweep', () => {
232
+ const templates = nf.loadTemplates(path.join(__dirname, 'newcomer-floor.templates.json'));
233
+ const ids = templates.artisan.map((t) => t.template_id);
225
234
  assert.ok(ids.includes('unit-test-pass'), 'the safe coding chore is present');
226
235
  assert.ok(ids.includes('docs-hygiene-pass'), 'the docs sweep is retained');
227
236
  // every engineer chore touches only safe (non-protected) surfaces by design:
228
237
  // touches[] is empty (git-merge is the collision detector), so this asserts the
229
238
  // contract that engineer chores carry no protected-path touches.
230
- for (const t of templates.engineer) {
239
+ for (const t of templates.artisan) {
231
240
  assert.deepStrictEqual(t.touches || [], [], `${t.template_id} declares no touches[]`);
232
241
  }
233
242
  });
@@ -123,10 +123,11 @@ function checkRolePacks(fsImpl = fs) {
123
123
  }
124
124
 
125
125
  // The split's own shape, asserted so a later edit cannot quietly drop a craft
126
- // back into the kernel: three core packs, and Governor deliberately absent
127
- // (criterion wa6-kernel-and-packs — "three packs, not four, until the owner decides").
128
- if (packCount < 3) {
129
- violations.push(`only ${packCount} craft(s) name a pack — the split is one kernel plus three packs (Engineer, Artist, Ideator). A craft with no pack has nowhere to keep what the role-neutral kernel does not.`);
126
+ // back into the kernel: two core packs since the game conversion folded the
127
+ // ideator into the artist (task 1005141, BONGOS-V3) — Artisan and Artist; Patron
128
+ // is a character, not a task craft, and has no pack.
129
+ if (packCount < 2) {
130
+ violations.push(`only ${packCount} craft(s) name a pack — the split is one kernel plus two packs (Artisan, Artist). A craft with no pack has nowhere to keep what the role-neutral kernel does not.`);
130
131
  }
131
132
 
132
133
  return {
@@ -10,7 +10,7 @@
10
10
  "title": "Spike: where the welcome checklist is stored",
11
11
  "description": "Output an ADR naming the table and who writes it.",
12
12
  "kind": "spike",
13
- "discipline": "engineer",
13
+ "discipline": "artisan",
14
14
  "priority": 4,
15
15
  "est_minutes": 90,
16
16
  "criterion_ids": ["newcomer-knows-what-to-do-next"]
@@ -20,7 +20,7 @@
20
20
  "title": "The welcome checklist API",
21
21
  "description": "Build to R01's ADR. Done-when: a new builder's checklist is returned by one GET.",
22
22
  "kind": "feature",
23
- "discipline": "engineer",
23
+ "discipline": "artisan",
24
24
  "priority": 4,
25
25
  "est_minutes": 180,
26
26
  "criterion_ids": ["newcomer-knows-what-to-do-next"],
@@ -42,7 +42,7 @@
42
42
  "title": "Proof: a newcomer finds their next step without asking",
43
43
  "description": "A brand-new builder signs in and finds what to do next unaided.",
44
44
  "kind": "verify",
45
- "discipline": "engineer",
45
+ "discipline": "artisan",
46
46
  "priority": 3,
47
47
  "est_minutes": 60,
48
48
  "criterion_ids": ["newcomer-knows-what-to-do-next"],
@@ -4,10 +4,9 @@
4
4
  // Usage:
5
5
  // node scripts/bongos/setup.js run interactively, perform the auth
6
6
  // node scripts/bongos/setup.js --force re-auth even if a valid session exists
7
- // node scripts/bongos/setup.js --skip-keys skip the per-builder Gemini-key prompt
8
- // (for CI / batch onboarding / builders
9
- // who don't need the art pipeline)
10
- // node scripts/bongos/setup.js --dry-run print every step that *would* run, without
7
+ // node scripts/bongos/setup.js --skip-keys accepted and ignored: setup asks for no
8
+ // API key any more (task 1005189)
9
+ // node scripts/bongos/setup.js --dry-run print every step that *would* run, without
11
10
  // contacting the API, writing files, or
12
11
  // prompting. Useful for newcomers who want
13
12
  // a preview before running it for real.
@@ -21,24 +20,18 @@
21
20
  const readline = require('readline');
22
21
  const { cliClient, errCode, saveSession, SESSION_PATH, loadSession, printOnboardingNudge, API_BASE, hallBase, surfaceAction, isTerminalSurface } = require('./cli-lib');
23
22
 
24
- // The instance's status dashboard, from branding — never a hardcoded host, so a
25
- // non-OTB instance points its builders at ITS OWN dashboard (task 1002428).
26
- // Falls back to the resolved API base when branding is unreadable.
27
- function statusOrigin() {
28
- try {
29
- const d = require('../../src/branding').branding().domains || {};
30
- if (d.statusOrigin) return d.statusOrigin;
31
- } catch (_) { /* fall through */ }
32
- return API_BASE;
33
- }
34
23
  const onboardingConfig = require('./onboarding-config');
35
24
  const { sleep } = require('../../src/sleep'); // the one shared sleep (task 1004893)
36
25
 
37
26
  const DRY_RUN = process.argv.includes('--dry-run');
38
- // --skip-keys mirrors the same flag on scripts/setup-builder.sh — when set,
39
- // the Gemini-key prompt is skipped entirely. Used by CI / batch onboarding /
40
- // builders who don't intend to run the art pipeline.
41
- const SKIP_KEYS = process.argv.includes('--skip-keys');
27
+ // Setup asks for NO API key (task 1005189). It used to stop every new builder,
28
+ // on every project, at a Google AI Studio key question — including projects
29
+ // with no art pipeline, and including agent-run setups where nobody can type
30
+ // an answer. An optional key is noise at sign-in: a builder adds it in hall
31
+ // Settings (#art-key) when art actually needs it, and the art pipeline's own
32
+ // "no key found" message is the one place that says so. The server-stored key
33
+ // still reaches this machine through fetch-art-key.js. --skip-keys is still
34
+ // accepted (setup-builder passes it) and does nothing.
42
35
 
43
36
  // Tiny helper so the dry-run output reads as a clear plan rather than a wall
44
37
  // of identical-looking log lines.
@@ -46,77 +39,6 @@ function dryRunStep(msg) {
46
39
  console.log(`[dry-run] ${msg}`);
47
40
  }
48
41
 
49
- // Ask the new builder for their own Google AI Studio key — used by the
50
- // pixel-art pipeline (gen_api.py). V3.R11 (#91): each builder pays their own
51
- // Gemini bill, so spend is attributable per-builder on the public dashboard.
52
- //
53
- // Stored as gemini_api_key in ~/.config/otb/bongos-session.json (mode 600, same
54
- // file as the GitHub session token). gen_api.py reads this location before
55
- // falling back to ~/.config/otb/env or .env.local.
56
- //
57
- // Behavior:
58
- // - Non-TTY shells: skip silently. Builder can edit the session file later.
59
- // - --skip-keys flag: skip silently. CI/batch onboarding.
60
- // - Existing key on file: show last-4, ask "overwrite? [y/N]". Default no.
61
- // - Empty input: leave whatever was there. Don't blank a working key by
62
- // accident.
63
- async function promptGeminiKey() {
64
- if (SKIP_KEYS) return;
65
- if (!process.stdin.isTTY) return;
66
-
67
- const fs = require('node:fs/promises');
68
- let session;
69
- try {
70
- session = JSON.parse(await fs.readFile(SESSION_PATH, 'utf8'));
71
- } catch (_) {
72
- // saveSession just wrote it; if it's not readable something's badly wrong.
73
- // Better to bail than to silently lose a key the builder is about to type.
74
- return;
75
- }
76
-
77
- const existing = session.gemini_api_key;
78
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
79
- const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
80
- try {
81
- console.log('');
82
- console.log('Google AI Studio key for the pixel-art pipeline (per-builder, optional).');
83
- console.log(' Get one at https://aistudio.google.com/app/apikey');
84
- console.log(' (Or skip this and add it later with browser clicks: hall Settings →');
85
- console.log(` ${hallBase()}/settings#art-key — stored encrypted, ADR 0073.)`);
86
- console.log(' Each builder pays their own Gemini bill so spend is attributed to them');
87
- console.log(` on ${statusOrigin()}.`);
88
- let prompt;
89
- if (existing && typeof existing === 'string' && existing.length >= 4) {
90
- const last4 = existing.slice(-4);
91
- const overwrite = (await ask(` Key on file ending …${last4}. Overwrite? [y/N] `)).trim().toLowerCase();
92
- if (!(overwrite === 'y' || overwrite === 'yes')) {
93
- console.log(' Kept the existing key.');
94
- return;
95
- }
96
- prompt = ' New GOOGLE_AI_STUDIO_KEY (press Enter to clear): ';
97
- } else {
98
- prompt = ' GOOGLE_AI_STUDIO_KEY (press Enter to skip): ';
99
- }
100
- const input = (await ask(prompt)).trim();
101
- if (!input) {
102
- if (existing) {
103
- // The user asked to overwrite, then entered nothing → clear it.
104
- delete session.gemini_api_key;
105
- await fs.writeFile(SESSION_PATH, JSON.stringify(session, null, 2), { mode: 0o600 });
106
- console.log(' Cleared the key.');
107
- } else {
108
- console.log(' Skipped — set it later by re-running setup.js or editing the session file.');
109
- }
110
- return;
111
- }
112
- session.gemini_api_key = input;
113
- await fs.writeFile(SESSION_PATH, JSON.stringify(session, null, 2), { mode: 0o600 });
114
- console.log(` Saved Gemini key to ${SESSION_PATH} (mode 600).`);
115
- } finally {
116
- rl.close();
117
- }
118
- }
119
-
120
42
  // V3.R10 (#235), criterion C3 — link the builder's Anthropic account.
121
43
  //
122
44
  // Claude Code is the trusted agent on the builder's machine: it writes its own
@@ -213,15 +135,15 @@ async function promptDisciplines() {
213
135
  try {
214
136
  console.log('');
215
137
  console.log('Which kind of work do you want surfaced first?');
216
- console.log(' 1) engineer — code, gameplay mechanics, infra, the GDS itself, perf, bugs');
217
- console.log(' 2) artist — pixel-art tiles, sprites, icons, avatars, the art pipeline');
218
- console.log(' 3) ideator — idea capture, scoping, research, docs/briefs, narrative, lore');
219
- console.log(' 4) all of them');
220
- console.log(' 5) skip for now (you can set this later on /builders)');
221
- const choice = (await ask('Choose [1-5]: ')).trim();
138
+ console.log(' 1) artisan — code, gameplay mechanics, infra, Bongos itself, perf, bugs');
139
+ console.log(' 2) artist — how the project looks and reads, and its ideas: art, copy, scoping, lore');
140
+ console.log(' 3) both of them');
141
+ console.log(' 4) skip for now (you can set this later on /builders)');
142
+ const choice = (await ask('Choose [1-4]: ')).trim();
143
+ // Two task crafts since task 1005141 (the ideator folded into the artist).
222
144
  const map = {
223
- 1: ['engineer'], 2: ['artist'], 3: ['ideator'],
224
- 4: ['engineer', 'artist', 'ideator'], 5: null,
145
+ 1: ['artisan'], 2: ['artist'],
146
+ 3: ['artisan', 'artist'], 4: null,
225
147
  };
226
148
  const disciplines = map[choice];
227
149
  if (disciplines === undefined) {
@@ -327,12 +249,9 @@ async function finishSetup(builder) {
327
249
  } catch (e) {
328
250
  console.log(`Note: could not activate git hooks (${e.message}); run: git config core.hooksPath .husky`);
329
251
  }
330
- await promptGeminiKey();
331
252
  // #1281: deliver the GDS shared art key to entitled newcomers right now, so
332
253
  // a brand-new Xenos's first art task works without waiting for the next
333
- // session start. Best-effort, bounded — never block setup. (Phase 2 will
334
- // make the promptGeminiKey copy above rank-aware — "we cover your first art
335
- // tasks; add your own key when you reach Thetes".)
254
+ // session start. Best-effort, bounded, silent — never block setup.
336
255
  try {
337
256
  const { spawnSync } = require('node:child_process');
338
257
  const path = require('node:path');
@@ -405,11 +324,6 @@ async function main() {
405
324
  dryRunStep('If GitHub refuses with device_flow_disabled (a one-click App whose checkbox is unticked, task 1003051): would print the web sign-in guidance (hall Settings → CLI Access) and prompt for a pasted CLI token instead of dead-ending.');
406
325
  dryRunStep('Would print the verification_uri and user_code, then poll /api/bongos/auth/device/poll every few seconds until the user approves or the code expires.');
407
326
  dryRunStep(`On approval, would write the bearer token + builder profile to ${SESSION_PATH} (chmod 600).`);
408
- if (SKIP_KEYS) {
409
- dryRunStep('Would skip the Gemini-key prompt (--skip-keys set).');
410
- } else {
411
- dryRunStep(`Would prompt for a per-builder GOOGLE_AI_STUDIO_KEY on TTY shells and store it as gemini_api_key in ${SESSION_PATH} (mode 600). Used by modules/art-pipeline/pipeline/gen_api.py for per-builder cost attribution.`);
412
- }
413
327
  dryRunStep('Would read ~/.claude.json oauthAccount and, on a TTY, prompt to confirm the Anthropic email; on a match would POST /api/bongos/me/anthropic-link and store anthropic_user_id in the session (kept as cosmetic attribution; the identity pin was removed — task 1039 / idea 258). On mismatch would REFUSE to link.');
414
328
  dryRunStep('Would prompt for preferred disciplines (creative / engineering / both / skip) and PATCH /api/bongos/me/disciplines on TTY shells.');
415
329
  dryRunStep(`Would prompt for onboarding-friction consent (Y/n) and record the decision to ${onboardingConfig.CONSENT_PATH} on TTY shells.`);
@@ -531,7 +445,6 @@ async function main() {
531
445
  const { activate } = require('./install-git-hooks');
532
446
  activate();
533
447
  } catch (_) { /* hook activation is best-effort */ }
534
- await promptGeminiKey();
535
448
  await promptAnthropicLink();
536
449
  await promptDisciplines();
537
450
  await promptOnboardingConsent();
@@ -220,9 +220,9 @@ function roleLabel(t) {
220
220
  // that read in both light and dark. Colours the Type column so the work lane
221
221
  // reads at a glance (task 1273 follow-up: "spice up the view").
222
222
  function disciplineColor(d) {
223
- if (d === 'engineer') return '#1D9E75'; // sea-teal
223
+ if (d === 'artisan') return '#1D9E75'; // sea-teal
224
224
  if (d === 'artist') return '#D85A30'; // terracotta
225
- if (d === 'ideator') return '#639922'; // olive
225
+ if (d === 'artist') return '#639922'; // olive
226
226
  return '#888780'; // unclassified — neutral gray
227
227
  }
228
228
  function whatItIs(t) {
@@ -7,7 +7,7 @@
7
7
  // the first-class front door.
8
8
  //
9
9
  // Usage:
10
- // bongos task new --title "…" --description "…" [--kind feature] [--discipline engineer]
10
+ // bongos task new --title "…" --description "…" [--kind feature] [--discipline artisan]
11
11
  // [--priority 3] [--version <id>] [--goal <id>] [--criteria C1,C2]
12
12
  // [--touches a/,b/] [--est-minutes N] [--credits N] [--ready] [--json]
13
13
  // bongos task show <id> [--json]
@@ -29,7 +29,7 @@ const { UPKEEP_KIND } = require('../../modules/lifecycle/upkeep-home');
29
29
  // Valid enums (mirrors the server's task-classifier vocab; a friendly client-side check
30
30
  // so an obvious typo fails before the round-trip — the server stays authoritative).
31
31
  const VALID_KINDS = ['feature', 'bug', 'infra', 'refactor', 'spike', 'learning-capture', 'cleanup', 'decision', 'blocker-resolution', 'verify'];
32
- const VALID_DISCIPLINES = ['engineer', 'artist', 'ideator', 'ui'];
32
+ const VALID_DISCIPLINES = ['artisan', 'artist', 'ui'];
33
33
 
34
34
  function csv(v) {
35
35
  return String(v || '').split(',').map((s) => s.trim()).filter(Boolean);
@@ -46,7 +46,8 @@ function buildTaskBody(args) {
46
46
 
47
47
  const kind = arg('--kind', args) || 'feature';
48
48
  if (!VALID_KINDS.includes(kind)) errors.push(`--kind must be one of: ${VALID_KINDS.join(', ')}`);
49
- const discipline = arg('--discipline', args) || 'engineer';
49
+ // Old spellings still work (task 1005141): engineer → artisan, ideator → artist.
50
+ const discipline = ({ engineer: 'artisan', ideator: 'artist' })[arg('--discipline', args)] || arg('--discipline', args) || 'artisan';
50
51
  if (!VALID_DISCIPLINES.includes(discipline)) errors.push(`--discipline must be one of: ${VALID_DISCIPLINES.join(', ')}`);
51
52
 
52
53
  const num = (flag) => { const v = arg(flag, args); return v == null ? undefined : Number(v); };
@@ -82,7 +83,7 @@ function buildTaskBody(args) {
82
83
 
83
84
  function usage() {
84
85
  console.error('usage:');
85
- console.error(' bongos task new --title "…" --description "…" [--kind feature] [--discipline engineer]');
86
+ console.error(' bongos task new --title "…" --description "…" [--kind feature] [--discipline artisan]');
86
87
  console.error(' [--priority 3] [--version <id>] [--goal <id>] [--criteria C1,C2]');
87
88
  console.error(' [--touches a/,b/] [--est-minutes N] [--credits N] [--ready] [--json]');
88
89
  console.error(' bongos task show <id> [--json]');
@@ -282,7 +282,7 @@ async function createTask({ versionId, title, description, priority, sourceRef,
282
282
  // promotion they just decided on. So resolve it here with the SAME function the
283
283
  // server uses, and name a fallback rather than letting the loop break — the
284
284
  // identical trade modules/ideas/inbox.js makes for capture.
285
- discipline: resolveWriteDiscipline({ title, description }).discipline || 'engineer',
285
+ discipline: resolveWriteDiscipline({ title, description }).discipline || 'artisan',
286
286
  };
287
287
  if (goalId == null) {
288
288
  throw new Error('createTask: goal_id is required — every task belongs to a goal (task 1003598 deleted the catch-all fallback).');