@bongos/core 1.18.0 → 1.18.18

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 (553) hide show
  1. package/.bongos-core.json +1740 -640
  2. package/.claude/skills/builder-box/SKILL.md +6 -0
  3. package/.claude/skills/builder-sequence/SKILL.md +97 -0
  4. package/.claude/skills/builder-ship/SKILL.md +8 -2
  5. package/.claude/skills/builder-start/SKILL.md +11 -0
  6. package/.claude/skills/builder-sync/SKILL.md +41 -0
  7. package/.claude/skills/fix-task/SKILL.md +64 -0
  8. package/.claude/skills/goal-create/SKILL.md +158 -0
  9. package/.claude/skills/goal-review/SKILL.md +3 -1
  10. package/.claude/skills/idea-triage/SKILL.md +14 -8
  11. package/.claude/skills/merge-mode/SKILL.md +2 -2
  12. package/.claude/skills/new-project/SKILL.md +1 -0
  13. package/.claude/skills/planning-session/SKILL.md +45 -18
  14. package/.env.local.example +4 -3
  15. package/README.md +5 -3
  16. package/bin/bongos.js +1 -0
  17. package/clients/bongos-client/README.md +2 -2
  18. package/clients/bongos-client/bongos-client.global.js +82 -18
  19. package/clients/bongos-client/index.cjs +82 -18
  20. package/clients/bongos-client/index.d.ts +131 -29
  21. package/clients/bongos-client/index.mjs +82 -18
  22. package/config/branding.neutral.json +11 -2
  23. package/config/governance.neutral.json +21 -0
  24. package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +24 -0
  25. package/docs/adr/0090-metic-task-authoring.md +1 -1
  26. package/docs/adr/0142-project-context-is-prose-only.md +41 -0
  27. package/docs/adr/0143-onboarding-repo-picker-oauth-scope.md +35 -0
  28. package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +41 -0
  29. package/docs/adr/0144-federated-single-logout-backchannel.md +42 -0
  30. package/docs/adr/0145-devbox-app-branding-driven-module.md +44 -0
  31. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +62 -0
  32. package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +53 -0
  33. package/docs/adr/0147-standalone-live-docs-regen.md +39 -0
  34. package/docs/adr/0148-task-scoped-box-source-access.md +48 -0
  35. package/docs/adr/0149-bongos-dev-local-launcher.md +57 -0
  36. package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +68 -0
  37. package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +229 -0
  38. package/docs/adr/0152-landing-gate-served-by-the-core-app.md +92 -0
  39. package/docs/adr/0152-metic-task-abandonment.md +67 -0
  40. package/docs/adr/0153-sequential-chain-runner.md +51 -0
  41. package/docs/adr/0154-goal-owner-criterion-authoring.md +86 -0
  42. package/docs/adr/README.md +15 -2
  43. package/docs/api/openapi.json +2648 -483
  44. package/docs/api-reference.md +73 -13
  45. package/docs/architecture.md +4 -2
  46. package/docs/branding-contract.md +3 -1
  47. package/docs/canonical-permissions.md +21 -8
  48. package/docs/file-map.md +15 -7
  49. package/docs/onboarding/diagrams/01-task-lifecycle.mmd +2 -2
  50. package/docs/onboarding/diagrams/02-rank-ladder.mmd +3 -1
  51. package/docs/onboarding/diagrams/05-versioning.mmd +1 -3
  52. package/docs/onboarding/diagrams/assertions.json +4 -7
  53. package/docs/onboarding/drachmae-and-karma.md +3 -3
  54. package/docs/onboarding/primer.md +17 -16
  55. package/docs/onboarding/slash-commands.md +124 -0
  56. package/docs/recipes/builder-box-lifecycle.md +23 -0
  57. package/docs/recipes/devbox-app-release.md +14 -4
  58. package/docs/recipes/instance-first-run.md +16 -0
  59. package/docs/recipes/local-dev.md +81 -0
  60. package/docs/recipes/ops-gotchas.md +19 -1
  61. package/docs/recipes/overnight-builder.md +1 -1
  62. package/docs/recipes/packaging-the-core.md +13 -0
  63. package/docs/recipes/parallel-goal-waves.md +174 -0
  64. package/docs/recipes/ship-and-regrade.md +2 -2
  65. package/docs/recipes/standalone-live-docs.md +48 -0
  66. package/docs/recipes/upgrading-the-core.md +5 -2
  67. package/docs/recipes/work-on-a-task-from-your-dev-box.md +108 -0
  68. package/migrations/003_pms.sql +6 -7
  69. package/migrations/006_pms_v2.sql +3 -31
  70. package/migrations/039_newcomer_friendly_column.sql +5 -0
  71. package/migrations/102_remove_box_first_provision_approval.sql +4 -0
  72. package/migrations/core_192_github_oauth_tokens.sql +33 -0
  73. package/migrations/core_193_task_needs_rebase.sql +28 -0
  74. package/migrations/core_194_confirmed_backlog_clawback.sql +63 -0
  75. package/migrations/core_195_idea_triage_provenance.sql +57 -0
  76. package/migrations/core_196_session_reward_topup.sql +69 -0
  77. package/modules/autonomy/pollers/routine-timers.js +9 -8
  78. package/modules/builder-settings/wandering-prefs.js +61 -18
  79. package/modules/dev-box/app/README.md +73 -0
  80. package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
  81. package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
  82. package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
  83. package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
  84. package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
  85. package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
  86. package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
  87. package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
  88. package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
  89. package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
  90. package/modules/dev-box/app/assets/tray/win-off.png +0 -0
  91. package/modules/dev-box/app/assets/tray/win-on.png +0 -0
  92. package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
  93. package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
  94. package/modules/dev-box/app/build-resources/icon.png +0 -0
  95. package/modules/dev-box/app/electron-builder.tmpl.yml +95 -0
  96. package/modules/dev-box/app/electron-builder.yml +95 -0
  97. package/modules/dev-box/app/package-lock.json +5291 -0
  98. package/modules/dev-box/app/package.json +30 -0
  99. package/modules/dev-box/app/renderer/app.js +319 -0
  100. package/modules/dev-box/app/renderer/index.html +45 -0
  101. package/modules/dev-box/app/renderer/styles.css +246 -0
  102. package/modules/dev-box/app/renderer/theme.generated.css +16 -0
  103. package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +82 -0
  104. package/modules/dev-box/app/scripts/dmgbuild-settings.py +39 -0
  105. package/modules/dev-box/app/scripts/gen-brand.js +138 -0
  106. package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +323 -0
  107. package/modules/dev-box/app/scripts/gen-icons.js +286 -0
  108. package/modules/dev-box/app/scripts/vendor-client.js +23 -0
  109. package/modules/dev-box/app/src/box-controller.js +287 -0
  110. package/modules/dev-box/app/src/brand.generated.json +13 -0
  111. package/modules/dev-box/app/src/brand.js +16 -0
  112. package/modules/dev-box/app/src/config.js +19 -0
  113. package/modules/dev-box/app/src/gds-client.js +90 -0
  114. package/modules/dev-box/app/src/main.js +610 -0
  115. package/modules/dev-box/app/src/pairing.js +57 -0
  116. package/modules/dev-box/app/src/preload.js +29 -0
  117. package/modules/dev-box/app/src/session-store.js +58 -0
  118. package/modules/dev-box/app/src/ssh-setup.js +569 -0
  119. package/modules/dev-box/app/src/task-picker.js +70 -0
  120. package/modules/dev-box/app/src/tray.js +99 -0
  121. package/modules/dev-box/app/src/updater.js +55 -0
  122. package/modules/dev-box/app/src/vendor/bongos-client.cjs +698 -0
  123. package/modules/dev-box/app/test/unit.mjs +526 -0
  124. package/modules/dev-box/box-access.js +121 -4
  125. package/modules/dev-box/boxes.js +20 -0
  126. package/modules/dev-box/module.json +1 -1
  127. package/modules/dev-box/routes/box.js +87 -3
  128. package/modules/economy/credits.js +116 -25
  129. package/modules/economy/reward.js +8 -0
  130. package/modules/economy/streak.js +2 -1
  131. package/modules/governance/CLAUDE.md +15 -0
  132. package/modules/governance/catalog.js +419 -0
  133. package/modules/governance/config.js +174 -0
  134. package/modules/governance/db.js +125 -0
  135. package/modules/governance/migrations/governance_001_tables.sql +81 -0
  136. package/modules/governance/migrations/governance_002_seed_and_backfill.sql +103 -0
  137. package/modules/governance/migrations/governance_003_task_abandon_metic.sql +28 -0
  138. package/modules/governance/migrations/governance_004_criterion_create.sql +39 -0
  139. package/modules/governance/migrations/governance_005_resync_builder_rank_roles.sql +56 -0
  140. package/modules/governance/module.json +10 -0
  141. package/modules/governance/protected-surfaces.js +88 -0
  142. package/modules/governance/protected-surfaces.json +80 -0
  143. package/modules/governance/rank-roles.js +83 -0
  144. package/modules/governance/resolver.js +221 -0
  145. package/modules/governance/routes/governance.js +89 -0
  146. package/modules/governance/routes/role-sync.js +49 -0
  147. package/modules/governance/session-scopes.js +127 -0
  148. package/modules/governance/session-scopes.json +38 -0
  149. package/modules/hall-ui/public/atlas.css +117 -0
  150. package/modules/hall-ui/public/atlas.html +3 -114
  151. package/modules/hall-ui/public/brand-apply.js +34 -6
  152. package/modules/hall-ui/public/builders.js +187 -219
  153. package/modules/hall-ui/public/diagrams.html +2 -0
  154. package/modules/hall-ui/public/dom-utils.js +27 -0
  155. package/modules/hall-ui/public/drachmae.html +2 -0
  156. package/modules/hall-ui/public/drachmae.js +54 -1
  157. package/modules/hall-ui/public/gate.css +98 -0
  158. package/modules/hall-ui/public/gate.html +3 -95
  159. package/modules/hall-ui/public/goal-inbox.js +4 -1
  160. package/modules/hall-ui/public/goals-page.js +626 -114
  161. package/modules/hall-ui/public/goals.css +79 -1
  162. package/modules/hall-ui/public/goals.html +38 -11
  163. package/modules/hall-ui/public/hall-widgets.js +62 -52
  164. package/modules/hall-ui/public/harbor.css +123 -0
  165. package/modules/hall-ui/public/harbor.html +3 -120
  166. package/modules/hall-ui/public/index.html +11 -4
  167. package/modules/hall-ui/public/instances-panel.js +4 -1
  168. package/modules/hall-ui/public/modules.css +63 -0
  169. package/modules/hall-ui/public/modules.html +3 -60
  170. package/modules/hall-ui/public/my-projects.html +78 -0
  171. package/modules/hall-ui/public/my-projects.js +142 -0
  172. package/modules/hall-ui/public/new-project-wizard.js +7 -3
  173. package/modules/hall-ui/public/not-ready.html +2 -0
  174. package/modules/hall-ui/public/pair.css +73 -0
  175. package/modules/hall-ui/public/pair.html +3 -70
  176. package/modules/hall-ui/public/palette.js +302 -0
  177. package/modules/hall-ui/public/primer.css +13 -0
  178. package/modules/hall-ui/public/primer.html +2 -0
  179. package/modules/hall-ui/public/profile.html +2 -0
  180. package/modules/hall-ui/public/ranks.html +2 -0
  181. package/modules/hall-ui/public/roadmap.css +179 -0
  182. package/modules/hall-ui/public/roadmap.html +9 -172
  183. package/modules/hall-ui/public/roadmap.js +329 -316
  184. package/modules/hall-ui/public/scouting.html +45 -0
  185. package/modules/hall-ui/public/scouting.js +73 -0
  186. package/modules/hall-ui/public/sessions.css +16 -0
  187. package/modules/hall-ui/public/sessions.html +3 -13
  188. package/modules/hall-ui/public/settings.html +2 -0
  189. package/modules/hall-ui/public/settings.js +4 -4
  190. package/modules/hall-ui/public/shell.js +143 -18
  191. package/modules/hall-ui/public/style.css +231 -39
  192. package/modules/hall-ui/public/task-detail.js +347 -26
  193. package/modules/hall-ui/public/task.html +6 -1
  194. package/modules/hall-ui/public/watch.html +2 -0
  195. package/modules/hall-ui/public/work.html +8 -6
  196. package/modules/hall-ui/public/work.js +1 -1
  197. package/modules/ideas/inbox.js +61 -11
  198. package/modules/ideas/routes/inbox.js +23 -0
  199. package/modules/lifecycle/claim-eligibility.js +72 -0
  200. package/modules/lifecycle/db.js +519 -139
  201. package/modules/lifecycle/done-when.js +18 -3
  202. package/modules/lifecycle/github-push.js +19 -2
  203. package/modules/lifecycle/goal-advisory.js +114 -0
  204. package/modules/lifecycle/goal-authz.js +62 -0
  205. package/modules/lifecycle/lifecycle.js +9 -0
  206. package/modules/lifecycle/publish-reconciler.js +109 -6
  207. package/modules/lifecycle/routes/claims.js +68 -23
  208. package/modules/lifecycle/routes/done-when.js +110 -15
  209. package/modules/lifecycle/routes/goals.js +8 -38
  210. package/modules/lifecycle/routes/tasks.js +38 -6
  211. package/modules/lifecycle/ship-card.js +6 -0
  212. package/modules/onboarding/onboarding-state.js +1 -1
  213. package/modules/platform-identity/CLAUDE.md +15 -0
  214. package/modules/platform-identity/hub-device.js +147 -0
  215. package/modules/platform-identity/hub-keys.js +151 -0
  216. package/modules/platform-identity/migrations/platform_identity_001_tables.sql +114 -0
  217. package/modules/platform-identity/migrations/platform_identity_002_client_origin_index.sql +18 -0
  218. package/modules/platform-identity/migrations/platform_identity_003_project_memberships.sql +42 -0
  219. package/modules/platform-identity/migrations/platform_identity_004_projects.sql +44 -0
  220. package/modules/platform-identity/migrations/platform_identity_005_builder_activity.sql +42 -0
  221. package/modules/platform-identity/module.json +18 -0
  222. package/modules/platform-identity/platform-identity.js +531 -0
  223. package/modules/platform-identity/pollers/code-sweep.js +43 -0
  224. package/modules/platform-identity/routes/my-projects.js +140 -0
  225. package/modules/platform-identity/routes/projects.js +107 -0
  226. package/modules/platform-identity/routes/scouting.js +35 -0
  227. package/modules/platform-identity/routes/sso.js +402 -0
  228. package/modules/platform-identity/tests/platform-identity.mjs +388 -0
  229. package/modules/platform-identity/ui/.gitkeep +0 -0
  230. package/modules/security/routes/reports.js +10 -0
  231. package/modules/sessions/db.js +166 -21
  232. package/modules/sessions/module.json +1 -1
  233. package/modules/sessions/pollers/session-reward-reconcile.js +78 -0
  234. package/modules/sessions/routes/sessions.js +33 -0
  235. package/modules/status-ui/public/explore.html +54 -0
  236. package/modules/status-ui/public/explore.js +67 -0
  237. package/package-lock.json +2 -2
  238. package/package.json +1 -1
  239. package/public-landing/assets/anim/idle/frame_00.webp +0 -0
  240. package/public-landing/assets/anim/idle/frame_01.webp +0 -0
  241. package/public-landing/assets/anim/idle/frame_02.webp +0 -0
  242. package/public-landing/assets/anim/idle/frame_03.webp +0 -0
  243. package/public-landing/assets/anim/idle/frame_04.webp +0 -0
  244. package/public-landing/assets/anim/idle/frame_05.webp +0 -0
  245. package/public-landing/assets/anim/idle/frame_06.webp +0 -0
  246. package/public-landing/assets/anim/idle/frame_07.webp +0 -0
  247. package/public-landing/assets/anim/idle/frame_08.webp +0 -0
  248. package/public-landing/assets/anim/idle/frame_09.webp +0 -0
  249. package/public-landing/assets/anim/idle/frame_10.webp +0 -0
  250. package/public-landing/assets/anim/idle/frame_11.webp +0 -0
  251. package/public-landing/assets/anim/idle/frame_12.webp +0 -0
  252. package/public-landing/assets/anim/idle/frame_13.webp +0 -0
  253. package/public-landing/assets/anim/idle/frame_14.webp +0 -0
  254. package/public-landing/assets/anim/idle/frame_15.webp +0 -0
  255. package/public-landing/assets/anim/idle/frame_16.webp +0 -0
  256. package/public-landing/assets/anim/idle/frame_17.webp +0 -0
  257. package/public-landing/assets/anim/idle/frame_18.webp +0 -0
  258. package/public-landing/assets/anim/idle/frame_19.webp +0 -0
  259. package/public-landing/assets/anim/idle/frame_20.webp +0 -0
  260. package/public-landing/assets/anim/idle/frame_21.webp +0 -0
  261. package/public-landing/assets/anim/idle/frame_22.webp +0 -0
  262. package/public-landing/assets/anim/idle/frame_23.webp +0 -0
  263. package/public-landing/assets/anim/idle/frame_24.webp +0 -0
  264. package/public-landing/assets/anim/idle/frame_25.webp +0 -0
  265. package/public-landing/assets/anim/idle/frame_26.webp +0 -0
  266. package/public-landing/assets/anim/idle/frame_27.webp +0 -0
  267. package/public-landing/assets/anim/idle/frame_28.webp +0 -0
  268. package/public-landing/assets/anim/idle/frame_29.webp +0 -0
  269. package/public-landing/assets/anim/idle/frame_30.webp +0 -0
  270. package/public-landing/assets/anim/idle/frame_31.webp +0 -0
  271. package/public-landing/assets/anim/idle/frame_32.webp +0 -0
  272. package/public-landing/assets/anim/idle/frame_33.webp +0 -0
  273. package/public-landing/assets/anim/idle/frame_34.webp +0 -0
  274. package/public-landing/assets/anim/idle/frame_35.webp +0 -0
  275. package/public-landing/assets/anim/idle/frame_36.webp +0 -0
  276. package/public-landing/assets/anim/idle/frame_37.webp +0 -0
  277. package/public-landing/assets/anim/idle/frame_38.webp +0 -0
  278. package/public-landing/assets/anim/idle/frame_39.webp +0 -0
  279. package/public-landing/assets/anim/idle/frame_40.webp +0 -0
  280. package/public-landing/assets/anim/idle/frame_41.webp +0 -0
  281. package/public-landing/assets/anim/idle/frame_42.webp +0 -0
  282. package/public-landing/assets/anim/idle/frame_43.webp +0 -0
  283. package/public-landing/create.html +252 -62
  284. package/public-landing/gate/README.md +137 -0
  285. package/public-landing/gate/dispmaps/bar.png +0 -0
  286. package/public-landing/gate/dispmaps/flat.png +0 -0
  287. package/public-landing/gate/dispmaps/key.png +0 -0
  288. package/public-landing/gate/index.html +587 -0
  289. package/public-landing/gate/previews/cloud-bongos.jpg +0 -0
  290. package/public-landing/gate/previews/off-the-boats.jpg +0 -0
  291. package/public-landing/gate/space.css +799 -0
  292. package/public-landing/gate/space.js +1826 -0
  293. package/public-landing/gate/sprites/enter.png +0 -0
  294. package/public-landing/gate/sprites/search.png +0 -0
  295. package/public-landing/gate/sprites/terminal-blank.png +0 -0
  296. package/public-landing/gate/sprites/tile.png +0 -0
  297. package/public-landing/world/assets/clouds/cloud-island-large.png +0 -0
  298. package/public-landing/world/assets/clouds/cloud-puff.png +0 -0
  299. package/public-landing/world/assets/creatures/creature-whale-cargo.png +0 -0
  300. package/public-landing/world/assets/creatures/creature-whale-glide.png +0 -0
  301. package/public-landing/world/assets/plates/plate-clouds-near.png +0 -0
  302. package/public-landing/world/assets/plates/scene-establishing.png +0 -0
  303. package/scripts/gds/abandon.js +6 -4
  304. package/scripts/gds/api.js +10 -5
  305. package/scripts/gds/api.ps1 +26 -0
  306. package/scripts/gds/api.sh +6 -0
  307. package/scripts/gds/audit-ships.js +7 -1
  308. package/scripts/gds/backfill-sessions.js +78 -18
  309. package/scripts/gds/backup.js +23 -1
  310. package/scripts/gds/box-connect-lib.js +124 -31
  311. package/scripts/gds/box-sync.js +66 -0
  312. package/scripts/gds/box.js +225 -53
  313. package/scripts/gds/cf-tunnel.js +145 -6
  314. package/scripts/gds/claim.js +34 -3
  315. package/scripts/gds/cli-lib.js +161 -21
  316. package/scripts/gds/code.js +6 -2
  317. package/scripts/gds/deploy-divergence.js +209 -0
  318. package/scripts/gds/dev-lib.js +309 -0
  319. package/scripts/gds/dev.js +322 -0
  320. package/scripts/gds/doctor.js +90 -61
  321. package/scripts/gds/fetch-art-key.js +1 -1
  322. package/scripts/gds/fetch-sound-prefs.js +2 -2
  323. package/scripts/gds/fitness.js +228 -12
  324. package/scripts/gds/gds-db-backup.service +16 -1
  325. package/scripts/gds/gds-db-offsite.service +9 -1
  326. package/scripts/gds/gen-api-client.js +1 -1
  327. package/scripts/gds/gen-api-docs.js +36 -14
  328. package/scripts/gds/gen-diagrams.js +45 -28
  329. package/scripts/gds/gen-file-map.js +2 -1
  330. package/scripts/gds/gen-freshness.js +32 -0
  331. package/scripts/gds/gen-repo-map.js +3 -2
  332. package/scripts/gds/gen-session-index.js +14 -5
  333. package/scripts/gds/hooks/pre-push.js +2 -2
  334. package/scripts/gds/init.js +60 -3
  335. package/scripts/gds/install-git-hooks.js +1 -1
  336. package/scripts/gds/key-card.js +15 -6
  337. package/scripts/gds/key-status.js +1 -1
  338. package/scripts/gds/linkify-refs.js +35 -9
  339. package/scripts/gds/lint-migrations.js +102 -0
  340. package/scripts/gds/login.js +58 -11
  341. package/scripts/gds/mirror-redact.js +5 -0
  342. package/scripts/gds/package-core.js +263 -4
  343. package/scripts/gds/paste-token.js +7 -1
  344. package/scripts/gds/preflight.js +126 -0
  345. package/scripts/gds/provision.js +268 -19
  346. package/scripts/gds/publish-manifest.js +22 -1
  347. package/scripts/gds/reauth.js +7 -3
  348. package/scripts/gds/regen-instance-docs.js +47 -0
  349. package/scripts/gds/run-unit-tests.js +42 -16
  350. package/scripts/gds/seed-bongos-v1-seo-tasks.js +104 -0
  351. package/scripts/gds/seed-bv1-profiles-tasks.js +126 -0
  352. package/scripts/gds/sequence.js +425 -0
  353. package/scripts/gds/session-cost.js +10 -1
  354. package/scripts/gds/setup.js +72 -5
  355. package/scripts/gds/shell.js +5 -3
  356. package/scripts/gds/ship.js +433 -59
  357. package/scripts/gds/smoke-gds.js +139 -8
  358. package/scripts/gds/start.js +171 -22
  359. package/scripts/gds/status.js +1 -1
  360. package/scripts/gds/triage.js +10 -9
  361. package/scripts/gds/upgrade.js +271 -7
  362. package/scripts/migrate.sh +10 -0
  363. package/scripts/status-mirror-sync.js +114 -84
  364. package/src/bongos/api-errors.js +1 -0
  365. package/src/bongos/auth.js +809 -107
  366. package/src/bongos/db-kernel.js +27 -0
  367. package/src/bongos/db.js +105 -0
  368. package/src/bongos/github-repos.js +93 -0
  369. package/src/bongos/module-scope-map.js +27 -1
  370. package/src/bongos/permission-path-check.js +136 -102
  371. package/src/bongos/repo-info.js +47 -11
  372. package/src/bongos/route-rank-check.js +429 -46
  373. package/src/bongos/routes/_helpers.js +12 -1
  374. package/src/bongos/routes/auth.js +312 -13
  375. package/src/bongos/routes/backup.js +58 -2
  376. package/src/bongos/routes/github.js +90 -0
  377. package/src/bongos/routes/instance.js +15 -3
  378. package/src/bongos/routes/me.js +35 -1
  379. package/src/bongos/routes.js +2 -0
  380. package/src/bongos/serve-internal.js +84 -15
  381. package/src/branding.js +49 -2
  382. package/src/instance-config.js +46 -0
  383. package/src/module-api.js +140 -1
  384. package/src/platform-server.js +86 -9
  385. package/tests/abandon_task.mjs +59 -3
  386. package/tests/api_cli_flags.mjs +53 -0
  387. package/tests/auth_page_gate.mjs +13 -0
  388. package/tests/backfill_sessions.mjs +127 -1
  389. package/tests/backup_cli.mjs +54 -0
  390. package/tests/bongos_box_connect.mjs +77 -0
  391. package/tests/box_access.mjs +130 -1
  392. package/tests/box_credential.mjs +125 -0
  393. package/tests/box_host_keys.mjs +24 -0
  394. package/tests/box_infra_resolve.mjs +149 -0
  395. package/tests/box_scope_session.mjs +6 -2
  396. package/tests/box_sync.mjs +55 -0
  397. package/tests/box_task_scope.mjs +99 -0
  398. package/tests/branding.mjs +41 -23
  399. package/tests/cf_ruleset_preflight.mjs +142 -0
  400. package/tests/cf_tunnel.mjs +37 -0
  401. package/tests/claim_gate_rebase.mjs +98 -0
  402. package/tests/clawback_confirmed_backlog.mjs +103 -0
  403. package/tests/cli_lib_formatters.mjs +20 -0
  404. package/tests/confirm_attribution.mjs +30 -46
  405. package/tests/credit_grant.mjs +198 -10
  406. package/tests/criterion_authoring_gate.mjs +272 -0
  407. package/tests/criterion_progress.mjs +33 -1
  408. package/tests/dev_lib.mjs +253 -0
  409. package/tests/devbox_artifact_gate.mjs +75 -0
  410. package/tests/devbox_dmg_backdrop.mjs +137 -0
  411. package/tests/devbox_icons.mjs +304 -0
  412. package/tests/devbox_installer_brand.mjs +232 -0
  413. package/tests/devbox_key_register.mjs +234 -0
  414. package/tests/docs_openapi_route.mjs +110 -0
  415. package/tests/doctor_hooks.mjs +141 -0
  416. package/tests/extract_json.mjs +140 -0
  417. package/tests/fitness.mjs +89 -0
  418. package/tests/gen_freshness.mjs +96 -0
  419. package/tests/github_repos_helpers.mjs +91 -0
  420. package/tests/github_token_store.mjs +121 -0
  421. package/tests/goal_advisory.mjs +251 -0
  422. package/tests/goal_criterion_confirm_ui.mjs +269 -0
  423. package/tests/goal_routes.mjs +59 -4
  424. package/tests/goal_tier.mjs +12 -3
  425. package/tests/governance_catalog.mjs +129 -0
  426. package/tests/governance_claim_eligibility.mjs +262 -0
  427. package/tests/governance_config.mjs +187 -0
  428. package/tests/governance_ownership_scope.mjs +410 -0
  429. package/tests/governance_page_gates.mjs +324 -0
  430. package/tests/governance_parity.mjs +336 -0
  431. package/tests/governance_principals.mjs +232 -0
  432. package/tests/governance_protected_surfaces.mjs +208 -0
  433. package/tests/governance_require_permission.mjs +223 -0
  434. package/tests/governance_resolver.mjs +84 -0
  435. package/tests/governance_role_sync.mjs +231 -0
  436. package/tests/governance_seed.mjs +127 -0
  437. package/tests/governance_session_scope.mjs +434 -0
  438. package/tests/governance_vanilla_boot.mjs +203 -0
  439. package/tests/grade_attribution.mjs +7 -7
  440. package/tests/hall_audit.mjs +223 -0
  441. package/tests/hall_nav.mjs +191 -0
  442. package/tests/hall_palette.mjs +206 -0
  443. package/tests/hall_signout.mjs +124 -0
  444. package/tests/hook_user_agents.mjs +10 -0
  445. package/tests/idea_triage.mjs +162 -0
  446. package/tests/idp_device_exchange.mjs +198 -0
  447. package/tests/idp_federation.mjs +100 -0
  448. package/tests/init.mjs +51 -0
  449. package/tests/instance_config.mjs +65 -0
  450. package/tests/instance_mismatch_guard.mjs +23 -6
  451. package/tests/instance_release_config.mjs +104 -0
  452. package/tests/linkify_refs.mjs +15 -2
  453. package/tests/migrations_forward_safe.mjs +86 -0
  454. package/tests/module_api.mjs +1 -1
  455. package/tests/module_contributions.mjs +55 -0
  456. package/tests/module_loader.mjs +10 -5
  457. package/tests/module_scope_active_claims.mjs +94 -0
  458. package/tests/needs_rebase.mjs +92 -0
  459. package/tests/one_active_claim_per_session.mjs +76 -0
  460. package/tests/override_attribution.mjs +10 -8
  461. package/tests/override_request_ownership.mjs +8 -4
  462. package/tests/package_core.mjs +246 -1
  463. package/tests/parse_cookie_decode.mjs +42 -0
  464. package/tests/pay_on_land.mjs +129 -0
  465. package/tests/platform_boot.mjs +100 -1
  466. package/tests/post_login_landing.mjs +42 -0
  467. package/tests/preflight_checks.mjs +72 -0
  468. package/tests/provision.mjs +289 -7
  469. package/tests/pruned_lockfile_integrity.mjs +79 -0
  470. package/tests/publish_branch_route.mjs +53 -0
  471. package/tests/publish_reconciler.mjs +187 -2
  472. package/tests/repo_info_binding.mjs +248 -0
  473. package/tests/reward_gate.mjs +11 -2
  474. package/tests/reward_mode_cost_plus_only.mjs +116 -0
  475. package/tests/reward_mode_surfacing.mjs +168 -0
  476. package/tests/route_permission_check.mjs +301 -0
  477. package/tests/route_rank_check.mjs +254 -12
  478. package/tests/route_validation.mjs +30 -0
  479. package/tests/sequence.mjs +267 -0
  480. package/tests/session_digest.mjs +44 -0
  481. package/tests/session_index.mjs +18 -6
  482. package/tests/session_records.mjs +39 -1
  483. package/tests/session_reward_on_land.mjs +381 -0
  484. package/tests/session_reward_reconcile.mjs +229 -0
  485. package/tests/ship_api_client_pathspec.mjs +100 -0
  486. package/tests/ship_base_flag.mjs +93 -0
  487. package/tests/ship_ci_deploy.mjs +140 -3
  488. package/tests/ship_upstream_deletion.mjs +57 -0
  489. package/tests/smoke_target.mjs +233 -0
  490. package/tests/start_env_guard.mjs +87 -0
  491. package/tests/start_rebase_warning.mjs +103 -0
  492. package/tests/status_mirror_sync.mjs +122 -0
  493. package/tests/stop_refcheck.mjs +19 -1
  494. package/tests/task_goal_link.mjs +15 -6
  495. package/tests/upgrade.mjs +393 -5
  496. package/tests/web_admission_status.mjs +162 -0
  497. package/tests/write_validation_gaps.mjs +110 -0
  498. package/.gitignore +0 -91
  499. package/docs/adr/0139-platform-history-migration-to-cloudbongos.md +0 -94
  500. package/docs/adr/0140-cloudbongos-standalone-reprovision.md +0 -43
  501. package/scripts/hall-preview/fixtures/access-requests.json +0 -20
  502. package/scripts/hall-preview/fixtures/achievements.json +0 -76
  503. package/scripts/hall-preview/fixtures/analytics__builder.json +0 -251
  504. package/scripts/hall-preview/fixtures/audit-log.json +0 -68
  505. package/scripts/hall-preview/fixtures/blockers__detail.json +0 -12
  506. package/scripts/hall-preview/fixtures/box__me.json +0 -5
  507. package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +0 -69
  508. package/scripts/hall-preview/fixtures/builders__active-claims.json +0 -1
  509. package/scripts/hall-preview/fixtures/builders__directory.json +0 -81
  510. package/scripts/hall-preview/fixtures/builders__roster.json +0 -48
  511. package/scripts/hall-preview/fixtures/goals.json +0 -502
  512. package/scripts/hall-preview/fixtures/goals__25.json +0 -96
  513. package/scripts/hall-preview/fixtures/goals__26.json +0 -67
  514. package/scripts/hall-preview/fixtures/goals__3.json +0 -103
  515. package/scripts/hall-preview/fixtures/goals__35.json +0 -49
  516. package/scripts/hall-preview/fixtures/goals__conflicts.json +0 -154
  517. package/scripts/hall-preview/fixtures/goals__detail.json +0 -96
  518. package/scripts/hall-preview/fixtures/goals__inbox.json +0 -5
  519. package/scripts/hall-preview/fixtures/goals__requests.json +0 -3
  520. package/scripts/hall-preview/fixtures/grades__by-builder.json +0 -32
  521. package/scripts/hall-preview/fixtures/inbox.json +0 -330
  522. package/scripts/hall-preview/fixtures/inbox__detail.json +0 -15
  523. package/scripts/hall-preview/fixtures/instance.json +0 -124
  524. package/scripts/hall-preview/fixtures/leaderboard.json +0 -184
  525. package/scripts/hall-preview/fixtures/me.json +0 -476
  526. package/scripts/hall-preview/fixtures/me__art-key__own.json +0 -6
  527. package/scripts/hall-preview/fixtures/me__event-sound-prefs.json +0 -209
  528. package/scripts/hall-preview/fixtures/me__recent-ships.json +0 -304
  529. package/scripts/hall-preview/fixtures/me__render.json +0 -5
  530. package/scripts/hall-preview/fixtures/me__skill-prefs.json +0 -143
  531. package/scripts/hall-preview/fixtures/me__sound-prefs.json +0 -67
  532. package/scripts/hall-preview/fixtures/me__wandering.json +0 -43
  533. package/scripts/hall-preview/fixtures/override-requests.json +0 -15
  534. package/scripts/hall-preview/fixtures/provisioning__fleet.json +0 -1
  535. package/scripts/hall-preview/fixtures/provisioning__fleet__cost-ledger.json +0 -1
  536. package/scripts/hall-preview/fixtures/provisioning__instances.json +0 -1
  537. package/scripts/hall-preview/fixtures/public__analytics__ecosystem.json +0 -516
  538. package/scripts/hall-preview/fixtures/public__bounty-table.json +0 -37
  539. package/scripts/hall-preview/fixtures/public__cost-summary.json +0 -742
  540. package/scripts/hall-preview/fixtures/public__estimation-drift.json +0 -41
  541. package/scripts/hall-preview/fixtures/public__grades.json +0 -50
  542. package/scripts/hall-preview/fixtures/public__progress.json +0 -124
  543. package/scripts/hall-preview/fixtures/public__recent-shipped.json +0 -426
  544. package/scripts/hall-preview/fixtures/public__repo-health.json +0 -160
  545. package/scripts/hall-preview/fixtures/public__repo-info.json +0 -5
  546. package/scripts/hall-preview/fixtures/security__docs.json +0 -10
  547. package/scripts/hall-preview/fixtures/security__reports.json +0 -31
  548. package/scripts/hall-preview/fixtures/sessions__search.json +0 -9
  549. package/scripts/hall-preview/fixtures/tasks.json +0 -2420
  550. package/scripts/hall-preview/fixtures/tasks__claimable.json +0 -2555
  551. package/scripts/hall-preview/fixtures/tasks__detail.json +0 -111
  552. package/scripts/hall-preview/fixtures/tasks__peer-votes__feed.json +0 -330
  553. package/scripts/hall-preview/fixtures/versions.json +0 -89
@@ -104,8 +104,8 @@ const LIFECYCLE_EDGES = [
104
104
  ['blocked', 'active', 'blocker resolved'],
105
105
  ['active', 'ready', '/builder-release (no credits)'],
106
106
  ['active', 'completed', '/builder-ship: declared done + smoke tests'],
107
- ['completed', 'confirmed', 'grader passes ◀ CREDITS AWARDED HERE'],
108
- ['confirmed', 'shipped', 'CI + gate-review → auto-merge + deploy'],
107
+ ['completed', 'confirmed', 'grader passes (verified — not yet paid)'],
108
+ ['confirmed', 'shipped', 'CI + gate-review → auto-merge + deploy ◀ CREDITS AWARDED HERE'],
109
109
  ['backlog', 'abandoned', 'dropped'],
110
110
  ];
111
111
 
@@ -297,7 +297,7 @@ function drachmaeKarmaMmd(f) {
297
297
  ` subgraph CURRENCY["${CUR} (the builder reward — equity / reputation, a stake, not a spendable currency)"]`,
298
298
  ' direction TB',
299
299
  ' D_TOKEN["Ship a SESSION — the main reward\\nround(true_cost_usd × 1.20) cents — cost-plus on real token spend\\n(LIVE: priced server-side from token counts; ADR 0054, idempotent per session)"]',
300
- ' D_SHIP["Confirm a TASK\\ncredits_reward × kind multiplier (0.8–1.5×)\\n+ 25% idea-promotion bonus to the capturer\\n(LIVE: ADR 0023)"]',
300
+ ' D_SHIP["SHIP a TASK — books when the code lands on main, not at confirm\\ncredits_reward × kind multiplier (0.8–1.5×)\\n+ 25% idea-promotion bonus to the capturer\\n(LIVE: ADR 0023; pay-on-land ADR 0120; withheld entirely on a cost-plus-only instance, ADR 0146)"]',
301
301
  ` D_ACH["Unlock an achievement\\nachievements.credit_bonus (LIVE: ${nAch} seeded)\\n+ small net-negative-ship bonus"]`,
302
302
  ` D_BOUNTY["Red-team / security bounty\\n${currency} payout by severity\\n(rate card ${rateCard ? 'LIVE' : 'PLANNED'}: R65 #284 — src/bongos/security.js + shown on /builders;\\nawarding still ${awarding ? 'LIVE' : 'PLANNED'}: R67 #286 + vulnerability_reports R63 #282)"]`,
303
303
  '',
@@ -375,10 +375,18 @@ function architectureMmd(f) {
375
375
  ? 'GitHub App, short-lived tokens'
376
376
  : 'standing push PAT (GitHub App pending — 1028)';
377
377
  // The whole box→prod chain LIVE? (all the gated legs + the end-to-end verify.)
378
- const chainLive = [919, 897, 904, 774, 1025, 859, 1028, 1027].every(shipped);
378
+ // NB the deploy leg is the standalone AUTO-PULL (ADR 0108/0129), not a task-gated
379
+ // GitHub-Actions CI job — so 859 (CI deploy) is intentionally NOT in this set.
380
+ const chainLive = [919, 897, 904, 774, 1025, 1028, 1027].every(shipped);
379
381
  const closingNote = chainLive
380
- ? 'The whole box → production path is LIVE and hardware-verified end-to-end (1027 · 1028).'
381
- : `Still landing (PLANNED): hardware-verify the box → prod flow end-to-end (${tag(1027)} · 1027) and the GitHub App push credential (${tag(1028)} · 1028).`;
382
+ ? 'The whole box → production path is LIVE: a credential-less box uploads its branch, the server merges it, and the instance auto-pulls + redeploys — hardware-verified end-to-end (1027 · 1028).'
383
+ : `Still landing (PLANNED): the server-mediated publish (${tag(1025)} · 1025) and the GitHub App push credential (${tag(1028)} · 1028).`;
384
+
385
+ // Product-neutral runtime label: the instance's own product name (branding),
386
+ // NOT a hard-coded "game" (this core powers any app, not only the reference
387
+ // game instance). Falls back to a generic phrase when branding carries none.
388
+ const productName = brand().productName;
389
+ const appLabel = productName && productName !== 'Cloud Bongos' ? productName : 'app';
382
390
 
383
391
  // Layout: ELK engine, top-down (task 1066). ELK minimizes edge crossings far
384
392
  // better than the default renderer — this map is edge-dense and was a tangle
@@ -390,16 +398,18 @@ function architectureMmd(f) {
390
398
  // low-signal annotation edges (box-token→bootstrap, DB-is-king, memory-
391
399
  // follows-you) are folded into node labels. (3) WATERFALL ordering (task 1073,
392
400
  // off Lars's "put the GitHub box last"): subgraphs are declared in flow order
393
- // DEVICE→BOX→SERVER→GH, and the live game is pulled OUT of the SERVER cluster
394
- // into its own terminal node at the very bottom (annotated "same droplet · same
395
- // Node process" so it stays honest). That makes the map one monotonic top-down
396
- // spine — device→box→server→github→live game — where the deploy is the LAST
397
- // forward step instead of an edge looping back UP into the server. So SERVER
398
- // tucks directly under BOX and the five BOX↔SERVER wires (re-auth, upload,
399
- // sync, pull, claim/ship) become short hops instead of detouring the full
400
- // height past a mid-stack GitHub. The redundant unlabelled ACTIONS→API edge is
401
- // dropped: API and the game are the same Node process, so the single labelled
402
- // ACTIONS→GAME deploy edge already carries it. NOTE on direction: we keep TB,
401
+ // DEVICE→BOX→SERVER→GH, and the live app (the instance's product runtime) is
402
+ // pulled OUT of the SERVER cluster into its own terminal node at the very
403
+ // bottom (annotated "same server · same Node process" so it stays honest).
404
+ // That makes the map one monotonic top-down spine — device→box→server→github→
405
+ // live app — where the deploy is the LAST forward step instead of an edge
406
+ // looping back UP into the server. So SERVER tucks directly under BOX and the
407
+ // BOX↔SERVER wires (re-auth, upload, sync, pull, claim/ship) become short hops
408
+ // instead of detouring the full height past a mid-stack GitHub. The deploy is
409
+ // the STANDALONE auto-pull (ADR 0108/0129): merge to the instance's own main →
410
+ // the server pulls its own repo → migrate/regenerate/restart → the live app —
411
+ // NOT a GitHub-Actions push-deploy (that was the reference monorepo's model).
412
+ // NOTE on direction: we keep TB,
403
413
  // not a repo-on-the-left LR/RL — the flow is an ~8-deep linear chain, so a
404
414
  // sideways layout stretches into an unreadable ~5:1 strip; top-down keeps a
405
415
  // legible aspect.
@@ -419,25 +429,26 @@ function architectureMmd(f) {
419
429
  ` CODE["the code — git clone + per-builder worktrees (.claude/worktrees/*)\\nfor sub-Metic builders it lives ONLY here · CLAUDE.md is DESCRIPTIVE only"]`,
420
430
  ' LOCAL["local ~/.claude memory\\nMAP = MEMORY.md — a one-line index loaded EVERY session\\n(a guard + auto-tidy keep it light; ADR 0074)\\n+ drawer of full notes, loaded only on demand"]',
421
431
  ' ORIGIN["credential-less git origin\\n(NO GitHub push secret on the box — by design)"]',
422
- ` BOXTOK["box-scoped GDS token\\n(deny-by-default: bootstrap endpoints only;\\nrejected for branch upload → re-auth first — ${tag(919)})"]`,
432
+ ` BOXTOK["box-scoped Bongos token\\n(deny-by-default: bootstrap endpoints only;\\nrejected for branch upload → re-auth first — ${tag(919)})"]`,
423
433
  ` PREVIEW["sandbox preview server on :3000\\n→ a per-builder sandbox URL (${tag(897)})"]`,
424
434
  ` SWITCH["on / off + browser terminal\\n(desktop app ${tag(904)} · ttyd-over-tunnel ${tag(774)})"]`,
425
435
  ' end',
426
436
  '',
427
- ` subgraph SERVER["THE SERVER — ${host} (the authority AND production)"]`,
428
- ' API["/api/gds/* (same Node process as the game)"]',
437
+ ` subgraph SERVER["THE INSTANCE SERVER — ${host} (the authority AND production)"]`,
438
+ ' API["/api/bongos/* (same Node process as the app)"]',
439
+ ' CORE["@bongos/core — the build platform, VENDORED as a pinned dependency\\n(ADR 0108: central updates are a version-pin bump via `bongos upgrade`, not a merge)"]',
429
440
  ' DB["Postgres — tasks · claims · versions · builders.rank\\ncredit_log · grades · idea_inbox · builder_memory\\n(KING for work-state + permissions; CLAUDE.md only describes it)"]',
430
441
  ' RANK["auth.requireRank — checked per request, NO caching\\n(ADR 0016: a demotion takes effect on the next request)"]',
431
442
  ` SRVMEM["server-side builder_memory store\\n${memLead}: per-builder · isolated · Archon-audited forensic read\\nyour memory follows you across machines & worktrees\\n/recall fetches only the relevant subset (ADR 0060)\\n(${memStatus})"]`,
432
443
  ` PUSHTOK["server-held push credential\\n(${pushCred} — the box never holds it)"]`,
444
+ ' DEPLOY["auto-pull deploy (ADR 0129)\\na timer pulls THIS instance repo every ~2 min →\\nnpm ci if the pin changed → migrate → regenerate docs → restart → healthz"]',
433
445
  ' end',
434
446
  '',
435
- ` subgraph GH["GITHUB — the code repository (${c.repoSlug})"]`,
436
- ' MAIN["branch-protected main\\n(no direct push; PR + required checks only)"]',
437
- ' ACTIONS["GitHub Actions — deploy-prod.yml"]',
447
+ ` subgraph GH["GITHUB — the instance's own repo (${c.repoSlug})"]`,
448
+ ' MAIN["branch-protected main\\n(no direct push; PR + required checks only)\\nholds config + the pinned @bongos/core — NOT the platform source"]',
438
449
  ' end',
439
450
  '',
440
- ` GAME["the live game — ${host}\\n(same server · same Node process as /api/gds/*)"]`,
451
+ ` LIVEAPP["the live ${appLabel} — ${host}\\n(same server · same Node process as /api/bongos/*)"]`,
441
452
  '',
442
453
  ' APP -->|"1 · toggle box on / open terminal"| SWITCH',
443
454
  ' SWITCH --> WORKER',
@@ -445,8 +456,9 @@ function architectureMmd(f) {
445
456
  ' WORKER -->|"3 · re-auth in browser"| API',
446
457
  flow('WORKER', 1025, '4 · upload branch as a git bundle', 'API'),
447
458
  flow('API', 1025, `5 · push + open PR + auto-merge (${tag(1025)})`, 'MAIN'),
448
- flow('MAIN', 859, `6 · merge triggers deploy (${tag(859)})`, 'ACTIONS'),
449
- ' ACTIONS -->|"7 · deploy (no droplet key on any box)"| GAME',
459
+ ' MAIN -->|"6 · the instance auto-pulls its own main"| DEPLOY',
460
+ ' DEPLOY -->|"7 · migrate · regenerate docs · restart"| LIVEAPP',
461
+ ' CORE --- API',
450
462
  ' PUSHTOK -.-> API',
451
463
  ' ORIGIN -. "direct push blocked" .-> MAIN',
452
464
  ' API -. "claim / ship / cost" .-> WORKER',
@@ -454,7 +466,7 @@ function architectureMmd(f) {
454
466
  ` ${pullEdge}`,
455
467
  '',
456
468
  ` NEXT["${closingNote}"]`,
457
- ' GAME --- NEXT',
469
+ ' LIVEAPP --- NEXT',
458
470
  ];
459
471
  return lines.join('\n') + '\n';
460
472
  }
@@ -467,6 +479,11 @@ function rebuildAssertions(prev, f) {
467
479
  next.rank_ladder.default_rank = f.default_rank;
468
480
  next.rank_ladder.wired_ranks = f.wired_ranks;
469
481
  next.task_lifecycle.task_statuses = f.task_statuses;
482
+ // Derived from LIFECYCLE_EDGES so this assertion can never drift from the
483
+ // rendered diagram again (it silently outlived ADR 0120's move of the award
484
+ // from completed→confirmed to confirmed→shipped).
485
+ const creditEdge = LIFECYCLE_EDGES.find(([, , label]) => label.includes('CREDITS AWARDED HERE'));
486
+ if (creditEdge) next.task_lifecycle.credits_awarded_at = `${creditEdge[0]}->${creditEdge[1]}`;
470
487
  // Persist only the stable identity fields (drop started_at ordering helper).
471
488
  next.versioning.diagram = '05-versioning';
472
489
  next.versioning.versions = f.versions.map((v) => ({ id: v.id, status: v.status }));
@@ -485,11 +502,11 @@ function rebuildAssertions(prev, f) {
485
502
 
486
503
  // 04-architecture — the ADR 0024 memory chain + the dev-box / box→prod ship
487
504
  // flow, merged (task 1059). A piece is LIVE iff its backing task is shipped.
488
- const arch = split([273, 274, 275, 276, 904, 774, 897, 919, 1025, 859, 1027, 1028]);
505
+ const arch = split([273, 274, 275, 276, 904, 774, 897, 919, 1025, 1027, 1028]);
489
506
  next.architecture = {
490
507
  diagram: '04-architecture',
491
508
  _comment:
492
- "Pieces of 04-architecture gated on a backing GDS task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/gds/gen-diagrams.js in lockstep with the diagram, so this is the drift checker's backstop. The device/box/server/GitHub topology is structural (not task-gated); these are the LIVE/PLANNED markers. MEMORY layer (ADR 0024): 273 schema / 274 sync-on-ship / 275 pull-on-start / 276 isolation. DEV BOX + box→prod: 904/774 (on-off + browser terminal), 897 (sandbox preview), 919 (scoped box token), 1025 (server-mediated publish), 859 (CI deploy), 1028 (GitHub App push credential), 1027 (end-to-end hardware-verify). Supersedes the old server_repo_memory + dev_box slices.",
509
+ "Pieces of 04-architecture gated on a backing GDS task. A piece is LIVE iff its task is 'shipped'; live_tasks must ALL be shipped, pending_tasks must NOT yet be. Rebuilt by scripts/gds/gen-diagrams.js in lockstep with the diagram, so this is the drift checker's backstop. The device/box/server/GitHub topology is structural (not task-gated); these are the LIVE/PLANNED markers. MEMORY layer (ADR 0024): 273 schema / 274 sync-on-ship / 275 pull-on-start / 276 isolation. DEV BOX + box→prod: 904/774 (on-off + browser terminal), 897 (sandbox preview), 919 (scoped box token), 1025 (server-mediated publish), 1028 (GitHub App push credential), 1027 (end-to-end hardware-verify). The deploy leg is the standalone AUTO-PULL (ADR 0108/0129), not a task-gated GitHub-Actions CI job, so 859 is intentionally absent. Supersedes the old server_repo_memory + dev_box slices.",
493
510
  live_tasks: arch.live_tasks,
494
511
  pending_tasks: arch.pending_tasks,
495
512
  };
@@ -32,6 +32,7 @@
32
32
  const fs = require('node:fs');
33
33
  const path = require('node:path');
34
34
  const { resolveInstanceRoot } = require('../../src/instance-config'); // ADR 0108 §1: configurable-root
35
+ const { sameContent } = require('./gen-freshness'); // eol-insensitive freshness (CRLF checkouts)
35
36
 
36
37
  // ADR 0108 §Consequences #4 (configurable-root W6 / task 1886) — every target of this generator is
37
38
  // INSTANCE-materialized content: the .claude/skills + .claude/scheduled-tasks it lists (materialized
@@ -141,7 +142,7 @@ function computeWrites() {
141
142
  if (next.indexOf(begin(m.dir + '/')) === -1) { markersMissing.push(m.dir); continue; }
142
143
  next = injectSection(next, m);
143
144
  }
144
- if (next !== cur) writes.push({ full: MAP_PATH, content: next, rel: 'docs/file-map.md' });
145
+ if (!sameContent(next, cur)) writes.push({ full: MAP_PATH, content: next, rel: 'docs/file-map.md' });
145
146
  }
146
147
  return { model, writes, missing, stale, markersMissing };
147
148
  }
@@ -0,0 +1,32 @@
1
+ // gen-freshness.js — how the doc generators decide "is the committed copy stale?"
2
+ //
3
+ // Shared by gen-repo-map / gen-session-index / gen-file-map / gen-api-docs, whose
4
+ // `--check` / `--check-block` modes back the fitness.js `generated artifacts fresh`
5
+ // hard gate.
6
+ //
7
+ // Why this exists: the generators render their blocks with `join('\n')` — always
8
+ // LF — but read the working copy with readFileSync, which on a `core.autocrlf=true`
9
+ // checkout (every default Windows clone) returns CRLF. A byte-wise `next !== cur`
10
+ // then reports STALE forever, and regenerating never clears it: the write lands as
11
+ // LF, git normalizes it back to CRLF on the next checkout, and the gate is red
12
+ // again. That is worse than noisy — a permanently-failing hard gate trains builders
13
+ // to ignore it, and it hides REAL drift, since the check is red either way.
14
+ //
15
+ // Freshness is a question about content, so compare content. Written bytes are
16
+ // unaffected: callers still write exactly the string they built, and git applies
17
+ // its own eol policy on commit.
18
+ 'use strict';
19
+
20
+ // CRLF → LF. Lone-CR (classic Mac) is not normalized; no generator emits it and
21
+ // no checkout policy produces it, so treating it as content keeps this honest.
22
+ function normalizeEol(s) {
23
+ return String(s == null ? '' : s).replace(/\r\n/g, '\n');
24
+ }
25
+
26
+ // True when two documents are identical apart from line-ending convention.
27
+ // Use this for every freshness comparison — never `===` on raw file bytes.
28
+ function sameContent(a, b) {
29
+ return normalizeEol(a) === normalizeEol(b);
30
+ }
31
+
32
+ module.exports = { normalizeEol, sameContent };
@@ -35,6 +35,7 @@ const { resolveCoreRoot, resolveInstanceRoot } = require('../../src/instance-con
35
35
  // build the doc link-graph + staleness flags. Requiring it is side-effect-free
36
36
  // (its CLI body is require.main-guarded).
37
37
  const docsEntropy = require('./docs-entropy');
38
+ const { sameContent } = require('./gen-freshness'); // eol-insensitive freshness (CRLF checkouts)
38
39
 
39
40
  // ADR 0108 §Consequences #4 (configurable-root W6 / task 1886) — the generator scans TWO trees.
40
41
  // CORE code + the core-owned nested docs live under the core PACKAGE (resolveCoreRoot); the
@@ -475,7 +476,7 @@ function main() {
475
476
  const mapPath = path.join(REPO_ROOT, 'docs', 'repo-map.md');
476
477
  const mapNext = renderRepoMap(model);
477
478
  const mapCur = fs.existsSync(mapPath) ? fs.readFileSync(mapPath, 'utf8') : '';
478
- if (mapCur !== mapNext) writes.push({ full: mapPath, content: mapNext, rel: 'docs/repo-map.md', bucket: 'whole' });
479
+ if (!sameContent(mapCur, mapNext)) writes.push({ full: mapPath, content: mapNext, rel: 'docs/repo-map.md', bucket: 'whole' });
479
480
 
480
481
  // 2) nested CLAUDE.md generated blocks (tracked; the HARD-gated bucket)
481
482
  for (const dir of NESTED_DIRS) {
@@ -486,7 +487,7 @@ function main() {
486
487
  }
487
488
  const cur = fs.readFileSync(docPath, 'utf8');
488
489
  const next = injectBlock(cur, renderNestedBlock(model, dir));
489
- if (cur !== next) writes.push({ full: docPath, content: next, rel: `${dir}/CLAUDE.md`, bucket: 'block' });
490
+ if (!sameContent(cur, next)) writes.push({ full: docPath, content: next, rel: `${dir}/CLAUDE.md`, bucket: 'block' });
490
491
  }
491
492
 
492
493
  if (checkAll || checkWhole || checkBlock) {
@@ -30,8 +30,17 @@
30
30
 
31
31
  const fs = require('node:fs');
32
32
  const path = require('node:path');
33
-
34
- const REPO_ROOT = path.resolve(__dirname, '..', '..');
33
+ const { resolveInstanceRoot } = require('../../src/instance-config'); // ADR 0108 §1: configurable-root
34
+ const { sameContent } = require('./gen-freshness'); // eol-insensitive freshness (CRLF checkouts)
35
+
36
+ // The session log, its index, and the CLAUDE.md §13 snippet are all HOST (instance) content —
37
+ // docs/session-logs/ + docs/session-log-index.md + the instance root CLAUDE.md. Resolve them under
38
+ // the host repo so a STANDALONE instance (core in node_modules, ADR 0108) indexes its OWN sessions,
39
+ // not the vendored core's. On the monorepo/at-ship this still resolves to the checkout (cwd
40
+ // fallback). Do NOT reintroduce a hardcoded `path.resolve(__dirname,'..','..')` — post-split it
41
+ // points INTO node_modules/@bongos/core (the exact bug gen-repo-map/gen-file-map warn against; the
42
+ // reason gen-session-index was writing the core's index on a standalone box — task 1002314).
43
+ const REPO_ROOT = resolveInstanceRoot();
35
44
  const LOGS_DIR = path.join(REPO_ROOT, 'docs', 'session-logs');
36
45
  const INDEX_PATH = path.join(REPO_ROOT, 'docs', 'session-log-index.md');
37
46
  const CLAUDE_PATH = path.join(REPO_ROOT, 'CLAUDE.md');
@@ -100,7 +109,7 @@ function renderIndex(entries) {
100
109
  const L = [];
101
110
  L.push('# Session log — index');
102
111
  L.push('');
103
- L.push('> **AUTO-GENERATED — do not hand-edit.** Regenerate with `node scripts/gds/gen-session-index.js` (auto-regenerated + committed at ship, like `gen-repo-map.js`). Generated from the files in [`session-logs/`](session-logs/) so a link can never dangle (retires the recurring §13 drift: idea 57, [#580](https://amazonprimea.com/builders#/task/580)). The session PROSE is canonical in those files (older entries compressed into [`session-log-archive.md`](session-log-archive.md)); the DB session corpus (`session_logs` + the BFG `session_records`) is the structured record. See [ADR 0062](adr/0062-medusa-instance-model-decoupling.md) §8 · [ADR 0063](adr/0063-generated-repo-map-symbol-skeleton.md).');
112
+ L.push('> **AUTO-GENERATED — do not hand-edit.** Regenerate with `node scripts/gds/gen-session-index.js` (auto-regenerated + committed at ship, like `gen-repo-map.js`). Generated from the files in [`session-logs/`](session-logs/) so a link can never dangle (retires the recurring §13 drift: idea 57, [#580](https://example.com/builders#/task/580)). The session PROSE is canonical in those files (older entries compressed into [`session-log-archive.md`](session-log-archive.md)); the DB session corpus (`session_logs` + the BFG `session_records`) is the structured record. See [ADR 0062](adr/0062-medusa-instance-model-decoupling.md) §8 · [ADR 0063](adr/0063-generated-repo-map-symbol-skeleton.md).');
104
113
  L.push('');
105
114
  L.push(`**${entries.length} sessions**, newest first.`);
106
115
  L.push('');
@@ -144,12 +153,12 @@ function computeWrites() {
144
153
  // stays a HARD CI gate. main() gates them separately (--check-block vs --check-whole).
145
154
  const idxNext = renderIndex(entries);
146
155
  const idxCur = fs.existsSync(INDEX_PATH) ? fs.readFileSync(INDEX_PATH, 'utf8') : '';
147
- if (idxCur !== idxNext) writes.push({ full: INDEX_PATH, content: idxNext, rel: 'docs/session-log-index.md', bucket: 'whole' });
156
+ if (!sameContent(idxCur, idxNext)) writes.push({ full: INDEX_PATH, content: idxNext, rel: 'docs/session-log-index.md', bucket: 'whole' });
148
157
 
149
158
  if (fs.existsSync(CLAUDE_PATH)) {
150
159
  const cur = fs.readFileSync(CLAUDE_PATH, 'utf8');
151
160
  const next = injectSnippet(cur, renderSnippet(entries));
152
- if (cur !== next) {
161
+ if (!sameContent(cur, next)) {
153
162
  if (next === cur && cur.indexOf(BEGIN) === -1) {
154
163
  console.error('gen-session-index: WARN CLAUDE.md has no snippet markers — skipping snippet injection.');
155
164
  } else {
@@ -28,7 +28,7 @@ const { execSync } = require('node:child_process');
28
28
  const path = require('node:path');
29
29
 
30
30
  const ppc = require(path.join(__dirname, '..', '..', '..', 'src', 'bongos', 'permission-path-check.js'));
31
- const { apiCall } = require(path.join(__dirname, '..', 'cli-lib.js'));
31
+ const { apiCall, productName } = require(path.join(__dirname, '..', 'cli-lib.js'));
32
32
 
33
33
  const ZERO = /^0+$/;
34
34
  const MAIN_REF = 'refs/heads/main';
@@ -82,7 +82,7 @@ async function liveRank() {
82
82
  }
83
83
 
84
84
  function block(lines) {
85
- console.error('\n\x1b[31m✖ push blocked by the Off the Boats permission gate (SEC #863)\x1b[0m');
85
+ console.error(`\n\x1b[31m✖ push blocked by the ${productName()} permission gate (SEC #863)\x1b[0m`);
86
86
  for (const l of lines) console.error(' ' + l);
87
87
  console.error('');
88
88
  process.exit(1);
@@ -543,12 +543,51 @@ async function interview({ adopt = false } = {}) {
543
543
  // Write + seed
544
544
  // ---------------------------------------------------------------------------
545
545
 
546
+ // PURE (exported for tests) — the root START-HERE guide (task 1002404, goal
547
+ // 1000052). A freshly-CLONED instance must tell a human what to run FIRST: the
548
+ // `bongos` CLI ships INSIDE the not-yet-installed @bongos/core package, so
549
+ // `npm install` has to come before any `bongos` command (including `bongos dev`).
550
+ // `bongos init` runs npm install during scaffolding, but a later `git clone` of
551
+ // the committed instance repo skips that — so this file is the signpost the clone
552
+ // carries. Owner-friendly and plain-language (the owner may not be an engineer).
553
+ function buildReadme(branding = {}) {
554
+ const identity = branding.identity || {};
555
+ const name = identity.productName || 'This Cloud Bongos instance';
556
+ const origin = (branding.domains && branding.domains.publicOrigin) || '';
557
+ const loginLine = origin
558
+ ? `3. \`bongos login ${origin}\` — only if you're signed in to a *different* instance (\`bongos dev\` signs you in the first time).`
559
+ : `3. \`bongos login <your-instance-url>\` — only if you're signed in to a *different* instance (\`bongos dev\` signs you in the first time).`;
560
+ return [
561
+ `# ${name}`,
562
+ '',
563
+ 'A **Cloud Bongos** instance — an AI-first build platform: AI agents claim tasks and write the code; a human owner sets scope and the gates.',
564
+ '',
565
+ '## Start here (first run)',
566
+ '',
567
+ 'Just cloned this repo? Run these **in order**. The `bongos` CLI ships *inside* the `@bongos/core` dependency, so step 1 comes first — until it runs there is no `bongos` command:',
568
+ '',
569
+ '1. `npm install` — installs the toolchain (this is what creates the `bongos` command).',
570
+ '2. `bongos dev` — one command to run this instance locally: starts Postgres, builds the tables, launches the server, and signs you in.',
571
+ loginLine,
572
+ '4. `bongos start` — see what you can work on. (Inside Claude Code, the same thing is `/builder-start`.)',
573
+ '',
574
+ 'Requires Node >= 22 and Postgres installed locally (macOS/Linux; on Windows use WSL). If anything looks off, `bongos doctor` checks your setup and prints the fix.',
575
+ '',
576
+ '## Where to look next',
577
+ '',
578
+ '- `CLAUDE.md` — how this project is built (the operating manual agents read first).',
579
+ '- `bongos --help` — the full CLI: claim, ship, status, recall, and more.',
580
+ '',
581
+ ].join('\n');
582
+ }
583
+
546
584
  function writeConfigs(spec, { dir, force, dryRun, coreDep, log = console.log }) {
547
585
  const configDir = path.join(dir, 'config');
548
586
  const brandingPath = path.join(configDir, 'branding.json');
549
587
  const modulesPath = path.join(configDir, 'modules.json');
550
588
  const hierarchyPath = path.join(configDir, 'hierarchy.json');
551
589
  const pkgPath = path.join(dir, 'package.json');
590
+ const readmePath = path.join(dir, 'README.md');
552
591
  const branding = buildBrandingConfig(spec);
553
592
  const modules = buildModulesConfig(spec);
554
593
  const hierarchy = buildHierarchyConfig(spec);
@@ -574,7 +613,8 @@ function writeConfigs(spec, { dir, force, dryRun, coreDep, log = console.log })
574
613
  log(JSON.stringify(hierarchy, null, 2));
575
614
  log(`\n[dry-run] would write ${pkgPath} (pins ${CORE_PKG} → ${pinNote}):`);
576
615
  log(JSON.stringify(pkg, null, 2));
577
- return { brandingPath, modulesPath, hierarchyPath, pkgPath, wrote: false };
616
+ log(`\n[dry-run] would write ${readmePath} (root START-HERE first-run guide) if absent`);
617
+ return { brandingPath, modulesPath, hierarchyPath, pkgPath, readmePath, wrote: false };
578
618
  }
579
619
  if (fs.existsSync(brandingPath) && !force) {
580
620
  throw new Error(`${brandingPath} already exists — refusing to overwrite. Use --force to replace, or run init in a fresh --dir.`);
@@ -588,7 +628,15 @@ function writeConfigs(spec, { dir, force, dryRun, coreDep, log = console.log })
588
628
  log(` ✓ wrote ${path.relative(dir, modulesPath)}`);
589
629
  log(` ✓ wrote ${path.relative(dir, hierarchyPath)}`);
590
630
  log(` ✓ wrote ${path.relative(dir, pkgPath)} (pins ${CORE_PKG} → ${pinNote})`);
591
- return { brandingPath, modulesPath, hierarchyPath, pkgPath, wrote: true };
631
+ // README START-HERE (task 1002404): write-if-absent so a --force re-init or an
632
+ // owner's own README is never clobbered.
633
+ if (!fs.existsSync(readmePath)) {
634
+ fs.writeFileSync(readmePath, buildReadme(branding));
635
+ log(` ✓ wrote ${path.relative(dir, readmePath)} (first-run START-HERE)`);
636
+ } else {
637
+ log(` • ${path.relative(dir, readmePath)} already present — left untouched`);
638
+ }
639
+ return { brandingPath, modulesPath, hierarchyPath, pkgPath, readmePath, wrote: true };
592
640
  }
593
641
 
594
642
  // `--adopt` config layering (ADR 0121 §Decision 2 / task 2042). The additive
@@ -955,11 +1003,20 @@ async function main(argv) {
955
1003
  // catches any lag on the consumer's first CI.
956
1004
  if (dryRun) {
957
1005
  console.log('\n[dry-run] would regenerate the API artifacts (OpenAPI spec + reference + typed client) from the pinned core');
1006
+ console.log('[dry-run] would seed the whole-file navigation docs (session-log index) from the pinned core');
958
1007
  } else {
959
1008
  console.log('\nRegenerating the API artifacts from the pinned core (OpenAPI spec + reference + typed client):');
960
1009
  const regen = upgrade.regenerateApiArtifacts({ instanceDir: dir, coreRoot: resolveCoreRoot() });
961
1010
  if (regen.ok) console.log(` ✓ regenerated (${regen.ran.join(', ')}) → docs/api/ + docs/api-reference.md + clients/bongos-client/`);
962
1011
  else console.error(` ! API-artifact regen skipped (${regen.reason}) — regenerate in ${dir} by hand; the instance-root fitness gate flags the lag otherwise.`);
1012
+ // Seed the whole-file navigation docs (session-log index) so a fresh instance's docs/ isn't
1013
+ // empty before its first deploy-regen (ADR 0110 / task 1002314). Gitignored on the instance —
1014
+ // this is a local seed; the ongoing refresh is regen-instance-docs.js on deploy + `bongos
1015
+ // upgrade`. Best-effort, mirrors the api-artifacts line: a hiccup warns, never fails init.
1016
+ console.log('\nSeeding the navigation docs from the pinned core (session-log index):');
1017
+ const nav = upgrade.regenerateNavDocs({ instanceDir: dir, coreRoot: resolveCoreRoot() });
1018
+ if (nav.ok) console.log(` ✓ seeded (${nav.ran.join(', ')}) → docs/`);
1019
+ else console.error(` ! nav-doc seed incomplete (failed: ${nav.failed.map((f) => f.gen).join(', ')}) — the deploy regen (regen-instance-docs.js) retries.`);
963
1020
  }
964
1021
 
965
1022
  let seedResult = {};
@@ -979,7 +1036,7 @@ async function main(argv) {
979
1036
  }
980
1037
 
981
1038
  module.exports = {
982
- buildBrandingConfig, buildModulesConfig, buildHierarchyConfig, kickoffTasks, normalizeSpec,
1039
+ buildBrandingConfig, buildModulesConfig, buildHierarchyConfig, buildReadme, kickoffTasks, normalizeSpec,
983
1040
  validateSpec, versionPayload, slugify, writeConfigs, seedViaApi, interview, main,
984
1041
  provisioningSlug, provisioningRequest, productionTopologyKickoff,
985
1042
  // --adopt (brownfield layering, ADR 0121 / task 2042)
@@ -36,7 +36,7 @@ function registerMergeDriver(root) {
36
36
  // mangle %VARS on Windows cmd).
37
37
  try {
38
38
  execFileSync('git', ['config', `merge.${MERGE_DRIVER_NAME}.name`,
39
- 'Off the Boats generated-file regenerator (task 1243)'], { cwd: root, stdio: 'ignore' });
39
+ `${require('./cli-lib').productName()} generated-file regenerator (task 1243)`], { cwd: root, stdio: 'ignore' });
40
40
  execFileSync('git', ['config', `merge.${MERGE_DRIVER_NAME}.driver`,
41
41
  'node scripts/gds/git-merge-regen.js %O %A %B %P'], { cwd: root, stdio: 'ignore' });
42
42
  return { ok: true };
@@ -37,18 +37,27 @@
37
37
  const fs = require('node:fs');
38
38
  const os = require('node:os');
39
39
  const path = require('node:path');
40
+ const ic = require('../../src/instance-config');
40
41
 
42
+ // NOTE the require path: this file lives in scripts/gds/, so branding is two
43
+ // levels up. It read '../src/branding' (= scripts/src/branding, which does not
44
+ // exist) until task 1002428 — so the require ALWAYS threw, safeBrand ALWAYS took
45
+ // the catch, and every key card told the builder to visit the hardcoded founder
46
+ // host regardless of which instance they were signed into.
47
+ //
48
+ // The settings URL now comes from the session-aware base (task 1002427), so it
49
+ // names the instance the builder is actually on; the neutral productName default
50
+ // is kept for the genuinely-unreadable-branding case.
41
51
  function safeBrand() {
52
+ const settingsUrl = `${ic.resolveApiBase()}/builders/settings`;
42
53
  try {
43
- const b = require('../src/branding').branding();
54
+ const b = require('../../src/branding').branding();
44
55
  return {
45
56
  productName: (b.identity && b.identity.productName) || 'Cloud Bongos',
46
- settingsUrl: (b.domains && b.domains.publicOrigin)
47
- ? `${b.domains.publicOrigin}/builders/settings`
48
- : 'https://amazonprimea.com/builders/settings',
57
+ settingsUrl,
49
58
  };
50
59
  } catch (_) {
51
- return { productName: 'Cloud Bongos', settingsUrl: 'https://amazonprimea.com/builders/settings' };
60
+ return { productName: 'Cloud Bongos', settingsUrl };
52
61
  }
53
62
  }
54
63
 
@@ -179,7 +188,7 @@ async function resolveWidgetsEnabled() {
179
188
  const timer = setTimeout(() => ctrl.abort(), 3000);
180
189
  try {
181
190
  const res = await fetch(`${base}/api/gds/me`, {
182
- headers: { Authorization: `Bearer ${session.token}`, 'User-Agent': 'amazonprimea-key-card/1.0' },
191
+ headers: { Authorization: `Bearer ${session.token}`, 'User-Agent': require('../../src/branding').userAgent() },
183
192
  signal: ctrl.signal,
184
193
  });
185
194
  if (!res.ok) return true;
@@ -59,7 +59,7 @@ function refuseIfKeyPasted(argv) {
59
59
  console.log('✖ That looks like an API key — this tool never accepts one as an argument,');
60
60
  console.log(' and keys are never pasted into chat (the transcript is uploaded).');
61
61
  console.log(' Add it in Settings instead — it syncs to your box automatically:');
62
- console.log(' https://amazonprimea.com/builders/settings#art-key');
62
+ console.log(` ${require('./cli-lib').hallBase()}/settings#art-key`);
63
63
  return true;
64
64
  }
65
65
 
@@ -48,11 +48,34 @@
48
48
  const fs = require('node:fs');
49
49
  const path = require('node:path');
50
50
 
51
- const RESOLVER_BASE = 'https://amazonprimea.com/builders#/task/';
52
- const RESOLVER_BASE_IDEA = 'https://amazonprimea.com/builders#/idea/';
53
- const RESOLVER_BASE_BLOCKER = 'https://amazonprimea.com/builders#/blocker/';
54
- // API_BASE comes from cli-lib (the single source). Safe for this token-free CI
55
- // gate: cli-lib does no eager session read or network at module scope.
51
+ // The Builders' Hall deep-link resolver bases (task/idea/blocker), derived from the
52
+ // instance's STABLE public hall — branding.domains.buildersOrigin, falling back to
53
+ // publicOrigin. Deliberately NOT the env-overridable API_BASE: these bases get
54
+ // WRITTEN INTO COMMITTED markdown, so a per-dev CLOUDBONGOS_API_BASE=localhost
55
+ // override must never leak into a committed doc link (task 1002234; the committed-doc
56
+ // twin of the task-1002233 printed-link fix, which purged the last hardcoded
57
+ // `https://amazonprimea.com`). When branding is unreadable the base is the
58
+ // root-relative '/builders#/…' — never a hardcoded founder host. Reading branding is
59
+ // a sync file read (no network), safe at module scope for this token-free CI gate.
60
+ function resolverHallRoot() {
61
+ try {
62
+ const d = (require('../../src/branding').branding().domains) || {};
63
+ const origin = (typeof d.buildersOrigin === 'string' && d.buildersOrigin.trim())
64
+ || (typeof d.publicOrigin === 'string' && d.publicOrigin.trim())
65
+ || '';
66
+ return `${String(origin).replace(/\/+$/, '')}/builders#`;
67
+ } catch (_) {
68
+ return '/builders#';
69
+ }
70
+ }
71
+ const RESOLVER_HALL_ROOT = resolverHallRoot();
72
+ const RESOLVER_BASE = `${RESOLVER_HALL_ROOT}/task/`;
73
+ const RESOLVER_BASE_IDEA = `${RESOLVER_HALL_ROOT}/idea/`;
74
+ const RESOLVER_BASE_BLOCKER = `${RESOLVER_HALL_ROOT}/blocker/`;
75
+ // API_BASE (env → session → branding default) comes from cli-lib (the single
76
+ // source) — used ONLY for the RUNTIME /public/ref-ids fetch below, never for the
77
+ // committed link bases above. Safe for this token-free CI gate: cli-lib does no
78
+ // eager session read or network at module scope.
56
79
  const { API_BASE } = require('./cli-lib');
57
80
 
58
81
  // Every hand-authored markdown file that a reader sees rendered on github.com:
@@ -101,11 +124,14 @@ const GITHUB_NS_RE = /\b(pull requests?|pull|prs?)[`'")\s(:]*$/i;
101
124
  // "reports #2–#11" treat BOTH ends the same way.
102
125
  const LIST_GLUE_RE = /^[\s,–—-]*(?:and|&|\+|or|to|through)?[\s,–—-]*$/i;
103
126
 
104
- const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
105
- // An existing markdown link of the exact TASK form this script writes:
106
- // [#N](task-resolver/N). Used only to undo PAST task-resolver mis-links of
127
+ // An existing markdown link of the TASK-resolver form this script writes:
128
+ // [#N](…/builders#/task/N). Used only to undo PAST task-resolver mis-links of
107
129
  // entity/idea/blocker refs; the idea/blocker resolver links are left alone.
108
- const RESOLVER_LINK_RE = new RegExp(`\\[#(\\d{1,5})\\]\\(${escapeRe(RESOLVER_BASE)}(\\d{1,5})\\)`, 'g');
130
+ // HOST-AGNOSTIC (task 1002234 part b): matches any http(s) host OR a root-relative
131
+ // '/builders#/…', so the mislink-cleanup path still recognizes a link a PRIOR run
132
+ // committed under a DIFFERENT host — the legacy `amazonprimea.com` links, a
133
+ // co-tenant's, or this instance's own — not only the current RESOLVER_BASE host.
134
+ const RESOLVER_LINK_RE = /\[#(\d{1,5})\]\((?:https?:\/\/[^/)\s]+)?\/builders#\/task\/(\d{1,5})\)/g;
109
135
 
110
136
  // Classify a ref by its left context (+ list-glue carry). Returns one of
111
137
  // 'github' | 'idea' | 'blocker' | 'entity' | null (null = candidate task ref).