@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,425 @@
1
+ #!/usr/bin/env node
2
+ // /builder-sequence — walk a dependency chain end to end from one kickoff.
3
+ //
4
+ // Two-thirds of the open backlog is dependency-gated. Shipping a task
5
+ // auto-promotes its dependents backlog→ready (the promote_dependents_on_ship
6
+ // trigger, migrations 020/163) — but nothing picks the newly-unblocked task
7
+ // up, so an N-task chain needs N separate kickoffs. This is the plumbing that
8
+ // lets one session walk the whole chain: resolve scope → next → claim → build
9
+ // → ship → next.
10
+ //
11
+ // Calling forms:
12
+ // node scripts/gds/sequence.js --from 1002277 plan the chain below a task
13
+ // node scripts/gds/sequence.js --goal 1000045 plan a goal's open tasks
14
+ // node scripts/gds/sequence.js --from 1002277 --next the one task to claim now
15
+ // node scripts/gds/sequence.js --check 1002277 stop-reasons for one task
16
+ //
17
+ // Flags:
18
+ // --next emit only the next claimable task (or a named stop reason)
19
+ // --check <id> evaluate the halt matrix for one task
20
+ // --json machine-readable output
21
+ // --limit N cap the planned chain at N tasks (default 50)
22
+ //
23
+ // This script DECIDES NOTHING it cannot decide from facts. Every halt is a
24
+ // named reason a human can act on — see HALT_REASONS. The judgement half of
25
+ // the loop (actually building each task) lives in .claude/skills/builder-sequence.
26
+
27
+ const { cliClient, requireSession, fmtMinutes, hallBase } = require('./cli-lib');
28
+ const { matchProtected } = require('../../src/bongos/permission-path-check');
29
+
30
+ // Rank → tier, mirroring REQUIRES_RANK_TIER. Imported shape would drag the
31
+ // server's db.js (and its pool) into a CLI that only needs four integers.
32
+ const RANK_TIER = { xenos: 1, thetes: 2, metic: 3, archon: 4 };
33
+
34
+ // A task in one of these statuses is done as far as the chain is concerned —
35
+ // 'completed'/'confirmed' are mid-ship states, so the chain must not re-claim
36
+ // them, but they are also not yet 'shipped' so successors stay gated.
37
+ const TERMINAL = new Set(['shipped', 'abandoned']);
38
+ const IN_FLIGHT = new Set(['active', 'completed', 'confirmed']);
39
+
40
+ const DEFAULT_LIMIT = 50;
41
+
42
+ // A description shorter than this can't carry acceptance criteria worth
43
+ // verifying against, so the runner refuses to guess at what "done" means.
44
+ const MIN_SPEC_CHARS = 120;
45
+
46
+ // Kinds whose whole point is a judgement call the owner should see before it
47
+ // lands: a decision needs an ADR, a spike's output is a recommendation.
48
+ const JUDGEMENT_KINDS = new Set(['decision', 'spike']);
49
+
50
+ const HALT_REASONS = {
51
+ needs_migration: 'wants a schema migration — needs a reserved number and human eyes',
52
+ security_sensitive: 'flagged security-sensitive',
53
+ protected_path: 'touches the permission/deploy core',
54
+ rank_floor: 'requires a rank above yours',
55
+ thin_spec: 'has no acceptance criteria to verify against',
56
+ judgement_kind: 'is a judgement call (decision/spike), not mechanical work',
57
+ not_claimable: 'is ready but the server will not hand it over',
58
+ deps_unmet: 'is still waiting on a predecessor',
59
+ in_flight: 'is already mid-ship',
60
+ cycle: 'sits in a dependency cycle',
61
+ chain_complete: 'nothing left in this chain',
62
+ };
63
+
64
+ // -------------------------------------------------------------------------
65
+ // Argv
66
+ // -------------------------------------------------------------------------
67
+
68
+ function readArgv(argv) {
69
+ const out = { from: null, goal: null, check: null, next: false, json: false, limit: DEFAULT_LIMIT };
70
+ for (let i = 0; i < argv.length; i++) {
71
+ const a = argv[i];
72
+ if (a === '--next') { out.next = true; continue; }
73
+ if (a === '--json') { out.json = true; continue; }
74
+ if (a === '--from') { out.from = Number(argv[++i]); continue; }
75
+ if (a.startsWith('--from=')) { out.from = Number(a.slice(7)); continue; }
76
+ if (a === '--goal') { out.goal = Number(argv[++i]); continue; }
77
+ if (a.startsWith('--goal=')) { out.goal = Number(a.slice(7)); continue; }
78
+ if (a === '--check') { out.check = Number(argv[++i]); continue; }
79
+ if (a.startsWith('--check=')) { out.check = Number(a.slice(8)); continue; }
80
+ if (a === '--limit') { out.limit = Number(argv[++i]); continue; }
81
+ if (a.startsWith('--limit=')) { out.limit = Number(a.slice(8)); continue; }
82
+ if (!out.from && /^\d+$/.test(a)) { out.from = Number(a); continue; }
83
+ }
84
+ return out;
85
+ }
86
+
87
+ // -------------------------------------------------------------------------
88
+ // Pure core (exported for tests)
89
+ // -------------------------------------------------------------------------
90
+
91
+ // Ids a task object points at, tolerating both the bare-number and the
92
+ // {id,kind} row shapes GET /tasks/:id has returned across schema versions.
93
+ // Ids must come out positive: Number(null) is 0, so a null edge would
94
+ // otherwise enter the graph as task 0 and be chased as a real dependency.
95
+ function edgeIds(list) {
96
+ return (list || [])
97
+ .map((d) => Number(d && typeof d === 'object' ? d.id : d))
98
+ .filter((n) => Number.isFinite(n) && n > 0);
99
+ }
100
+
101
+ // Kahn's algorithm over edges BETWEEN the given tasks. An out-of-scope
102
+ // dependency doesn't block ordering the scope itself — it surfaces later as a
103
+ // deps_unmet halt, which is the honest place for it. Ties break on the same
104
+ // keys the claimable feed sorts by, so a plan reads in the order the board
105
+ // would have offered the work.
106
+ function topoOrder(tasks) {
107
+ const inScope = new Set(tasks.map((t) => Number(t.id)));
108
+ const byId = new Map(tasks.map((t) => [Number(t.id), t]));
109
+ const remaining = new Set(inScope);
110
+ const order = [];
111
+
112
+ const rank = (id) => {
113
+ const t = byId.get(id);
114
+ return [
115
+ t.priority == null ? 99 : Number(t.priority),
116
+ Number(t.est_minutes_calibrated ?? t.est_minutes ?? 0) || 0,
117
+ Number(t.id),
118
+ ];
119
+ };
120
+ const cmp = (a, b) => {
121
+ const [pa, ma, ia] = rank(a);
122
+ const [pb, mb, ib] = rank(b);
123
+ return pa - pb || ma - mb || ia - ib;
124
+ };
125
+
126
+ let progressed = true;
127
+ while (remaining.size && progressed) {
128
+ progressed = false;
129
+ const free = Array.from(remaining).filter((id) => {
130
+ const deps = edgeIds(byId.get(id).dependencies).filter((d) => remaining.has(d));
131
+ return deps.length === 0;
132
+ });
133
+ if (free.length === 0) break;
134
+ free.sort(cmp);
135
+ for (const id of free) {
136
+ order.push(byId.get(id));
137
+ remaining.delete(id);
138
+ progressed = true;
139
+ }
140
+ }
141
+
142
+ return { order, cycle: Array.from(remaining).sort((a, b) => a - b) };
143
+ }
144
+
145
+ // The chain below a head: the head plus every task transitively reachable
146
+ // through dependents[]. Terminal tasks are walked THROUGH (a shipped task's
147
+ // successors are exactly what we want) but not included in the plan.
148
+ function chainFrom(headId, byId, { limit = DEFAULT_LIMIT } = {}) {
149
+ const seen = new Set();
150
+ const picked = [];
151
+ const queue = [Number(headId)];
152
+ while (queue.length) {
153
+ const id = queue.shift();
154
+ if (seen.has(id)) continue;
155
+ seen.add(id);
156
+ const t = byId.get(id);
157
+ if (!t) continue;
158
+ if (!TERMINAL.has(t.status)) picked.push(t);
159
+ for (const d of edgeIds(t.dependents)) queue.push(d);
160
+ if (picked.length >= limit) break;
161
+ }
162
+ return picked;
163
+ }
164
+
165
+ // The halt matrix. Facts only — every entry is something the runner can read
166
+ // off the task row, never a judgement about the work itself.
167
+ function haltReasons(task, { rank, claimableIds = null } = {}) {
168
+ const reasons = [];
169
+ if (!task) return [{ code: 'not_found', detail: 'no such task' }];
170
+
171
+ if (task.needs_migration) {
172
+ reasons.push({ code: 'needs_migration', detail: HALT_REASONS.needs_migration });
173
+ }
174
+ if (task.security_sensitive) {
175
+ reasons.push({ code: 'security_sensitive', detail: HALT_REASONS.security_sensitive });
176
+ }
177
+
178
+ const hits = matchProtected(task.touches || []);
179
+ if (hits.length) {
180
+ reasons.push({
181
+ code: 'protected_path',
182
+ detail: `${HALT_REASONS.protected_path} (${hits.map((h) => h.glob).join(', ')})`,
183
+ });
184
+ }
185
+
186
+ const floor = RANK_TIER[task.requires_rank] ?? 0;
187
+ const mine = RANK_TIER[rank] ?? 0;
188
+ if (floor > mine) {
189
+ reasons.push({
190
+ code: 'rank_floor',
191
+ detail: `needs rank '${task.requires_rank}', you are '${rank || 'unknown'}'`,
192
+ });
193
+ }
194
+
195
+ if (JUDGEMENT_KINDS.has(task.kind)) {
196
+ reasons.push({ code: 'judgement_kind', detail: `kind '${task.kind}' ${HALT_REASONS.judgement_kind}` });
197
+ }
198
+
199
+ const spec = String(task.description || '').trim();
200
+ if (spec.length < MIN_SPEC_CHARS) {
201
+ reasons.push({
202
+ code: 'thin_spec',
203
+ detail: spec ? `description is ${spec.length} chars — too thin to verify against` : 'no description',
204
+ });
205
+ }
206
+
207
+ if (IN_FLIGHT.has(task.status)) {
208
+ reasons.push({ code: 'in_flight', detail: `status is '${task.status}'` });
209
+ } else if (task.status !== 'ready') {
210
+ reasons.push({ code: 'deps_unmet', detail: `status is '${task.status}', not 'ready'` });
211
+ } else if (claimableIds && !claimableIds.has(Number(task.id))) {
212
+ // 'ready' but absent from the server's own claimable feed means an open
213
+ // blocker, a touches collision with a live claim, or a rank filter. Rather
214
+ // than re-deriving that query client-side (and drifting from it), defer.
215
+ reasons.push({ code: 'not_claimable', detail: HALT_REASONS.not_claimable });
216
+ }
217
+
218
+ return reasons;
219
+ }
220
+
221
+ function summarize(tasks) {
222
+ return tasks.reduce(
223
+ (acc, t) => ({
224
+ count: acc.count + 1,
225
+ credits: acc.credits + (Number(t.credits_reward) || 0),
226
+ minutes: acc.minutes + (Number(t.est_minutes_calibrated ?? t.est_minutes) || 0),
227
+ }),
228
+ { count: 0, credits: 0, minutes: 0 },
229
+ );
230
+ }
231
+
232
+ // -------------------------------------------------------------------------
233
+ // Fetching
234
+ // -------------------------------------------------------------------------
235
+
236
+ async function fetchTask(api, id) {
237
+ const r = await api.tasks.getTasksId({ id });
238
+ if (!r.ok) return null;
239
+ return r.data.task || r.data;
240
+ }
241
+
242
+ // Breadth-first over dependents[], fetching each task's detail once. The list
243
+ // endpoint omits the edge arrays, so the detail route is the only source.
244
+ async function fetchChain(api, headId, limit) {
245
+ const byId = new Map();
246
+ const queue = [Number(headId)];
247
+ const seen = new Set();
248
+ while (queue.length && byId.size < limit * 4) {
249
+ const wave = queue.splice(0, 12).filter((id) => !seen.has(id));
250
+ wave.forEach((id) => seen.add(id));
251
+ if (!wave.length) continue;
252
+ const rows = await Promise.all(wave.map((id) => fetchTask(api, id)));
253
+ for (const t of rows) {
254
+ if (!t) continue;
255
+ byId.set(Number(t.id), t);
256
+ for (const d of edgeIds(t.dependents)) if (!seen.has(d)) queue.push(d);
257
+ }
258
+ }
259
+ return byId;
260
+ }
261
+
262
+ async function fetchGoalTasks(api, goalId, limit) {
263
+ const r = await api.tasks.getTasks({ query: { limit: 1000 } });
264
+ const rows = (r.ok ? rowsOf(r.data) : null) || [];
265
+ const mine = rows
266
+ .filter((t) => Number(t.goal_id) === Number(goalId) && !TERMINAL.has(t.status))
267
+ .slice(0, limit);
268
+ const byId = new Map();
269
+ for (let i = 0; i < mine.length; i += 12) {
270
+ const rowsD = await Promise.all(mine.slice(i, i + 12).map((t) => fetchTask(api, t.id)));
271
+ rowsD.forEach((t) => t && byId.set(Number(t.id), t));
272
+ }
273
+ return byId;
274
+ }
275
+
276
+ // GET /tasks/claimable returns its rows under `claimable`; GET /tasks under
277
+ // `tasks`. Returning null (not an empty Set) on an unreadable response matters:
278
+ // haltReasons treats null as "couldn't check" and stays quiet, where an empty
279
+ // Set would mark every task not_claimable and halt the runner on its first step.
280
+ function rowsOf(payload) {
281
+ if (Array.isArray(payload)) return payload;
282
+ if (!payload || typeof payload !== 'object') return null;
283
+ for (const key of ['claimable', 'tasks', 'rows', 'items']) {
284
+ if (Array.isArray(payload[key])) return payload[key];
285
+ }
286
+ return null;
287
+ }
288
+
289
+ async function fetchClaimableIds(api) {
290
+ const r = await api.tasks.getTasksClaimable({ query: { limit: 500 } });
291
+ if (!r.ok) return null;
292
+ const rows = rowsOf(r.data);
293
+ return rows ? new Set(rows.map((t) => Number(t.id))) : null;
294
+ }
295
+
296
+ // -------------------------------------------------------------------------
297
+ // Output
298
+ // -------------------------------------------------------------------------
299
+
300
+ function taskLine(t, i) {
301
+ const est = t.est_minutes_calibrated ?? t.est_minutes;
302
+ return ` ${String(i + 1).padStart(2)}. task ${t.id} · ${t.status} · ${t.credits_reward ?? 0}c · ${est ? fmtMinutes(est) : '—'}\n ${t.title}`;
303
+ }
304
+
305
+ function printPlan({ plan, cycle, scope, base }) {
306
+ if (!plan.length) {
307
+ console.log(`No open tasks in ${scope}. ${HALT_REASONS.chain_complete}.`);
308
+ return;
309
+ }
310
+ const tot = summarize(plan);
311
+ console.log(`Chain for ${scope} — ${tot.count} open task(s), ${tot.credits}c, ${fmtMinutes(tot.minutes)}\n`);
312
+ plan.forEach((t, i) => console.log(taskLine(t, i)));
313
+ if (cycle.length) {
314
+ console.log(`\n⚠ ${cycle.length} task(s) could not be ordered — dependency cycle: ${cycle.join(', ')}`);
315
+ }
316
+ console.log(`\nHall: ${base}#/task/${plan[0].id}`);
317
+ console.log(`Next: node scripts/gds/sequence.js --from ${plan[0].id} --next`);
318
+ }
319
+
320
+ function printHalt(task, reasons) {
321
+ console.log(`HALT — task ${task.id}: ${task.title}\n`);
322
+ for (const r of reasons) console.log(` • [${r.code}] ${r.detail}`);
323
+ console.log('\nThis one needs a human. Everything already shipped stays shipped.');
324
+ }
325
+
326
+ // -------------------------------------------------------------------------
327
+ // Main
328
+ // -------------------------------------------------------------------------
329
+
330
+ async function main() {
331
+ const opts = readArgv(process.argv.slice(2));
332
+ if (!opts.from && !opts.goal && !opts.check) {
333
+ console.error('usage: sequence.js --from <task-id> | --goal <goal-id> | --check <task-id> [--next] [--json]');
334
+ process.exit(2);
335
+ }
336
+
337
+ await requireSession();
338
+ const api = await cliClient();
339
+ const meR = await api.me.getMe();
340
+ const rank = meR.ok ? meR.data.builder?.rank : null;
341
+ const base = hallBase();
342
+
343
+ // --check: one task, halt matrix, non-zero exit if anything fires.
344
+ if (opts.check) {
345
+ const t = await fetchTask(api, opts.check);
346
+ const claimableIds = await fetchClaimableIds(api);
347
+ const reasons = haltReasons(t, { rank, claimableIds });
348
+ if (opts.json) {
349
+ console.log(JSON.stringify({ task_id: opts.check, ok: reasons.length === 0, reasons }, null, 2));
350
+ } else if (!reasons.length) {
351
+ console.log(`task ${opts.check} is clear to run.`);
352
+ } else {
353
+ printHalt(t || { id: opts.check, title: '(not found)' }, reasons);
354
+ }
355
+ process.exit(reasons.length ? 1 : 0);
356
+ }
357
+
358
+ const byId = opts.goal
359
+ ? await fetchGoalTasks(api, opts.goal, opts.limit)
360
+ : await fetchChain(api, opts.from, opts.limit);
361
+
362
+ const scope = opts.goal ? `goal ${opts.goal}` : `task ${opts.from}`;
363
+ const candidates = opts.goal
364
+ ? Array.from(byId.values()).filter((t) => !TERMINAL.has(t.status))
365
+ : chainFrom(opts.from, byId, { limit: opts.limit });
366
+
367
+ const { order, cycle } = topoOrder(candidates);
368
+ const plan = order.slice(0, opts.limit);
369
+
370
+ // --next: the first task in the plan that clears the halt matrix.
371
+ if (opts.next) {
372
+ const claimableIds = await fetchClaimableIds(api);
373
+ const head = plan[0];
374
+ if (!head) {
375
+ const payload = { next: null, stop: 'chain_complete', detail: HALT_REASONS.chain_complete };
376
+ console.log(opts.json ? JSON.stringify(payload, null, 2) : `${HALT_REASONS.chain_complete} in ${scope}.`);
377
+ process.exit(0);
378
+ }
379
+ const reasons = haltReasons(head, { rank, claimableIds });
380
+ if (reasons.length) {
381
+ const payload = { next: null, stop: reasons[0].code, reasons, task: { id: head.id, title: head.title } };
382
+ if (opts.json) console.log(JSON.stringify(payload, null, 2));
383
+ else printHalt(head, reasons);
384
+ process.exit(1);
385
+ }
386
+ const remaining = summarize(plan);
387
+ if (opts.json) {
388
+ console.log(JSON.stringify({ next: head, remaining, scope }, null, 2));
389
+ } else {
390
+ console.log(`Next in ${scope}: task ${head.id} — ${head.title}`);
391
+ console.log(` ${head.credits_reward ?? 0}c · ${fmtMinutes(head.est_minutes_calibrated ?? head.est_minutes ?? 0)} · ${remaining.count - 1} more after this`);
392
+ console.log(` node scripts/gds/claim.js ${head.id}`);
393
+ }
394
+ process.exit(0);
395
+ }
396
+
397
+ if (opts.json) {
398
+ console.log(JSON.stringify({ scope, plan, cycle, totals: summarize(plan) }, null, 2));
399
+ return;
400
+ }
401
+ printPlan({ plan, cycle, scope, base });
402
+ }
403
+
404
+ if (require.main === module) {
405
+ main().catch((err) => {
406
+ console.error('fatal:', err.message || err);
407
+ process.exit(1);
408
+ });
409
+ }
410
+
411
+ module.exports = {
412
+ readArgv,
413
+ edgeIds,
414
+ rowsOf,
415
+ topoOrder,
416
+ chainFrom,
417
+ haltReasons,
418
+ summarize,
419
+ HALT_REASONS,
420
+ RANK_TIER,
421
+ MIN_SPEC_CHARS,
422
+ JUDGEMENT_KINDS,
423
+ TERMINAL,
424
+ IN_FLIGHT,
425
+ };
@@ -42,7 +42,16 @@ function encodeCwd(cwd) {
42
42
  // grader transcript's first user message is the worker system prompt — match it
43
43
  // to exclude it from the newest-file fallback. (The SessionEnd hook path is
44
44
  // unaffected: it passes an explicit transcript_path and never calls this.)
45
- const GRADER_WORKER_SIGNATURE = /You are the \*\*(Quality|Hacker|Narc|Efficiency)/;
45
+ // Match the SHAPE of the worker preamble, not a closed list of persona names: a
46
+ // new worker added to modules/grading/grader-workers/ would otherwise slip past
47
+ // this and be treated as a builder session — which now means real money, since
48
+ // backfill-sessions.js uses this to decide what NOT to pay for. Every worker
49
+ // opens `You are the **<Persona>** … adversarial subagent` (narc/hacker/efficiency
50
+ // say "a specialist adversarial subagent", quality says "specialist — an
51
+ // adversarial subagent"). Still anchored on that preamble rather than the bare
52
+ // phrase, so a real session that merely DISCUSSES adversarial subagents in its
53
+ // first message is not misread as one.
54
+ const GRADER_WORKER_SIGNATURE = /You are the \*\*[^*\n]{1,40}\*\*[^\n]{0,60}?adversarial subagent/;
46
55
 
47
56
  function firstUserText(file) {
48
57
  // Scan only until the first user message — avoids fully parsing a large
@@ -19,7 +19,18 @@
19
19
  // home directory) and prints the builder profile.
20
20
 
21
21
  const readline = require('readline');
22
- const { cliClient, saveSession, SESSION_PATH, loadSession, printOnboardingNudge, API_BASE } = require('./cli-lib');
22
+ const { cliClient, saveSession, SESSION_PATH, loadSession, printOnboardingNudge, API_BASE, hallBase } = require('./cli-lib');
23
+
24
+ // The instance's status dashboard, from branding — never a hardcoded host, so a
25
+ // non-OTB instance points its builders at ITS OWN dashboard (task 1002428).
26
+ // Falls back to the resolved API base when branding is unreadable.
27
+ function statusOrigin() {
28
+ try {
29
+ const d = require('../../src/branding').branding().domains || {};
30
+ if (d.statusOrigin) return d.statusOrigin;
31
+ } catch (_) { /* fall through */ }
32
+ return API_BASE;
33
+ }
23
34
  const onboardingConfig = require('./onboarding-config');
24
35
 
25
36
  const DRY_RUN = process.argv.includes('--dry-run');
@@ -70,9 +81,9 @@ async function promptGeminiKey() {
70
81
  console.log('Google AI Studio key for the pixel-art pipeline (per-builder, optional).');
71
82
  console.log(' Get one at https://aistudio.google.com/app/apikey');
72
83
  console.log(' (Or skip this and add it later with browser clicks: hall Settings →');
73
- console.log(' https://amazonprimea.com/builders/settings#art-key — stored encrypted, ADR 0073.)');
84
+ console.log(` ${hallBase()}/settings#art-key — stored encrypted, ADR 0073.)`);
74
85
  console.log(' Each builder pays their own Gemini bill so spend is attributed to them');
75
- console.log(' on https://status.amazonprimea.com.');
86
+ console.log(` on ${statusOrigin()}.`);
76
87
  let prompt;
77
88
  if (existing && typeof existing === 'string' && existing.length >= 4) {
78
89
  const last4 = existing.slice(-4);
@@ -265,6 +276,21 @@ async function promptOnboardingConsent() {
265
276
  (async () => {
266
277
  const forceFlag = process.argv.includes('--force');
267
278
 
279
+ // Environment preflight (task 1002401, onboarding-audit area 4). Before auth,
280
+ // surface the three things that used to bite a new builder one reactive crash
281
+ // at a time — Node, installed deps, a launchable `claude` CLI — each with a
282
+ // copy-paste fix, instead of `node: command not found` / `Cannot find module
283
+ // 'pg'` / SUBAGENT_SPAWN_FAILED turning up deep in the session. Read-only and
284
+ // NON-blocking: a fail is actionable, not fatal to sign-in, so it also runs in
285
+ // --dry-run. Shares one source of truth with `bongos doctor` (preflight.js).
286
+ try {
287
+ const { preflightChecks, formatPreflight } = require('./preflight');
288
+ console.log(formatPreflight(preflightChecks(), { color: process.stdout.isTTY, heading: true }));
289
+ console.log('');
290
+ } catch (err) {
291
+ console.error(`(environment preflight skipped: ${err.message})`);
292
+ }
293
+
268
294
  // --dry-run: describe every side effect the real flow would perform, without
269
295
  // doing any of them. No API calls, no file writes, no TTY prompts. Suitable
270
296
  // for a newcomer to read before running for real. Intentionally calls
@@ -378,6 +404,47 @@ async function promptOnboardingConsent() {
378
404
  }
379
405
  }
380
406
 
407
+ // Federation (ADR 0141 §1, task #1002108): a HOSTED instance has no GitHub app of
408
+ // its own — it federates sign-in through the Cloud Bongos hub. If the instance
409
+ // manifest advertises auth.idp, run the hub device flow + assertion exchange via
410
+ // the shared login.js runDeviceFlow, then the SAME onboarding prompts. The direct
411
+ // GitHub device flow below (auth.idp unset) is untouched — the self-hosted default.
412
+ let idpCfg = null;
413
+ try {
414
+ const m = await fetch(`${API_BASE}/api/gds/instance`);
415
+ const mj = m.ok ? await m.json() : null;
416
+ idpCfg = (mj && mj.auth && mj.auth.idp) || null;
417
+ } catch (_) { /* manifest unreachable → fall through to the direct flow */ }
418
+ if (idpCfg) {
419
+ console.log('Signing in via the Cloud Bongos platform (hub)...');
420
+ const { runDeviceFlow } = require('./login');
421
+ const flow = await runDeviceFlow(API_BASE, { log: console.log, idp: idpCfg });
422
+ if (flow.state === 'not_approved') {
423
+ console.error('');
424
+ console.error('This GitHub account has not been approved yet.');
425
+ console.error('An Archon must approve your access request before you can enroll.');
426
+ console.error(' → Ask an Archon to invite you, then re-run /builder-setup.');
427
+ process.exit(1);
428
+ }
429
+ if (flow.state !== 'ok') {
430
+ console.error(`\nSign-in did not complete (${flow.state}). Re-run /builder-setup.`);
431
+ process.exit(1);
432
+ }
433
+ const { token, builder } = flow;
434
+ await saveSession({ token, builder, api_base: API_BASE });
435
+ console.log('');
436
+ console.log(`Authenticated as ${builder.github_login} (#${builder.id}). Session saved to ${SESSION_PATH}.`);
437
+ try {
438
+ const { activate } = require('./install-git-hooks');
439
+ activate();
440
+ } catch (_) { /* hook activation is best-effort */ }
441
+ await promptGeminiKey();
442
+ await promptAnthropicLink();
443
+ await promptDisciplines();
444
+ await promptOnboardingConsent();
445
+ process.exit(0);
446
+ }
447
+
381
448
  console.log('Starting GitHub Device Flow...');
382
449
  const start = await (await cliClient()).auth.postAuthDeviceStart();
383
450
  if (!start.ok) {
@@ -411,7 +478,7 @@ async function promptOnboardingConsent() {
411
478
  if (poll.data && poll.data.state === 'not_approved') {
412
479
  console.error('');
413
480
  console.error('This GitHub account has not been approved yet.');
414
- console.error('Off the Boats admits new builders by invitation: an Archon must');
481
+ console.error(`${require('./cli-lib').productName()} admits new builders by invitation: an Archon must`);
415
482
  console.error('approve your access request before you can enroll.');
416
483
  console.error(' → Ask an Archon to invite you / approve your access request,');
417
484
  console.error(' then re-run /builder-setup (or: node scripts/gds/setup.js).');
@@ -485,7 +552,7 @@ async function promptOnboardingConsent() {
485
552
  printOnboardingNudge(meRes.data);
486
553
  console.log('');
487
554
  console.log('Newcomer path:');
488
- console.log(' 1. Choose thy craft: open https://amazonprimea.com/builders (creative / engineering)');
555
+ console.log(` 1. Choose thy craft: open ${hallBase()} (creative / engineering)`);
489
556
  console.log(' 2. Survey work: /builder-start');
490
557
  console.log(' 3. Claim + ship: /builder-claim N → /builder-ship');
491
558
  console.log(' 4. Ship three works and you rise to Thetes — the whole queue opens.');
@@ -68,7 +68,7 @@ async function main() {
68
68
  process.exit(1);
69
69
  }
70
70
 
71
- console.log('▸ Connecting to your Amazonprimea dev box…');
71
+ console.log(`▸ Connecting to your ${require('./cli-lib').productName()} dev box…`);
72
72
  const { box, keyPath } = await lib.prepareConnection({ log });
73
73
  const host = box.hostname;
74
74
 
@@ -81,8 +81,10 @@ async function main() {
81
81
  sshArgs.push(...remoteCmd);
82
82
  } else {
83
83
  firstRun = forceLogin || lib.isFirstLogin(host);
84
- sshArgs.push(lib.buildRemoteShellCommand({ firstRun }));
85
- console.log(`▸ Opening a shell on ${host} (${lib.WORKSPACE})…`);
84
+ // BV1.R101: re-scope /workspace to the active claim on connect, so an
85
+ // interactive shell always lands in a folder holding exactly that task's code.
86
+ sshArgs.push(lib.buildRemoteShellCommand({ firstRun, sync: true }));
87
+ console.log(`▸ Opening a shell on ${host} (${lib.WORKSPACE}, scoped to your active claim)…`);
86
88
  }
87
89
 
88
90
  const r = spawnSync('ssh', sshArgs, { stdio: 'inherit' });