@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
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ // scripts/gds/preflight.js — the onboarding ENVIRONMENT PREFLIGHT (task 1002401,
3
+ // onboarding-audit area 4).
4
+ //
5
+ // One shared, PURE check of the three things that used to bite a brand-new
6
+ // builder one reactive crash at a time:
7
+ // 1. Node >= 22 — the very first command (`bongos start`) shells to node;
8
+ // on a bare machine it died with `node: command not found`.
9
+ // 2. deps installed — a missing node_modules is why a ship later blew up with
10
+ // `Cannot find module 'pg'`.
11
+ // 3. `claude` launchable — the grader spawns it per worker; without it every ship
12
+ // failed grading with SUBAGENT_SPAWN_FAILED.
13
+ // Each finding carries a copy-paste fix.
14
+ //
15
+ // Before this, those checks lived only inside `bongos doctor` (doctor.js) and were
16
+ // never run at setup/start time — so newcomers hit the failures reactively, deep
17
+ // in the session. This module is the front-loadable, testable source of truth:
18
+ // - `setup.js` runs it at the top of `/builder-setup` (catch a bare machine up front)
19
+ // - `doctor.js` `checkToolchain()` delegates to it (one source, no drift)
20
+ // - `node scripts/gds/preflight.js` runs it standalone (a quick `bongos`-less check)
21
+ //
22
+ // Pure by construction: every OS touch (node version, fs, child_process) is an
23
+ // injectable dep, so the unit test drives it with zero real I/O.
24
+
25
+ 'use strict';
26
+
27
+ const nodeFs = require('node:fs');
28
+ const nodePath = require('node:path');
29
+ const { spawnSync } = require('node:child_process');
30
+
31
+ const REPO_ROOT = nodePath.resolve(__dirname, '..', '..');
32
+ const MIN_NODE_MAJOR = 22;
33
+
34
+ // The node_modules-present check, factored out (task 1002403) so a HOT-PATH
35
+ // caller — /builder-start runs on every session open — can reuse THIS exact
36
+ // finding without paying for the `claude --version` spawn the full
37
+ // preflightChecks() does. Pure: injectable fs + repoRoot.
38
+ function depsFinding({ repoRoot = REPO_ROOT, fs = nodeFs } = {}) {
39
+ let hasDeps = false;
40
+ try { hasDeps = fs.existsSync(nodePath.join(repoRoot, 'node_modules')); } catch (_) { /* treat as missing */ }
41
+ if (hasDeps) {
42
+ return { level: 'ok', title: 'Dependencies', detail: 'node_modules present.' };
43
+ }
44
+ return {
45
+ level: 'warn',
46
+ title: 'Dependencies',
47
+ detail: 'node_modules is missing — scripts that need pg/express (a ship, the server) will fail until installed.',
48
+ fix: 'Run `npm ci` in the repo root.',
49
+ };
50
+ }
51
+
52
+ // preflightChecks(deps) → [{ level:'ok'|'warn'|'fail', title, detail, fix? }]
53
+ // No printing, no process.exit — callers (setup, doctor, the test) format it.
54
+ function preflightChecks({
55
+ repoRoot = REPO_ROOT,
56
+ nodeVersion = process.version,
57
+ env = process.env,
58
+ fs = nodeFs,
59
+ spawn = spawnSync,
60
+ platform = process.platform,
61
+ } = {}) {
62
+ const out = [];
63
+
64
+ // 1. Node >= 22.
65
+ const major = Number(String(nodeVersion).replace(/^v/, '').split('.')[0]) || 0;
66
+ if (major >= MIN_NODE_MAJOR) {
67
+ out.push({ level: 'ok', title: 'Node', detail: `${nodeVersion} (>= ${MIN_NODE_MAJOR}).` });
68
+ } else {
69
+ out.push({
70
+ level: 'fail',
71
+ title: 'Node',
72
+ detail: `${nodeVersion} is below the required v${MIN_NODE_MAJOR}.`,
73
+ fix: 'Install Node >= 22 — e.g. `brew install node`, `nvm install 22`, or https://nodejs.org.',
74
+ });
75
+ }
76
+
77
+ // 2. Dependencies installed (node_modules present) — shared with the hot-path
78
+ // /builder-start caller via depsFinding() (task 1002403).
79
+ out.push(depsFinding({ repoRoot, fs }));
80
+
81
+ // 3. `claude` CLI launchable — same resolution order the grader uses.
82
+ const bin = env.OTB_CLAUDE_BIN || env.CLAUDE_BIN || 'claude';
83
+ const via = env.OTB_CLAUDE_BIN ? ' (via OTB_CLAUDE_BIN)' : env.CLAUDE_BIN ? ' (via CLAUDE_BIN)' : '';
84
+ let r;
85
+ try {
86
+ r = spawn(bin, ['--version'], { encoding: 'utf8', shell: platform === 'win32', windowsHide: true, timeout: 15000 });
87
+ } catch (err) { r = { error: err }; }
88
+ if (r && !r.error && r.status === 0) {
89
+ const ver = String(r.stdout || '').trim().split('\n')[0] || 'present';
90
+ out.push({ level: 'ok', title: 'claude CLI', detail: `launches${via}: ${ver} — the grader can spawn its workers.` });
91
+ } else {
92
+ const why = r && r.error ? (r.error.code || r.error.message) : `exited ${r ? r.status : '?'}`;
93
+ out.push({
94
+ level: 'fail',
95
+ title: 'claude CLI',
96
+ detail: `not launchable (${bin}: ${why}) — the grader spawns it per worker, so ships fail grading with SUBAGENT_SPAWN_FAILED.`,
97
+ fix: 'Run `npm i -g @anthropic-ai/claude-code` (puts a real claude on PATH), or set OTB_CLAUDE_BIN to its full path.',
98
+ });
99
+ }
100
+
101
+ return out;
102
+ }
103
+
104
+ const GLYPH = { ok: '✓', warn: '!', fail: '✖' };
105
+ const ANSI = { ok: '\x1b[32m', warn: '\x1b[33m', fail: '\x1b[31m', dim: '\x1b[2m', reset: '\x1b[0m' };
106
+
107
+ // formatPreflight(results, opts) → a printable multi-line string.
108
+ function formatPreflight(results, { color = false, heading = false } = {}) {
109
+ const c = (k, s) => (color ? `${ANSI[k]}${s}${ANSI.reset}` : s);
110
+ const lines = [];
111
+ if (heading) lines.push('Checking your environment…');
112
+ for (const r of results) {
113
+ lines.push(` ${c(r.level, GLYPH[r.level])} ${r.title}: ${r.detail}`);
114
+ if (r.fix) lines.push(` ${c('dim', '→')} ${r.fix}`);
115
+ }
116
+ return lines.join('\n');
117
+ }
118
+
119
+ module.exports = { preflightChecks, depsFinding, formatPreflight, REPO_ROOT, MIN_NODE_MAJOR };
120
+
121
+ // Standalone: `node scripts/gds/preflight.js` — exit 1 if any hard fail.
122
+ if (require.main === module) {
123
+ const results = preflightChecks();
124
+ console.log(formatPreflight(results, { color: process.stdout.isTTY, heading: true }));
125
+ process.exit(results.some((r) => r.level === 'fail') ? 1 : 0);
126
+ }
@@ -47,6 +47,7 @@ const fs = require('node:fs');
47
47
  const os = require('node:os');
48
48
  const path = require('node:path');
49
49
  const cp = require('node:child_process');
50
+ const crypto = require('node:crypto');
50
51
 
51
52
  const REPO_ROOT = path.resolve(__dirname, '..', '..');
52
53
 
@@ -180,7 +181,23 @@ function loadDeps() {
180
181
  const provisioning = require(path.join(REPO_ROOT, 'modules/provisioning/provisioning'));
181
182
  // eslint-disable-next-line global-require
182
183
  const { DoApi } = require(path.join(REPO_ROOT, 'scripts/gds/do-api'));
183
- _deps = { pool, provisioning, DoApi };
184
+ // Cross-site identity (ADR 0141 §4, task #1002109). Resolve the hub-side module by a
185
+ // VARIABLE key, gated on isModuleEnabled — the module-loader's own dynamic-dispatch
186
+ // pattern (the run-unit-tests.js precedent), NOT a static 'modules/platform-identity/'
187
+ // literal that would couple this publishable core file to an instance-EXCLUDED module
188
+ // (fitness Check 16b / task 2135). A vanilla core that neither ships nor enables the
189
+ // module never resolves it → no MODULE_NOT_FOUND on a fresh boot. Null here → federation
190
+ // dormant (shouldFederate skips) → the instance owns its auth, byte-identical to today.
191
+ let platformIdentity = null, hubKeys = null;
192
+ const PI_KEY = 'platform-identity';
193
+ let piEnabled = false;
194
+ try { piEnabled = require(path.join(REPO_ROOT, 'src/modules')).isModuleEnabled(PI_KEY); } catch { piEnabled = false; }
195
+ if (piEnabled) {
196
+ const piBase = path.join(REPO_ROOT, 'modules', PI_KEY);
197
+ try { platformIdentity = require(path.join(piBase, 'platform-identity')); hubKeys = require(path.join(piBase, 'hub-keys')); }
198
+ catch { platformIdentity = null; hubKeys = null; }
199
+ }
200
+ _deps = { pool, provisioning, DoApi, platformIdentity, hubKeys };
184
201
  return _deps;
185
202
  }
186
203
 
@@ -279,6 +296,14 @@ function standaloneMigrateCmd(inst, { privileged = false } = {}) {
279
296
  ? `sudo -u postgres env PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 bash ${CORE_PKG_DIR}/scripts/migrate.sh`
280
297
  : `PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 bash ${CORE_PKG_DIR}/scripts/migrate.sh`;
281
298
  }
299
+ // Regenerate the instance's whole-file navigation docs (session-log index) from the pinned core,
300
+ // AFTER the pull+install+migrate — the standalone analogue of the monorepo's deploy-prod.sh regen
301
+ // block (ADR 0110 §2, task 1002314). Best-effort + LOUD (the script always exits 0 + prints
302
+ // ::ERROR:: on a generator failure) so a stale/absent nav doc never fails a deploy. Run with
303
+ // cwd = standaloneRoot so resolveInstanceRoot() writes into the instance repo. Without this a
304
+ // standalone instance's docs never refresh on deploy — the gap the owner hit ("docs didn't
305
+ // transfer from primea"): its bespoke pull-deploy regenerated nothing.
306
+ function standaloneRegenDocsCmd() { return `node ${CORE_PKG_DIR}/scripts/gds/regen-instance-docs.js`; }
282
307
 
283
308
  // ── STANDALONE scaffold (ADR 0108 / 0125, task 2055) ──────────────────────────
284
309
  // The MISSING first leg of the greenfield "one guided flow": provision.js used to
@@ -325,7 +350,54 @@ function instanceInitSpec(inst, { ownerLogin } = {}) {
325
350
  // dist/ + secrets OUT of the commit, while the vendored core tarball (vendor/*.tgz)
326
351
  // and package-lock.json stay IN — `npm ci` on deploy resolves the pinned core from
327
352
  // the committed tarball (ADR 0108), so they MUST be tracked.
328
- const INSTANCE_GITIGNORE = ['node_modules/', 'dist/', '.env', '.env.local', '*.log', ''].join('\n');
353
+ //
354
+ // docs/session-log-index.md is a WHOLE-FILE generated navigation doc (ADR 0110): every byte is
355
+ // machine-owned, regenerated at deploy by regen-instance-docs.js (task 1002314) into the served
356
+ // tree. Untracked so `git reset --hard origin/main` never wipes it and no branch can diff it —
357
+ // the same reasoning the core's own .gitignore applies to it. (docs/repo-map.md is listed for
358
+ // parity/forward-compat; it is core-scoped today so a standalone instance doesn't generate its
359
+ // own — see regenerateNavDocs() in upgrade.js — but ignoring it costs nothing and prevents a
360
+ // stray future write from entering the tree.)
361
+ const INSTANCE_GITIGNORE = [
362
+ 'node_modules/', 'dist/', '.env', '.env.local', '*.log',
363
+ '# Whole-file generated navigation docs — regenerated at deploy (ADR 0110, task 1002314).',
364
+ 'docs/session-log-index.md', 'docs/repo-map.md', '',
365
+ ].join('\n');
366
+
367
+ // A PRE-BUILT @bongos/core tarball this control plane can vendor into a NEW instance
368
+ // WITHOUT rebuilding from source — the consumer-control-plane fix (task 1002275).
369
+ //
370
+ // The scaffold's default core-install path rebuilds the tarball with package-core.js,
371
+ // which enumerates GIT-TRACKED files (`git -C REPO_ROOT ls-tree`). That works from a core
372
+ // SOURCE checkout, but NOT when this runner is ITSELF a consumer instance whose core is an
373
+ // installed npm dependency: provision.js then runs out of node_modules/@bongos/core, so
374
+ // REPO_ROOT points INSIDE gitignored node_modules, git sees ZERO tracked files there, and
375
+ // package-core aborts ("the core set is EMPTY") — the scaffold never pushes and the freshly
376
+ // created repo is left EMPTY (seen live: trial/test on cloudbongos.com).
377
+ //
378
+ // But the exact pinned tarball this instance installed FROM is on disk, named by its own
379
+ // package.json ("@bongos/core": "file:vendor/bongos-core-<v>.tgz"). Reuse it: byte-identical,
380
+ // already proven, and the child pins the SAME core the hub runs. Returns an absolute .tgz
381
+ // path, or null on a source checkout (no `file:` core pin) — where package-core.js is the
382
+ // right, unchanged path. `instanceRoot` defaults to $BONGOS_INSTANCE_ROOT (what
383
+ // infra/core-node.sh resolves against) else process.cwd() (the runner's WorkingDirectory).
384
+ // fs injectable for tests. PURE-ish (fs reads only, no mutation → dry-run safe).
385
+ function installedCoreTarball({ instanceRoot, fsImpl = fs } = {}) {
386
+ const root = instanceRoot || process.env.BONGOS_INSTANCE_ROOT || process.cwd();
387
+ let pkg;
388
+ try { pkg = JSON.parse(fsImpl.readFileSync(path.join(root, 'package.json'), 'utf8')); }
389
+ catch { return null; } // no/unreadable package.json → treat as a source checkout
390
+ const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
391
+ // Both scope aliases the loader accepts (infra/core-node.sh checks the same two).
392
+ for (const name of ['@bongos/core', '@cloudbongos/core']) {
393
+ const m = /^file:(.+\.tgz)$/.exec(String(deps[name] || '').trim());
394
+ if (m) {
395
+ const abs = path.resolve(root, m[1]);
396
+ if (fsImpl.existsSync(abs)) return abs; // a dangling pin (tarball pruned) → fall through to null
397
+ }
398
+ }
399
+ return null;
400
+ }
329
401
 
330
402
  // Clone the (empty) target_ref → standaloneRoot → materialize config/ + .claude/ + a
331
403
  // file:-pinned @bongos/core + lockfile (via `bongos init --vendor-core`, task 2053)
@@ -366,11 +438,19 @@ async function scaffoldStandaloneRepo(inst, deps) {
366
438
  } catch { /* fall back to the repo owner */ }
367
439
  }
368
440
 
369
- // 1. clone the (possibly empty) repo, then force the initial branch to `main` (an empty
370
- // clone leaves an unborn HEAD). A re-run after a partial clone reuses the existing dir.
441
+ // 1. Materialize the working tree + set the initial branch to `main`. A freshly-created
442
+ // instance repo is EMPTY (github-repos.createUserRepo, auto_init:false), so there is
443
+ // NOTHING to clone — and a git DEPLOY KEY is per-repo, so the shared PROVISION_GIT_SSH_ALIAS
444
+ // key (e.g. `github-instances`, a deploy key scoped to ONE repo) can't even READ a
445
+ // consumer's brand-new repo under their own account: `git clone` 500s with "Repository not
446
+ // found." This is the clone-side twin of the task-1002317 push bug. So `git init` locally
447
+ // instead of cloning, and set `origin` to the SSH remote for the no-token fallback push
448
+ // below; the OAuth-preferred push (its token CREATED the repo, so it can write) handles the
449
+ // upload for consumer repos. A re-run after a partial init reuses the existing dir.
371
450
  if (!fs.existsSync(path.join(root, '.git'))) {
372
- exec(`mkdir -p ${path.posix.dirname(root)}`);
373
- exec(`git clone ${remote} ${root}`);
451
+ exec(`mkdir -p ${root}`);
452
+ exec(`git -C ${root} init -q`);
453
+ exec(`git -C ${root} remote add origin ${remote}`);
374
454
  }
375
455
  exec(`git -C ${root} checkout -B main`);
376
456
 
@@ -400,9 +480,19 @@ async function scaffoldStandaloneRepo(inst, deps) {
400
480
  log(` [scaffold] core install: registry — PRIVATE @bongos/core@${coreV} via env-fed NPM_TOKEN (ADR 0134)`);
401
481
  exec(`node scripts/gds/init.js --from ${specPath} --dir ${root} --no-seed --force --registry-core`);
402
482
  } else {
403
- log(` [scaffold] core install: vendored tarball (offline default)`);
404
- exec(`node scripts/gds/package-core.js`); // cwd = REPO_ROOT → dist/bongos-core-<v>.tgz
405
- const tgz = path.join(REPO_ROOT, 'dist', `bongos-core-${coreV}.tgz`);
483
+ // Vendored tarball. PREFER a pre-built pinned tarball this control plane already has
484
+ // (a consumer instance's own `file:` core pin) — rebuilding via package-core.js aborts
485
+ // with an EMPTY core set when this runner IS a consumer instance (REPO_ROOT is inside
486
+ // gitignored node_modules), which left new repos empty (task 1002275). Only rebuild
487
+ // from source when there is no such pin (a genuine core source checkout).
488
+ let tgz = installedCoreTarball({ instanceRoot: deps.instanceRoot });
489
+ if (tgz) {
490
+ log(` [scaffold] core install: reusing the pinned core tarball this control plane runs (${path.basename(tgz)}) — no source rebuild (task 1002275)`);
491
+ } else {
492
+ log(` [scaffold] core install: building a fresh vendored tarball from source (package-core.js)`);
493
+ exec(`node scripts/gds/package-core.js`); // cwd = REPO_ROOT → dist/bongos-core-<v>.tgz
494
+ tgz = path.join(REPO_ROOT, 'dist', `bongos-core-${coreV}.tgz`);
495
+ }
406
496
  exec(`node scripts/gds/init.js --from ${specPath} --dir ${root} --no-seed --force --vendor-core ${tgz}`);
407
497
  }
408
498
 
@@ -412,8 +502,38 @@ async function scaffoldStandaloneRepo(inst, deps) {
412
502
  exec(`git -C ${root} config user.email "bot@cloudbongos.com"`);
413
503
  exec(`git -C ${root} add -A`);
414
504
  exec(`git -C ${root} commit -m "chore: scaffold ${inst.slug} on a pinned @bongos/core ${coreV} (provision.js, ADR 0108)"`, { allowFail: true });
415
- exec(`git -C ${root} push -u origin main`);
416
- log(` [scaffold] pushed initial scaffold to ${repo.owner}/${repo.name}@main`);
505
+ // Push. A GitHub DEPLOY KEY is per-repo, so the box's shared key can't write to a NEW
506
+ // project repo — the second empty-repo cause (task 1002317). PREFER the instance owner's
507
+ // OAuth token (it CREATED the repo via github-repos.createUserRepo, so `public_repo`
508
+ // already grants write). The token rides a chmod-600 git credential-store file — never
509
+ // the logged command or makeExec's env (both are surfaced) — and is removed right after
510
+ // (finally, so a failed push can't leave it on disk). No token (org-owned repo, or
511
+ // secret-box off) → fall back to the SSH `origin` push (unchanged; e.g. an instance whose
512
+ // matching deploy key was cloned into origin). `deps.getGithubToken` is injectable for
513
+ // tests; production resolves it lazily against the core db slice with the runner's pool.
514
+ let ownerToken = null;
515
+ if (inst.owner_builder_id) {
516
+ try {
517
+ const fetchTok = deps.getGithubToken
518
+ || ((bid) => require(path.join(REPO_ROOT, 'src/bongos/db')).getGithubToken({ builderId: bid, p: deps.db }));
519
+ const r = await fetchTok(inst.owner_builder_id);
520
+ ownerToken = r && r.token ? r.token : null;
521
+ } catch (e) { log(` [scaffold] owner OAuth token lookup failed (${e && e.message}) — using the SSH remote`); }
522
+ }
523
+ if (ownerToken) {
524
+ const credFile = path.join(os.tmpdir(), `bongos-gitcred-${inst.slug}-${process.pid}`);
525
+ writeFile(credFile, `https://x-access-token:${ownerToken}@github.com\n`, { mode: 0o600 });
526
+ const httpsUrl = `https://x-access-token@github.com/${repo.owner}/${repo.name}.git`;
527
+ try {
528
+ exec(`git -C ${root} -c credential.helper='store --file=${credFile}' push ${httpsUrl} HEAD:main`);
529
+ log(` [scaffold] pushed to ${repo.owner}/${repo.name}@main via the owner's GitHub token (task 1002317)`);
530
+ } finally {
531
+ exec(`rm -f ${credFile}`, { allowFail: true });
532
+ }
533
+ } else {
534
+ exec(`git -C ${root} push -u origin main`);
535
+ log(` [scaffold] pushed initial scaffold to ${repo.owner}/${repo.name}@main (SSH origin)`);
536
+ }
417
537
  return { ok: true, scaffolded: true };
418
538
  }
419
539
 
@@ -562,7 +682,7 @@ function provisionedEnvPrefix() {
562
682
  // the hall on this host + OAuth uses the real callback origin — without them the pack
563
683
  // stays at neutral localhost and the hall never loads (task 1972). No domain yet
564
684
  // (bring-your-own deferred, ADR 0111 §4) ⇒ origins stay neutral until one is set.
565
- function webEnvBody(inst, { clientId = '', clientSecret = '' } = {}) {
685
+ function webEnvBody(inst, { clientId = '', clientSecret = '', federation = null } = {}) {
566
686
  const prefix = provisionedEnvPrefix();
567
687
  const origin = inst.domain ? `https://${inst.domain}` : '';
568
688
  const originLines = origin
@@ -583,7 +703,11 @@ ${prefix}_OAUTH_ORIGIN=${origin}
583
703
  // Manifest flow (placeManifestCreds fetches them control-plane-side + re-writes this
584
704
  // file with clientId/clientSecret set), or the manual oauth-secret.js placement. On
585
705
  // the first provision write they are blank placeholders the flow fills later.
586
- const credComment = clientId || clientSecret
706
+ const credComment = federation
707
+ ? `# Provisioned by scripts/gds/provision.js (ADR 0111). This instance FEDERATES sign-in
708
+ # through the Cloud Bongos hub (ADR 0141 §4) — it needs no GitHub app of its own, so the
709
+ # GitHub client id/secret below stay blank; the ${prefix}_HUB_* block drives sign-in.`
710
+ : clientId || clientSecret
587
711
  ? `# Provisioned by scripts/gds/provision.js (ADR 0111). OAuth app creds placed via the
588
712
  # GitHub App Manifest flow (ADR 0133) — the owner clicked one link; no secret was pasted.`
589
713
  : `# Provisioned by scripts/gds/provision.js (ADR 0111). Fill the OAuth app creds via the
@@ -602,6 +726,20 @@ ${prefix}_OAUTH_ORIGIN=${origin}
602
726
  const firstAdminLine = ownerRepo && ownerRepo.owner
603
727
  ? `${prefix}_FIRST_ADMIN=${ownerRepo.owner}\n`
604
728
  : '';
729
+ // Cross-site identity federation block (ADR 0141 §4, task #1002109). Present only for
730
+ // a HOSTED (federated) instance: flips auth.idp ON via the env override branding.js
731
+ // reads (idpEnabled), and carries the hub origin, this instance's per-client secret
732
+ // (authenticates it TO the hub, server-to-server), and the hub's PUBLIC key (verifies
733
+ // the CLI's hub-signed assertion offline). All written straight to this chmod-600
734
+ // file — never through a browser or chat (the ADR 0133 secret-placement posture).
735
+ const fedLines = federation
736
+ ? `${prefix}_AUTH_IDP=true
737
+ ${prefix}_HUB_ORIGIN=${federation.hubOrigin}
738
+ ${prefix}_HUB_CLIENT_ID=${federation.clientId}
739
+ ${prefix}_HUB_CLIENT_SECRET=${federation.clientSecret}
740
+ ${prefix}_HUB_PUBLIC_KEY=${federation.hubPublicKey}
741
+ `
742
+ : '';
605
743
  return `# /etc/${inst.slug}/web.env — per-instance secrets (chmod 600, OUTSIDE the repo).
606
744
  ${credComment}
607
745
  ${prefix}_GITHUB_CLIENT_ID=${clientId || ''}
@@ -609,12 +747,88 @@ ${prefix}_GITHUB_CLIENT_SECRET=${clientSecret || ''}
609
747
  ${firstAdminLine}GDS_BRANDING_FILE=${PROVISIONED_BRANDING_FILE}
610
748
  ${originLines}PORT=${inst.port}
611
749
  PGDATABASE=${dbName(inst)}
612
- `;
750
+ ${fedLines}`;
613
751
  }
614
752
  function webEnvPath(inst, { etcBase = CONFIG.etcBase } = {}) {
615
753
  return path.posix.join(etcBase, inst.slug, 'web.env');
616
754
  }
617
755
 
756
+ // ── Cross-site identity: federate a hosted instance's sign-in through the hub ───
757
+ // (ADR 0141 §4, task #1002109). The control plane IS the hub (cloudbongos.com), so
758
+ // registration is a direct in-process call against the hub pool — no HTTP hop.
759
+
760
+ // The hub's own public origin, written into each federated instance as
761
+ // <PREFIX>_HUB_ORIGIN (what auth.js loadIdpConfig reads). Explicit PROVISION_HUB_ORIGIN
762
+ // wins; otherwise the control plane's OWN public origin (<PREFIX>_PUBLIC_ORIGIN, the
763
+ // env the hub already runs under). Null → not hub-configured → shouldFederate is false.
764
+ function federationHubOrigin() {
765
+ const explicit = process.env.PROVISION_HUB_ORIGIN;
766
+ if (explicit) return String(explicit).replace(/\/+$/, '');
767
+ const own = process.env[`${provisionedEnvPrefix()}_PUBLIC_ORIGIN`];
768
+ return own ? String(own).replace(/\/+$/, '') : null;
769
+ }
770
+
771
+ // Whether provisioning should federate this instance's sign-in through the hub.
772
+ // Dormant-by-default: true ONLY when the control plane is hub-configured (the
773
+ // platform-identity module is present, a signing key is loadable, AND a hub origin
774
+ // resolves), the instance is a CO-TENANT (the mothership-hosted shape whose web.env is
775
+ // written locally — a `standalone` is owner-hosted and keeps its own GitHub app, the
776
+ // self-hosted path; a `dedicated` droplet has no local web.env write yet, same gap the
777
+ // ADR 0133 manifest flow leaves — see placeManifestCreds), and it has a public domain
778
+ // (the origin the hub binds the CLI mint audience to — the #1002108 account-takeover
779
+ // fix). Returns { federate, reason } so the caller can log WHY it skipped. PURE-ish
780
+ // (reads env + the injected module handles; no DB, no I/O).
781
+ function shouldFederate(inst, deps) {
782
+ const { platformIdentity, hubKeys } = deps;
783
+ if (!platformIdentity || !hubKeys) return { federate: false, reason: 'platform-identity module not present (control plane is not the hub)' };
784
+ if (!hubKeys.hubSigningConfigured()) return { federate: false, reason: 'hub signing key not configured (<PREFIX>_HUB_SIGNING_KEY unset)' };
785
+ if (!federationHubOrigin()) return { federate: false, reason: 'hub origin not resolvable (set PROVISION_HUB_ORIGIN or <PREFIX>_PUBLIC_ORIGIN)' };
786
+ if (inst.hosting_shape !== 'co-tenant') return { federate: false, reason: `${inst.hosting_shape} keeps its own GitHub app (not the co-tenant hosted path)` };
787
+ if (!inst.domain) return { federate: false, reason: 'no domain yet — federation needs the instance public origin' };
788
+ return { federate: true, reason: null };
789
+ }
790
+
791
+ // Register this hosted instance as an sso_clients row on the HUB (fresh per-client
792
+ // secret) and return the federation block to bake into its web.env (ADR 0141 §4).
793
+ // SECURITY: `origin` MUST be the instance's EXACT public origin — the hub binds the
794
+ // CLI-federation mint audience to it (the #1002108 fix), so a mismatch would break
795
+ // device sign-in (and a wrong origin would be a takeover vector). client_id is the
796
+ // (unique, DNS-safe) slug, so a re-provision rotates the SAME row's secret via
797
+ // registerClient's ON CONFLICT rather than orphaning a client. redirect_uri lists both
798
+ // callback prefixes the core may emit (/api/gds is what auth.js builds today) so the
799
+ // hub's exact-match allow-list accepts the browser leg. --apply gates the real hub
800
+ // write; dry-run returns a placeholder block (so webEnvBody can still be composed) + logs.
801
+ async function federateInstance(inst, deps) {
802
+ const { db, platformIdentity, hubKeys, log, apply } = deps;
803
+ const origin = `https://${inst.domain}`;
804
+ const clientId = inst.slug;
805
+ const hubOrigin = federationHubOrigin();
806
+ const hubPublicKey = hubKeys.hubPublicKeyB64();
807
+ if (!hubPublicKey) throw new Error('hub signing key is set but unreadable — cannot derive HUB_PUBLIC_KEY');
808
+ const redirectUris = [
809
+ `${origin}/api/gds/auth/web/callback`,
810
+ `${origin}/api/bongos/auth/web/callback`,
811
+ ];
812
+ if (!apply) {
813
+ log(` [federate] would register client '${clientId}' on the hub (origin ${origin}) + write ${provisionedEnvPrefix()}_HUB_* + AUTH_IDP into web.env`);
814
+ return { hubOrigin, clientId, clientSecret: '0'.repeat(43), hubPublicKey };
815
+ }
816
+ const clientSecret = crypto.randomBytes(32).toString('base64url');
817
+ // Best-effort owner github_id — nice for the hub catalog's ownership link, but
818
+ // nullable in registerClient, so a lookup miss must never fail the provision.
819
+ let ownerGithubId = null;
820
+ try {
821
+ const { rows } = await db.query('SELECT github_id FROM builders WHERE id = $1', [inst.owner_builder_id]);
822
+ ownerGithubId = rows[0] ? rows[0].github_id : null;
823
+ } catch { /* non-fatal */ }
824
+ await platformIdentity.registerClient(
825
+ { clientId, secret: clientSecret, name: inst.slug, redirectUris, origin, ownerGithubId },
826
+ { pool: db },
827
+ );
828
+ log(` [federate] registered client '${clientId}' on the hub (origin ${origin})`);
829
+ return { hubOrigin, clientId, clientSecret, hubPublicKey };
830
+ }
831
+
618
832
  // The Cloudflare zone a hostname's A record lives in. Cloudflare zones are
619
833
  // registrable domains, which for the common case is the last two DNS labels
620
834
  // (mercury.example.com → example.com; apex example.com → example.com). Multi-part
@@ -925,6 +1139,28 @@ async function provisionInstance(inst, deps) {
925
1139
  if (apply) await provisioning.setInstanceStatus(db, inst.id, 'provisioning');
926
1140
  const patch = {};
927
1141
 
1142
+ // ── cross-site identity: register hub federation (ADR 0141 §4, task #1002109) ──
1143
+ // Computed BEFORE the shape steps so the co-tenant web.env write (step 3) bakes it
1144
+ // in — the instance comes up already federated, no post-hoc restart. Dormant unless
1145
+ // the control plane is hub-configured AND the shape/domain qualify (shouldFederate);
1146
+ // otherwise the instance owns its auth (direct GitHub), byte-identically to today.
1147
+ let federation = null;
1148
+ {
1149
+ const fed = shouldFederate(inst, deps);
1150
+ if (fed.federate) {
1151
+ federation = await federateInstance(inst, deps);
1152
+ log(` [federate] ${inst.slug} → hub sign-in (auth.idp on) — no GitHub app of its own`);
1153
+ if (apply) {
1154
+ await provisioning.recordEvent(db, {
1155
+ instanceId: inst.id, ownerBuilderId: inst.owner_builder_id, event: 'federated',
1156
+ detail: `sign-in federated to the hub (client ${inst.slug}, origin https://${inst.domain})`, actor: 'runner',
1157
+ }).catch(() => {});
1158
+ }
1159
+ } else {
1160
+ log(` [federate] skipped (${fed.reason}) — instance owns its auth (direct GitHub)`);
1161
+ }
1162
+ }
1163
+
928
1164
  // ── step 1: server ───────────────────────────────────────────────────────
929
1165
  // The A-record target for step 5's DNS UPSERT:
930
1166
  // • co-tenant → PROVISION_PUBLIC_IP (the box it runs on — under remote-exec that
@@ -1018,11 +1254,19 @@ async function provisionInstance(inst, deps) {
1018
1254
  // here should surface. Dedicated bakes the same step into cloud-init (dedicatedUserData).
1019
1255
  log(' [seed] first version (only if versions is empty)');
1020
1256
  boxExec(seedFirstVersionCmd(inst, { privileged }));
1257
+ // Regenerate the whole-file navigation docs from the pinned core so a fresh/redeployed
1258
+ // standalone instance's docs/ stays live (ADR 0110 §2, task 1002314) — the deploy-regen the
1259
+ // bespoke pull scripts were missing. Best-effort: regen-instance-docs.js exits 0 even on a
1260
+ // generator hiccup (LOUD ::ERROR::), so a stale nav doc never fails a provision.
1261
+ if (standalone) {
1262
+ log(' [docs] regenerate whole-file navigation docs (session-log index)');
1263
+ boxExec(standaloneRegenDocsCmd(), { cwd: standaloneRoot(inst), allowFail: true });
1264
+ }
1021
1265
  } else { log(' (db create + migrate + first-version seed run via cloud-init on the droplet)'); }
1022
1266
 
1023
1267
  // ── step 3: config + per-instance secrets ─────────────────────────────────
1024
1268
  log(' [config] per-instance web.env');
1025
- if (local) boxWriteFile(webEnvPath(inst), webEnvBody(inst), { mode: 0o600, sudo: true });
1269
+ if (local) boxWriteFile(webEnvPath(inst), webEnvBody(inst, { federation }), { mode: 0o600, sudo: true });
1026
1270
  else log(' (baked into cloud-init)');
1027
1271
 
1028
1272
  // ── step 4: systemd unit + enable ─────────────────────────────────────────
@@ -1476,8 +1720,8 @@ async function main() {
1476
1720
  const slug = args[1] && !args[1].startsWith('--') ? args[1] : null;
1477
1721
  if ((cmd === 'provision' || cmd === 'teardown') && !slug) { console.error(`${cmd} needs a <slug>`); process.exit(2); }
1478
1722
 
1479
- let pool, provisioning, DoApi;
1480
- try { ({ pool, provisioning, DoApi } = loadDeps()); }
1723
+ let pool, provisioning, DoApi, platformIdentity, hubKeys;
1724
+ try { ({ pool, provisioning, DoApi, platformIdentity, hubKeys } = loadDeps()); }
1481
1725
  catch (e) { console.error(`cannot load DB layer (run this on the control plane): ${e.message}`); process.exit(1); }
1482
1726
 
1483
1727
  // Box-target executor (remote over SSH when PROVISION_REMOTE_HOST is set, else
@@ -1492,6 +1736,9 @@ async function main() {
1492
1736
  writeFile: makeWriteFile({ apply, privileged: CONFIG.privileged, exec, remoteHost: CONFIG.remoteHost }),
1493
1737
  controlWriteFile: makeWriteFile({ apply, privileged: CONFIG.privileged, exec: controlExec }),
1494
1738
  doApi: DoApi ? new DoApi({ dryRun: !apply, token: CONFIG.doToken, log: (m) => console.log(' ' + m) }) : null,
1739
+ // Cross-site identity (ADR 0141 §4). Null on a non-hub control plane → federation
1740
+ // dormant; shouldFederate() then skips and the instance owns its auth (as today).
1741
+ platformIdentity, hubKeys,
1495
1742
  };
1496
1743
 
1497
1744
  let result;
@@ -1518,8 +1765,8 @@ module.exports = {
1518
1765
  dbName, dbCreateCmd, migrateCmd, seedFirstVersionCmd, serviceUnit, serviceUnitPath, webEnvBody, webEnvPath,
1519
1766
  backupUnitName, backupServicePath, backupTimerPath, backupScriptPath, backupService, backupTimer,
1520
1767
  standaloneRoot, standalonePullCmd, standaloneInstallCmd, standaloneMigrateCmd,
1521
- // standalone scaffold (ADR 0108 / 0125, task 2055)
1522
- parseTargetRef, instanceRepoRemote, instanceInitSpec, scaffoldStandaloneRepo, coreVersionSafe,
1768
+ // standalone scaffold (ADR 0108 / 0125, task 2055; consumer-core fix task 1002275)
1769
+ parseTargetRef, instanceRepoRemote, instanceInitSpec, installedCoreTarball, scaffoldStandaloneRepo, coreVersionSafe,
1523
1770
  zoneForDomain, dnsUpsertEnv, classifyZoneScope, checkDnsTokenScope,
1524
1771
  caddyBlock, caddySnippetPath, healthzCmd, dedicatedUserData, nextFreePort,
1525
1772
  // executors + orchestration (injectable deps for tests)
@@ -1527,4 +1774,6 @@ module.exports = {
1527
1774
  cmdRunIntents, cmdReconcile, cmdStatus,
1528
1775
  // GitHub App Manifest flow (task 2080, ADR 0133)
1529
1776
  convertManifestCode, placeManifestCreds, drainOAuthManifests, MANIFEST_UA,
1777
+ // cross-site identity federation (ADR 0141 §4, task #1002109)
1778
+ federationHubOrigin, shouldFederate, federateInstance,
1530
1779
  };
@@ -61,17 +61,20 @@ const PUBLISH_ALLOWLIST = [
61
61
  'modules/sessions/',
62
62
  'modules/autonomy/',
63
63
  'modules/security/',
64
+ 'modules/governance/', // configurable roles & permissions (BV1 goal 1000030, ADR 0151) — the rank/permission machinery publishes AS the open-source methodology (ADR 0065 §4 / ADR 0098); it is in PROTECTED_GLOBS for write-protection, NOT publish-exclusion. default:true.
64
65
  'modules/hall-ui/',
65
66
  'modules/status-ui/',
66
67
  'modules/dev-box/',
67
68
  'modules/discord/',
68
69
  'modules/provisioning/', // instance-hosting control plane — CORE like its sibling dev-box (same do-api/cf-tunnel toolchain, all core). Reclassified IN so the core CLI scripts/gds/provision.js resolves it from the core package post-split, not a MODULE_NOT_FOUND (task 2135). default:false — shipped ≠ enabled.
70
+ 'modules/platform-identity/', // hub identity provider (ADR 0141) — ships in core, enabled ONLY on the hub (cloudbongos.com). MUST be here or the hub's enabledModules() throws "unknown module" and provision.js's federation load MODULE_NOT_FOUNDs (task 1002235, same rationale as provisioning above). default:false — shipped ≠ enabled; a vanilla/self-hosted instance never turns it on.
69
71
  // --- neutral (customer-0) config starters + their schemas (the filled-in *.json twins are denied) ---
70
72
  'config/branding.neutral.json',
71
73
  'config/modules.neutral.json',
72
74
  'config/design-tokens.neutral.json',
73
75
  'config/design-tokens.schema.json', // the design-token contract (generic schema, no instance values)
74
76
  'config/hierarchy.neutral.json',
77
+ 'config/governance.neutral.json', // the starting role templates a fresh instance inherits (BV1.R96)
75
78
  // --- the portable methodology core, governance, and sanitized docs ---
76
79
  'CLAUDE.md',
77
80
  'README.md',
@@ -86,7 +89,9 @@ const PUBLISH_ALLOWLIST = [
86
89
  'docs/architecture.md',
87
90
  // Served to the hall / advertised by /api/bongos — a standalone that omits these
88
91
  // renders broken onboarding + API-reference pages (task 2078). Redacted like the
89
- // rest of this docs section; the diagrams' .png/.svg regenerate on the box.
92
+ // rest of this docs section. Only the diagram .mmd SOURCES ship: the .png/.svg are
93
+ // gitignored (ADR 0122) so the tarball cannot carry them, and `bongos upgrade`
94
+ // regenerates them on the box via healDocAssets() (upgrade.js, task 1002441).
90
95
  'docs/onboarding/', // primer.md + drachmae-and-karma.md + diagram sources (/builders/primer · /drachmae · /diagrams)
91
96
  // API artifacts (classification settled, task 2141): CORE-shipped STARTERS so a fresh
92
97
  // hall/docs render before first regen — but per-instance GENERATED. init + `bongos
@@ -206,6 +211,7 @@ const INSTANCE_EXCLUDE = [
206
211
  'config/modules.json',
207
212
  'config/devbox-app.json',
208
213
  'config/hierarchy.json',
214
+ 'config/governance.json',
209
215
  'CODEOWNERS', // names the owner (a protected surface, excluded as identity)
210
216
  // instance SEED migrations — this host's scope-truth SQL (planning seeds, world data).
211
217
  // ADR 0105: a consumer's core must not carry them; migrate.sh reads instance seeds from
@@ -250,6 +256,21 @@ const INSTANCE_EXCLUDE = [
250
256
  'docs/design/container-exterior-tiles.md',
251
257
  'docs/design/container-interior.md',
252
258
  'docs/design/world-map-scroll.md',
259
+ // instance-HISTORY ADRs (task 1002219 / audit H5). These record THIS platform-family's own
260
+ // migration + identity history (the amazonprimea→cloudbongos lift, the standalone reprovision,
261
+ // the cloudbongos identity hub) — they are inherently full of the prior instance's name/owner and
262
+ // genericize to nothing (unlike a portable methodology ADR). docs/adr/ is allowlisted wholesale for
263
+ // the portable decision records; these three stay in this repo and never ship in the core, mirroring
264
+ // the instance-recipe / game-design carve-outs above. (The portable ADRs + the README index still publish.)
265
+ 'docs/adr/0139-platform-history-migration-to-cloudbongos.md',
266
+ 'docs/adr/0140-cloudbongos-standalone-reprovision.md',
267
+ 'docs/adr/0141-cross-site-identity-federated-hub-idp.md',
268
+ // hall-preview FIXTURES (task 1002219 / audit H5) — the local hall-UI preview harness's sample
269
+ // data is a snapshot of THIS instance's builders/goals/tasks (owner login, world name, prior
270
+ // instance name), pure instance identity. The harness code stays portable; only the OTB data is
271
+ // carved out, so a consumer core never ships customer-zero's directory. (Fixtures remain in-repo
272
+ // for local dev — this only affects what package-core publishes.)
273
+ 'scripts/hall-preview/fixtures/',
253
274
  'blockers/',
254
275
  'limitations/',
255
276
  'generative-ideas.md',
@@ -39,6 +39,9 @@
39
39
  const { spawn } = require('node:child_process');
40
40
  const lib = require('./cli-lib');
41
41
 
42
+ // lib.API_BASE is session-aware (task 1002427), so both URLs name the instance the
43
+ // builder is actually signed into — not this checkout's branding default, which is
44
+ // what sent a cloudbongos.com builder to a localhost:3000 click-path.
42
45
  const HALL_URL = `${lib.API_BASE}/builders`;
43
46
  // The "Re-issue CLI token" button lives on the Settings page (under "CLI
44
47
  // Access"), not the hall profile — open the builder right where the button is.
@@ -56,9 +59,10 @@ function openInBrowser(url) {
56
59
  case 'win32':
57
60
  // rundll32's FileProtocolHandler opens a URL without going through a
58
61
  // shell, so (unlike `cmd /c start`) shell metacharacters in the URL are
59
- // never interpreted. Defense-in-depth: today the URL is a constant
60
- // (GDS_API_BASE + '/builders'), but this keeps it safe if that ever
61
- // becomes influenced by less-trusted input.
62
+ // never interpreted. This is now load-bearing, not hypothetical: since
63
+ // task 1002427 the URL derives from the session file's api_base (via the
64
+ // session-aware lib.API_BASE), not from a bare env/branding constant. That
65
+ // file is the builder's own 0600 config, but it IS file-derived input.
62
66
  cmd = 'rundll32';
63
67
  args = ['url.dll,FileProtocolHandler', url];
64
68
  break;