@bongos/core 1.21.174 → 1.21.176

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 (1744) hide show
  1. package/.bongos-core.json +3565 -1515
  2. package/.claude/skills/ask-for-help/SKILL.md +4 -4
  3. package/.claude/skills/backlog-review/SKILL.md +1 -1
  4. package/.claude/skills/blocker-review/SKILL.md +2 -2
  5. package/.claude/skills/blocker-solve/SKILL.md +4 -4
  6. package/.claude/skills/bongos-feedback/SKILL.md +1 -1
  7. package/.claude/skills/bug-triage/SKILL.md +8 -8
  8. package/.claude/skills/builder-backup/SKILL.md +3 -3
  9. package/.claude/skills/builder-claim/SKILL.md +5 -5
  10. package/.claude/skills/builder-cost/SKILL.md +5 -5
  11. package/.claude/skills/builder-end/SKILL.md +6 -6
  12. package/.claude/skills/builder-exit/SKILL.md +2 -2
  13. package/.claude/skills/builder-key/SKILL.md +6 -6
  14. package/.claude/skills/builder-reauth/SKILL.md +4 -4
  15. package/.claude/skills/builder-redteam/SKILL.md +3 -3
  16. package/.claude/skills/builder-release/SKILL.md +3 -3
  17. package/.claude/skills/builder-sequence/SKILL.md +8 -8
  18. package/.claude/skills/builder-setup/SKILL.md +4 -4
  19. package/.claude/skills/builder-ship/SKILL.md +7 -7
  20. package/.claude/skills/builder-stage/SKILL.md +8 -8
  21. package/.claude/skills/builder-start/SKILL.md +4 -4
  22. package/.claude/skills/collab-review/SKILL.md +5 -5
  23. package/.claude/skills/design/SKILL.md +6 -4
  24. package/.claude/skills/design-sync/SKILL.md +3 -3
  25. package/.claude/skills/figma-design-sync/SKILL.md +4 -4
  26. package/.claude/skills/fix-task/SKILL.md +8 -8
  27. package/.claude/skills/goal-close/SKILL.md +9 -9
  28. package/.claude/skills/goal-create/SKILL.md +18 -18
  29. package/.claude/skills/grade-recover/SKILL.md +8 -8
  30. package/.claude/skills/grade-sweep/SKILL.md +15 -15
  31. package/.claude/skills/idea-triage/SKILL.md +15 -15
  32. package/.claude/skills/merge-mode/SKILL.md +14 -14
  33. package/.claude/skills/new-project/SKILL.md +5 -5
  34. package/.claude/skills/owner-review/SKILL.md +1 -1
  35. package/.claude/skills/planning-session/SKILL.md +14 -14
  36. package/.claude/skills/priority-session/SKILL.md +6 -6
  37. package/.claude/skills/read-session-export/SKILL.md +3 -3
  38. package/.claude/skills/recall/SKILL.md +7 -7
  39. package/.claude/skills/scan-before-install/SKILL.md +3 -3
  40. package/.claude/skills/session-handoff/SKILL.md +1 -1
  41. package/.claude/skills/ship-check/SKILL.md +3 -3
  42. package/.claude/skills/status/SKILL.md +3 -3
  43. package/.claude/skills/strand-fix/SKILL.md +6 -6
  44. package/.claude/skills/worktree-clean/SKILL.md +4 -4
  45. package/.gitattributes +12 -32
  46. package/GOVERNMENT.md +1 -1
  47. package/README.md +8 -8
  48. package/bin/bongos.js +23 -23
  49. package/clients/bongos-client/README.md +2 -2
  50. package/clients/bongos-client/bongos-client.global.js +22 -4
  51. package/clients/bongos-client/index.cjs +22 -4
  52. package/clients/bongos-client/index.d.ts +33 -4
  53. package/clients/bongos-client/index.mjs +21 -3
  54. package/config/branding.neutral.json +1 -1
  55. package/config/design-tokens.schema.json +1 -1
  56. package/config/modules.neutral.json +1 -3
  57. package/docs/adr/0029-status-page-outage-resilience.md +6 -6
  58. package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +1 -1
  59. package/docs/adr/0294-cli-sessions-do-not-expire.md +1 -1
  60. package/docs/adr/0383-sessions-end-after-ten-idle-days.md +35 -0
  61. package/docs/adr/0384-the-raise-page-is-a-hub-module-builders-edit-in-place.md +31 -0
  62. package/docs/adr/README.md +2 -0
  63. package/docs/api/openapi.json +606 -44
  64. package/docs/api-reference.md +25 -12
  65. package/docs/architecture.md +8 -7
  66. package/docs/canonical-permissions.md +3 -3
  67. package/docs/copy-inventory.md +1389 -1437
  68. package/docs/copy-registry.json +2160 -2634
  69. package/docs/design/demonolith-plan.md +6 -6
  70. package/docs/design/modular-architecture/00-research-report.md +1 -1
  71. package/docs/design/project-startup-direction.md +2 -2
  72. package/docs/design/projects-hub-direction-v2.md +1 -1
  73. package/docs/design/reviews/hall-v2/README.md +2 -2
  74. package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
  75. package/docs/file-map.md +89 -88
  76. package/docs/handoff-template.md +4 -4
  77. package/docs/module-api-changelog.md +6 -2
  78. package/docs/modules-contract.md +8 -7
  79. package/docs/onboarding/diagrams/01-task-lifecycle.mmd +1 -1
  80. package/docs/onboarding/diagrams/02-rank-ladder.mmd +1 -1
  81. package/docs/onboarding/diagrams/03-drachmae-karma.mmd +1 -1
  82. package/docs/onboarding/diagrams/04-architecture.mmd +1 -1
  83. package/docs/onboarding/diagrams/05-versioning.mmd +1 -1
  84. package/docs/onboarding/diagrams/README.md +2 -2
  85. package/docs/onboarding/diagrams/assertions.json +2 -2
  86. package/docs/onboarding/slash-commands.md +2 -2
  87. package/docs/packs/artist.md +4 -4
  88. package/docs/packs/engineer.md +11 -10
  89. package/docs/packs/ideator.md +7 -7
  90. package/docs/page-inventory.json +25 -1
  91. package/docs/page-readings.json +4388 -4602
  92. package/docs/recipes/README.md +1 -1
  93. package/docs/recipes/audit-replay.md +4 -4
  94. package/docs/recipes/autobongos-windows-host.md +6 -6
  95. package/docs/recipes/bongos-cli-release.md +2 -2
  96. package/docs/recipes/claude-design-loop.md +7 -7
  97. package/docs/recipes/core-release-pipeline.md +19 -19
  98. package/docs/recipes/failed-ship-recovery-proof.md +2 -2
  99. package/docs/recipes/figma-design-loop.md +6 -6
  100. package/docs/recipes/gds-db-backup.md +9 -9
  101. package/docs/recipes/instance-deploy.md +3 -3
  102. package/docs/recipes/instance-first-run.md +1 -1
  103. package/docs/recipes/multi-builder-merge.md +2 -2
  104. package/docs/recipes/ops-gotchas.md +10 -9
  105. package/docs/recipes/packaging-the-core.md +6 -6
  106. package/docs/recipes/parallel-goal-waves.md +4 -4
  107. package/docs/recipes/private-npm-distribution.md +9 -9
  108. package/docs/recipes/redteam-patrol.md +5 -5
  109. package/docs/recipes/render-pilot.md +2 -2
  110. package/docs/recipes/reranking-a-goals-board.md +2 -2
  111. package/docs/recipes/search-index-ingestion.md +5 -5
  112. package/docs/recipes/self-host.md +1 -1
  113. package/docs/recipes/ship-and-regrade.md +4 -4
  114. package/docs/recipes/staging-environment.md +1 -1
  115. package/docs/recipes/standalone-live-docs.md +1 -1
  116. package/docs/recipes/ui-look-before-you-ship.md +2 -2
  117. package/docs/recipes/upgrading-the-core.md +11 -11
  118. package/docs/recipes/windows-builders.md +18 -18
  119. package/docs/research/llm-model-agnostic-and-cheaper-grader.md +6 -6
  120. package/docs/research/task-network-map-readability.md +3 -3
  121. package/migrations/core_278_builder_tester_tag.sql +18 -0
  122. package/migrations/core_279_bongos_shipper_new_scripts_path.sql +13 -0
  123. package/modules/agents/lib/answer-hold.js +1 -1
  124. package/modules/agents/lib/gate.js +1 -1
  125. package/modules/agents/lib/validate.js +1 -1
  126. package/modules/autonomy/gauge.js +1 -1
  127. package/modules/autonomy/governor-docket.js +2 -2
  128. package/modules/autonomy/pollers/routine-timers.js +3 -3
  129. package/modules/autonomy/routes/autonomy.js +3 -3
  130. package/modules/builder-settings/interaction-prefs.js +1 -1
  131. package/modules/builder-settings/render-prefs.js +1 -1
  132. package/modules/builder-settings/routes/settings.js +1 -1
  133. package/modules/builder-settings/skill-prefs.js +2 -2
  134. package/modules/builder-settings/wandering-prefs.js +2 -2
  135. package/modules/copy-desk/module.json +2 -2
  136. package/modules/copy-desk/page-data.js +5 -5
  137. package/modules/copy-desk/page-decisions.js +13 -1
  138. package/modules/copy-desk/proposals.js +4 -4
  139. package/modules/copy-desk/registry.js +2 -2
  140. package/modules/copy-desk/routes/copy-desk-release.js +119 -0
  141. package/modules/copy-desk/routes/copy-desk.js +3 -12
  142. package/modules/copy-desk/skills/tweak/SKILL.md +12 -12
  143. package/modules/copy-desk/tests/copy_flags.mjs +1 -1
  144. package/modules/copy-desk/tests/copy_no_cms.mjs +7 -3
  145. package/modules/copy-desk/tests/copy_proposals.mjs +2 -2
  146. package/modules/copy-desk/tests/copy_queue.mjs +1 -1
  147. package/modules/design-styles/skills/README.md +4 -4
  148. package/modules/design-styles/skills/imagegen-frontend-mobile/SKILL.md +1 -1
  149. package/modules/design-styles/skills/imagegen-frontend-web/SKILL.md +1 -1
  150. package/modules/discord/bug-attachments.js +1 -1
  151. package/modules/discord/ship-broadcast.js +1 -1
  152. package/modules/economy/achievements.js +49 -7
  153. package/modules/economy/cost-classify.js +6 -6
  154. package/modules/economy/cost-report.js +1 -1
  155. package/modules/economy/cost.js +1 -1
  156. package/modules/economy/credits.js +2 -1
  157. package/modules/economy/idea-size.js +2 -2
  158. package/modules/economy/reward.js +2 -0
  159. package/modules/economy/routes/cost.js +1 -1
  160. package/modules/economy/streak.js +43 -4
  161. package/modules/government/asks.js +1 -1
  162. package/modules/government/board.js +19 -2
  163. package/modules/government/catalog.js +3 -0
  164. package/modules/government/migrations/government_033_idea_vote.sql +46 -0
  165. package/modules/government/protected-surfaces.json +48 -47
  166. package/modules/government/recommendations.js +6 -2
  167. package/modules/government/routes/government.js +1 -1
  168. package/modules/grading/generated-provenance.js +13 -5
  169. package/modules/grading/grade-engine.js +1 -1
  170. package/modules/grading/grader-rubric.json +1 -1
  171. package/modules/grading/grader-score.js +1 -1
  172. package/modules/grading/grader-workers/index.js +1 -1
  173. package/modules/grading/grader.js +1 -1
  174. package/modules/grading/routes/grade.js +1 -1
  175. package/modules/hall-ui/public/atlas.html +5 -7
  176. package/modules/hall-ui/public/atlas.js +1 -1
  177. package/modules/hall-ui/public/atlas.states.json +1 -1
  178. package/modules/hall-ui/public/blockers.css +9 -0
  179. package/modules/hall-ui/public/blockers.html +6 -4
  180. package/modules/hall-ui/public/blockers.js +39 -27
  181. package/modules/hall-ui/public/board-room.css +5 -12
  182. package/modules/hall-ui/public/board-room.html +6 -6
  183. package/modules/hall-ui/public/board-room.js +41 -23
  184. package/modules/hall-ui/public/board-room.states.json +1 -1
  185. package/modules/hall-ui/public/brand-apply.js +1 -1
  186. package/modules/hall-ui/public/brand-holes.js +1 -1
  187. package/modules/hall-ui/public/builders.js +40 -86
  188. package/modules/hall-ui/public/city-decide.js +18 -4
  189. package/modules/hall-ui/public/city.html +1 -1
  190. package/modules/hall-ui/public/collab.html +1 -1
  191. package/modules/hall-ui/public/commands.html +6 -9
  192. package/modules/hall-ui/public/copy-desk.html +1 -1
  193. package/modules/hall-ui/public/copy-desk.js +2 -2
  194. package/modules/hall-ui/public/craft-mode.js +4 -0
  195. package/modules/hall-ui/public/deploy.html +6 -2
  196. package/modules/hall-ui/public/deploy.js +21 -13
  197. package/modules/hall-ui/public/diagrams.css +1 -1
  198. package/modules/hall-ui/public/diagrams.html +5 -8
  199. package/modules/hall-ui/public/diagrams.states.json +1 -1
  200. package/modules/hall-ui/public/drachmae.html +3 -7
  201. package/modules/hall-ui/public/fleet.html +3 -2
  202. package/modules/hall-ui/public/fleet.js +0 -4
  203. package/modules/hall-ui/public/gate.html +7 -7
  204. package/modules/hall-ui/public/gate.js +58 -77
  205. package/modules/hall-ui/public/goal-inbox.js +69 -86
  206. package/modules/hall-ui/public/goal-map.html +1 -1
  207. package/modules/hall-ui/public/goals-page.js +5 -5
  208. package/modules/hall-ui/public/goals-render.js +10 -11
  209. package/modules/hall-ui/public/goals.css +10 -0
  210. package/modules/hall-ui/public/goals.html +14 -7
  211. package/modules/hall-ui/public/government.html +3 -5
  212. package/modules/hall-ui/public/government.js +1 -5
  213. package/modules/hall-ui/public/hall-ask.css +57 -0
  214. package/modules/hall-ui/public/hall-ask.js +50 -0
  215. package/modules/hall-ui/public/hall-render.js +117 -57
  216. package/modules/hall-ui/public/hall-widgets.js +11 -11
  217. package/modules/hall-ui/public/idea.html +1 -1
  218. package/modules/hall-ui/public/ideas.html +3 -1
  219. package/modules/hall-ui/public/ideas.js +1 -0
  220. package/modules/hall-ui/public/index.html +15 -6
  221. package/modules/hall-ui/public/index.states.json +1 -1
  222. package/modules/hall-ui/public/mine-lens.js +1 -1
  223. package/modules/hall-ui/public/modules.html +7 -10
  224. package/modules/hall-ui/public/modules.js +0 -2
  225. package/modules/hall-ui/public/my-agents.html +1 -1
  226. package/modules/hall-ui/public/palette.js +5 -5
  227. package/modules/hall-ui/public/primer.html +4 -9
  228. package/modules/hall-ui/public/profile-connections.js +10 -2
  229. package/modules/hall-ui/public/profile-roles.js +3 -10
  230. package/modules/hall-ui/public/profile.html +12 -10
  231. package/modules/hall-ui/public/profile.js +13 -17
  232. package/modules/hall-ui/public/project-settings.html +3 -4
  233. package/modules/hall-ui/public/project-settings.js +0 -3
  234. package/modules/hall-ui/public/ranks.html +5 -11
  235. package/modules/hall-ui/public/ranks.js +6 -8
  236. package/modules/hall-ui/public/roadmap.css +0 -2
  237. package/modules/hall-ui/public/roadmap.html +1 -1
  238. package/modules/hall-ui/public/roadmap.js +16 -51
  239. package/modules/hall-ui/public/roster.html +1 -1
  240. package/modules/hall-ui/public/sessions.html +6 -8
  241. package/modules/hall-ui/public/sessions.js +7 -7
  242. package/modules/hall-ui/public/settings.html +1 -1
  243. package/modules/hall-ui/public/shell-trail.js +1 -1
  244. package/modules/hall-ui/public/shell.js +17 -17
  245. package/modules/hall-ui/public/street-deck.css +2 -1
  246. package/modules/hall-ui/public/street-deck.js +10 -2
  247. package/modules/hall-ui/public/street-desk.js +16 -5
  248. package/modules/hall-ui/public/street.html +1 -1
  249. package/modules/hall-ui/public/studio-lib.js +4 -0
  250. package/modules/hall-ui/public/studio.css +2 -0
  251. package/modules/hall-ui/public/studio.html +1 -1
  252. package/modules/hall-ui/public/studio.js +27 -1
  253. package/modules/hall-ui/public/style.css +43 -26
  254. package/modules/hall-ui/public/task-detail.js +10 -7
  255. package/modules/hall-ui/public/task.css +2 -0
  256. package/modules/hall-ui/public/task.html +3 -1
  257. package/modules/hall-ui/public/task.js +23 -7
  258. package/modules/hall-ui/public/thinking.html +1 -1
  259. package/modules/hall-ui/public/tweak-editor.html +1 -1
  260. package/modules/hall-ui/public/vote-control.js +208 -0
  261. package/modules/hall-ui/public/watch.html +3 -5
  262. package/modules/hall-ui/public/watch.js +5 -4
  263. package/modules/hall-ui/public/welcome-boot.css +130 -0
  264. package/modules/hall-ui/public/welcome-boot.js +334 -0
  265. package/modules/hall-ui/public/welcome-day.webp +0 -0
  266. package/modules/hall-ui/public/welcome-night.webp +0 -0
  267. package/modules/hall-ui/public/work-summary.js +1 -1
  268. package/modules/hall-ui/public/work.css +1 -0
  269. package/modules/hall-ui/public/work.html +3 -1
  270. package/modules/hall-ui/public/work.js +2 -1
  271. package/modules/hall-ui/records/accomplishments.md +16 -0
  272. package/modules/hall-ui/records/hall-ask-card.md +33 -0
  273. package/modules/hall-ui/records/karma-vote-control.md +13 -0
  274. package/modules/hall-ui/records/oversight-family.md +1 -1
  275. package/modules/hall-ui/records/profile-connections.md +2 -0
  276. package/modules/hall-ui/records/profile-roles.md +1 -1
  277. package/modules/hall-ui/records/reading-rooms.md +1 -1
  278. package/modules/hall-ui/records/welcome-tower.md +50 -0
  279. package/modules/ideas/migrations/ideas_011_idea_votes.sql +31 -0
  280. package/modules/ideas/projection.js +1 -1
  281. package/modules/ideas/routes/blockers.js +3 -3
  282. package/modules/ideas/routes/inbox.js +29 -1
  283. package/modules/ideas/routes/ratify-goal.js +1 -1
  284. package/modules/ideas/routing.js +1 -1
  285. package/modules/ideas/templates.js +1 -1
  286. package/modules/ideas/trail-descent.js +1 -1
  287. package/modules/ideas/trail-size.js +1 -1
  288. package/modules/ideas/votes.js +93 -0
  289. package/modules/lifecycle/abandon-leftovers.js +1 -1
  290. package/modules/lifecycle/cascade.js +1 -1
  291. package/modules/lifecycle/conflict-resolve.js +13 -21
  292. package/modules/lifecycle/criterion-ref.js +1 -1
  293. package/modules/lifecycle/db-analytics.js +57 -4
  294. package/modules/lifecycle/db-deps-criteria.js +1 -1
  295. package/modules/lifecycle/db-ship.js +2 -2
  296. package/modules/lifecycle/db-tasks.js +2 -2
  297. package/modules/lifecycle/db-versions.js +1 -1
  298. package/modules/lifecycle/db.js +2 -0
  299. package/modules/lifecycle/dead-deps.js +3 -3
  300. package/modules/lifecycle/dependency-advisory.js +3 -3
  301. package/modules/lifecycle/goal-category.js +1 -1
  302. package/modules/lifecycle/goal-forest.js +1 -1
  303. package/modules/lifecycle/goal-task-relevance-advisory.js +1 -1
  304. package/modules/lifecycle/kickoff-checklist.js +1 -1
  305. package/modules/lifecycle/kickoff-seed.js +1 -1
  306. package/modules/lifecycle/lifecycle.js +2 -0
  307. package/modules/lifecycle/merge-reach.js +29 -15
  308. package/modules/lifecycle/missing-dep-classify.js +3 -3
  309. package/modules/lifecycle/module.json +2 -1
  310. package/modules/lifecycle/page-tweak-approve.js +85 -1
  311. package/modules/lifecycle/page-tweak-hold.js +1 -1
  312. package/modules/lifecycle/publish-reconciler.js +1 -2
  313. package/modules/lifecycle/rot.js +1 -1
  314. package/modules/lifecycle/routes/artist-gate.js +1 -1
  315. package/modules/lifecycle/routes/gate-approvals.js +22 -1
  316. package/modules/lifecycle/routes/goals.js +1 -1
  317. package/modules/lifecycle/routes/task-route-helpers.js +1 -1
  318. package/modules/lifecycle/routes/task-votes.js +54 -0
  319. package/modules/lifecycle/routes/task-write-routes.js +1 -1
  320. package/modules/lifecycle/routes/tasks.js +2 -2
  321. package/modules/lifecycle/routes/versions.js +2 -2
  322. package/modules/lifecycle/ship-card.js +4 -4
  323. package/modules/lifecycle/ship-preflight.js +1 -1
  324. package/modules/lifecycle/task-classifier.js +1 -1
  325. package/modules/lifecycle/task-status.js +1 -1
  326. package/modules/lifecycle/task-visual-db.js +1 -1
  327. package/modules/memory/learnings.js +1 -1
  328. package/modules/memory/pollers/search-index.js +2 -2
  329. package/modules/memory/routes/learnings.js +1 -1
  330. package/modules/memory/routes/memory.js +1 -1
  331. package/modules/memory/search.js +2 -2
  332. package/modules/npm-release/release.js +1 -1
  333. package/modules/onboarding/howto-moments.js +1 -1
  334. package/modules/onboarding/newcomer-restock.js +3 -3
  335. package/modules/onboarding/onboarding.js +1 -1
  336. package/modules/pixel-art/skills/otb-tile-generate/SKILL.md +1 -1
  337. package/modules/platform-identity/origin-containment.js +1 -1
  338. package/modules/platform-identity/publish-gate.js +1 -1
  339. package/modules/platform-identity/tests/platform-identity.mjs +1 -1
  340. package/modules/provisioning/capacity.js +1 -1
  341. package/modules/provisioning/core-upgrade.js +15 -9
  342. package/modules/provisioning/credential-preflight.js +3 -3
  343. package/modules/provisioning/disconnect.js +1 -1
  344. package/modules/provisioning/dns-resolves.js +1 -1
  345. package/modules/provisioning/module.json +1 -1
  346. package/modules/provisioning/onboard-plan.js +1 -1
  347. package/modules/provisioning/paid-shape-gate.js +2 -2
  348. package/modules/provisioning/planet-physics.js +11 -2
  349. package/modules/provisioning/pollers/liveness-sweep.js +1 -1
  350. package/modules/provisioning/pollers/runner-drift-notify.js +5 -5
  351. package/modules/provisioning/provisioning.js +2 -2
  352. package/modules/provisioning/render-lookup.js +1 -1
  353. package/modules/provisioning/render-standup.js +3 -3
  354. package/modules/provisioning/repo-private.js +1 -1
  355. package/modules/provisioning/routes/callback-page.js +1 -1
  356. package/modules/provisioning/routes/core-upgrade.js +23 -3
  357. package/modules/provisioning/routes/provisioning.js +2 -2
  358. package/modules/provisioning/routes/render-standup.js +1 -1
  359. package/modules/provisioning/routes/repo-private.js +1 -1
  360. package/modules/provisioning/soft-limits.js +1 -1
  361. package/modules/provisioning/starter-bundles.js +4 -2
  362. package/modules/provisioning/tests/provisioning.mjs +1 -1
  363. package/modules/public-landing/public/account.html +4 -6
  364. package/modules/public-landing/public/assets/cosmos.css +13 -6
  365. package/modules/public-landing/public/connections.html +4 -6
  366. package/modules/public-landing/public/contact.html +4 -6
  367. package/modules/public-landing/public/guild.html +4 -6
  368. package/modules/public-landing/public/index.html +11 -9
  369. package/modules/public-landing/public/invitations.html +4 -6
  370. package/modules/public-landing/public/privacy.html +4 -6
  371. package/modules/public-landing/public/projects.html +168 -46
  372. package/modules/public-landing/public/terms.html +4 -6
  373. package/modules/raise/content/copy.json +40 -0
  374. package/modules/raise/content/nda.html +39 -0
  375. package/modules/raise/content/settings.json +49 -0
  376. package/modules/raise/db.js +95 -0
  377. package/modules/raise/migrations/raise_001_tables.sql +71 -0
  378. package/modules/raise/module.json +28 -0
  379. package/modules/raise/page.js +194 -0
  380. package/modules/raise/public/edit.js +152 -0
  381. package/modules/raise/public/favicon.svg +1 -0
  382. package/modules/raise/public/logo-ink.svg +1 -0
  383. package/modules/raise/public/logo-night.svg +1 -0
  384. package/modules/raise/public/mode.js +8 -0
  385. package/modules/raise/public/raise.css +234 -0
  386. package/modules/raise/public/raise.js +91 -0
  387. package/modules/raise/raise.js +174 -0
  388. package/modules/raise/routes/raise.js +152 -0
  389. package/modules/raise/views/gate.html +39 -0
  390. package/modules/raise/views/nda.html +21 -0
  391. package/modules/raise/views/page.html +133 -0
  392. package/modules/raise/web.js +72 -0
  393. package/modules/render-deploy/deploys.js +1 -1
  394. package/modules/render-deploy/render.js +2 -2
  395. package/modules/security/secret-scrub.js +3 -3
  396. package/modules/security/security.js +1 -1
  397. package/modules/security/tests/artifact_scan.mjs +1 -1
  398. package/modules/security/tests/mirror.mjs +1 -1
  399. package/modules/security/tests/scan_store.mjs +1 -1
  400. package/modules/sessions/db.js +2 -2
  401. package/modules/sessions/pulse.js +2 -2
  402. package/modules/sessions/routes/sessions.js +1 -1
  403. package/modules/specialities/specialities.js +1 -1
  404. package/modules/status-ui/public/README.md +3 -4
  405. package/modules/status-ui/public/index.html +5 -31
  406. package/modules/status-ui/public/status.js +6 -174
  407. package/modules/status-ui/public/style.css +4 -133
  408. package/modules/ui-design/docs/design-contract.md +3 -3
  409. package/modules/ui-design/kit/README.md +1 -1
  410. package/modules/ui-design/kit/serve.js +13 -4
  411. package/modules/ui-design/scripts/design-sync.js +1 -1
  412. package/modules/ui-design/scripts/figma-design-sync.js +1 -1
  413. package/modules/ui-design/scripts/validate-design.js +1 -1
  414. package/package-lock.json +27 -11
  415. package/package.json +2 -3
  416. package/release-notes.json +178 -0
  417. package/scripts/bongos/CLAUDE.md +15 -0
  418. package/scripts/bongos/abandon.js +137 -0
  419. package/scripts/bongos/accessibility-replay.js +195 -0
  420. package/scripts/bongos/address-park-sweep.sh +91 -0
  421. package/scripts/bongos/adopt-detect.js +123 -0
  422. package/scripts/bongos/adopt-import.js +111 -0
  423. package/scripts/bongos/adopt-preflight.js +187 -0
  424. package/scripts/bongos/adr-namespace.js +273 -0
  425. package/scripts/bongos/agent-invoke.js +689 -0
  426. package/scripts/bongos/agents-sync.js +502 -0
  427. package/scripts/bongos/api-alias-caller-scan.js +256 -0
  428. package/scripts/bongos/api-artifacts.js +91 -0
  429. package/scripts/bongos/api.js +86 -0
  430. package/scripts/bongos/api.ps1 +26 -0
  431. package/scripts/bongos/architect-audit.js +294 -0
  432. package/scripts/bongos/artifact-audit.js +326 -0
  433. package/scripts/bongos/artifact-format.js +203 -0
  434. package/scripts/bongos/artifact-staleness.js +267 -0
  435. package/scripts/bongos/ask.js +231 -0
  436. package/scripts/bongos/audit-authorship.sh +329 -0
  437. package/scripts/bongos/audit-deps.js +189 -0
  438. package/scripts/bongos/audit-gate.js +121 -0
  439. package/scripts/bongos/audit-rank-roles.js +172 -0
  440. package/scripts/bongos/audit-replay.sh +205 -0
  441. package/scripts/bongos/autobongos-digest.js +123 -0
  442. package/scripts/bongos/autobongos-grade-cap.js +187 -0
  443. package/scripts/bongos/autobongos-loop.js +210 -0
  444. package/scripts/bongos/autobongos-run.js +1361 -0
  445. package/scripts/bongos/autobongos-service.cmd +50 -0
  446. package/scripts/bongos/autobongos-service.ps1 +270 -0
  447. package/scripts/bongos/autobongos-tweak-lane.js +262 -0
  448. package/scripts/bongos/autobongos-verify.js +618 -0
  449. package/scripts/bongos/autonomy-gate.js +135 -0
  450. package/scripts/bongos/autonomy-gauge-statusline.js +124 -0
  451. package/scripts/bongos/autonomy-gauge.js +84 -0
  452. package/scripts/bongos/backfill-cost.js +176 -0
  453. package/scripts/bongos/backfill-disciplines.js +144 -0
  454. package/scripts/bongos/backfill-session-earnings.js +123 -0
  455. package/scripts/bongos/backfill-session-types.js +87 -0
  456. package/scripts/bongos/backfill-sessions.js +411 -0
  457. package/scripts/bongos/backfill-subagent-rewards.js +485 -0
  458. package/scripts/bongos/backfill-task-rewards.js +413 -0
  459. package/scripts/bongos/backlog-review.js +399 -0
  460. package/scripts/bongos/backup-dropin-sweep.sh +95 -0
  461. package/scripts/bongos/backup.js +116 -0
  462. package/scripts/bongos/baseline-staleness.js +217 -0
  463. package/scripts/bongos/bfg-principal-token.js +65 -0
  464. package/scripts/bongos/bug-attachment-audit.js +149 -0
  465. package/scripts/bongos/bug-attachment-recover.js +198 -0
  466. package/scripts/bongos/bug-triage.js +292 -0
  467. package/scripts/bongos/build-bongos.js +94 -0
  468. package/scripts/bongos/bump-version.js +221 -0
  469. package/scripts/bongos/capture.js +481 -0
  470. package/scripts/bongos/category-advisory-guard.js +206 -0
  471. package/scripts/bongos/ci-grade.js +82 -0
  472. package/scripts/bongos/claim-recall.js +112 -0
  473. package/scripts/bongos/claim.js +789 -0
  474. package/scripts/bongos/classify-discipline.js +153 -0
  475. package/scripts/bongos/claude-materialize.js +1200 -0
  476. package/scripts/bongos/claude-md-template.js +142 -0
  477. package/scripts/bongos/cleanup-orphans.js +74 -0
  478. package/scripts/bongos/cli-lib.js +1226 -0
  479. package/scripts/bongos/client-baseurl-guard.js +116 -0
  480. package/scripts/bongos/codemod-rename-scripts-gds.js +381 -0
  481. package/scripts/bongos/codemod-rename-src-gds.js +145 -0
  482. package/scripts/bongos/conductor-dispatch.js +535 -0
  483. package/scripts/bongos/context-pack.js +326 -0
  484. package/scripts/bongos/control-manifest.js +199 -0
  485. package/scripts/bongos/copy-apply.js +758 -0
  486. package/scripts/bongos/copy-inventory.js +571 -0
  487. package/scripts/bongos/core-origin-follow.js +185 -0
  488. package/scripts/bongos/core-pin-sync.js +260 -0
  489. package/scripts/bongos/coreB-carve-proof.sh +725 -0
  490. package/scripts/bongos/cost-report.js +63 -0
  491. package/scripts/bongos/cost.js +47 -0
  492. package/scripts/bongos/credential-presence.js +296 -0
  493. package/scripts/bongos/cutover-codemod.js +240 -0
  494. package/scripts/bongos/db-backup-nightly.sh +232 -0
  495. package/scripts/bongos/db-backup-offsite.sh +267 -0
  496. package/scripts/bongos/demote.js +45 -0
  497. package/scripts/bongos/deploy-divergence.js +454 -0
  498. package/scripts/bongos/deploy-main-audit-gate.sh +97 -0
  499. package/scripts/bongos/design-sync.js +12 -0
  500. package/scripts/bongos/dev-box-guard.js +166 -0
  501. package/scripts/bongos/dev-lib.js +722 -0
  502. package/scripts/bongos/dev.js +645 -0
  503. package/scripts/bongos/device-flow-help.js +72 -0
  504. package/scripts/bongos/diagnose-achievements.js +118 -0
  505. package/scripts/bongos/diagram-facts.js +355 -0
  506. package/scripts/bongos/discord-channels.js +77 -0
  507. package/scripts/bongos/do-api.js +109 -0
  508. package/scripts/bongos/doc-cli-guard.js +274 -0
  509. package/scripts/bongos/doc-comment-coverage.js +139 -0
  510. package/scripts/bongos/doc-control-claims.js +170 -0
  511. package/scripts/bongos/docs-entropy.js +453 -0
  512. package/scripts/bongos/doctor.js +493 -0
  513. package/scripts/bongos/enroll-control-plane.js +101 -0
  514. package/scripts/bongos/exec-path-guard.js +564 -0
  515. package/scripts/bongos/feedback-send.js +249 -0
  516. package/scripts/bongos/fetch-art-key.js +106 -0
  517. package/scripts/bongos/fetch-sound-prefs.js +126 -0
  518. package/scripts/bongos/figma-design-sync.js +11 -0
  519. package/scripts/bongos/find-untested.js +371 -0
  520. package/scripts/bongos/fitness-checks-error-envelope.js +134 -0
  521. package/scripts/bongos/fitness-checks-identity.js +343 -0
  522. package/scripts/bongos/fitness-checks-packaging.js +132 -0
  523. package/scripts/bongos/fitness-checks-route-pins.js +76 -0
  524. package/scripts/bongos/fitness-checks-vocabulary.js +392 -0
  525. package/scripts/bongos/fitness-checks-write-validation.js +157 -0
  526. package/scripts/bongos/fitness-lib.js +127 -0
  527. package/scripts/bongos/fitness-migration-contraction.js +50 -0
  528. package/scripts/bongos/fitness-ratchets.js +594 -0
  529. package/scripts/bongos/fitness.js +1498 -0
  530. package/scripts/bongos/forwarder.js +80 -0
  531. package/scripts/bongos/gate-review.js +330 -0
  532. package/scripts/bongos/gating.js +348 -0
  533. package/scripts/{gds → bongos}/gds-db-backup.service +6 -6
  534. package/scripts/{gds → bongos}/gds-db-backup.timer +4 -4
  535. package/scripts/{gds → bongos}/gds-db-offsite.service +4 -4
  536. package/scripts/{gds → bongos}/gds-db-offsite.timer +4 -4
  537. package/scripts/bongos/gds-literal-scan.js +196 -0
  538. package/scripts/bongos/gen-api-client.js +576 -0
  539. package/scripts/bongos/gen-api-docs.js +1111 -0
  540. package/scripts/bongos/gen-atlas.js +366 -0
  541. package/scripts/bongos/gen-diagrams.js +608 -0
  542. package/scripts/bongos/gen-file-map.js +337 -0
  543. package/scripts/bongos/gen-freshness.js +32 -0
  544. package/scripts/bongos/gen-interaction-posture.js +122 -0
  545. package/scripts/bongos/gen-repo-map.js +540 -0
  546. package/scripts/bongos/gen-role-responsibilities.js +134 -0
  547. package/scripts/bongos/gen-session-index.js +243 -0
  548. package/scripts/bongos/gen-sounds.js +176 -0
  549. package/scripts/bongos/git-merge-regen.js +254 -0
  550. package/scripts/bongos/go-live.js +1071 -0
  551. package/scripts/bongos/goal-names.js +102 -0
  552. package/scripts/bongos/goal.js +272 -0
  553. package/scripts/bongos/government-walk.js +1477 -0
  554. package/scripts/bongos/grade-correlation-audit.js +310 -0
  555. package/scripts/bongos/grade-replay.js +237 -0
  556. package/scripts/bongos/grade-smoke.js +205 -0
  557. package/scripts/bongos/home-isolation-guard.js +126 -0
  558. package/scripts/bongos/hooks/pre-push.js +211 -0
  559. package/scripts/bongos/howto.js +145 -0
  560. package/scripts/bongos/http-api-client.js +81 -0
  561. package/scripts/bongos/identity-strip.js +75 -0
  562. package/scripts/bongos/init.js +1159 -0
  563. package/scripts/bongos/install-git-hooks.js +200 -0
  564. package/scripts/bongos/instance-apex.js +38 -0
  565. package/scripts/bongos/intent-retry.js +52 -0
  566. package/scripts/bongos/key-card.js +222 -0
  567. package/scripts/bongos/key-status.js +126 -0
  568. package/scripts/bongos/land-watch.js +201 -0
  569. package/scripts/bongos/leak-scan-allowlist.js +293 -0
  570. package/scripts/bongos/learning-capture.js +145 -0
  571. package/scripts/bongos/lib.sh +74 -0
  572. package/scripts/bongos/linkify-refs.js +655 -0
  573. package/scripts/bongos/lint-infra-exec.js +70 -0
  574. package/scripts/bongos/lint-migrations.js +102 -0
  575. package/scripts/bongos/lint-workflow-exec.js +63 -0
  576. package/scripts/bongos/local-preview-lib.js +128 -0
  577. package/scripts/bongos/local-preview.js +82 -0
  578. package/scripts/bongos/login.js +360 -0
  579. package/scripts/bongos/main-audit.js +625 -0
  580. package/scripts/bongos/me.js +97 -0
  581. package/scripts/bongos/memory-map.js +176 -0
  582. package/scripts/bongos/merge-lock.js +71 -0
  583. package/scripts/bongos/migration-namespace.js +313 -0
  584. package/scripts/bongos/mirror-lineage-check.js +343 -0
  585. package/scripts/bongos/mirror-redact.js +350 -0
  586. package/scripts/bongos/module-artifact.js +289 -0
  587. package/scripts/bongos/module-assess-docs.js +238 -0
  588. package/scripts/bongos/module-assess-installs.js +228 -0
  589. package/scripts/bongos/module-assess-score.js +111 -0
  590. package/scripts/bongos/module-assess-security.js +252 -0
  591. package/scripts/bongos/module-assess-tests.js +278 -0
  592. package/scripts/bongos/module-assess-version.js +92 -0
  593. package/scripts/bongos/module-error-report.js +148 -0
  594. package/scripts/{gds → bongos}/module-test-runner/README.md +3 -3
  595. package/scripts/{gds → bongos}/module-test-runner/module-tests.yml +2 -2
  596. package/scripts/bongos/module-test-runner.js +414 -0
  597. package/scripts/bongos/module.js +1376 -0
  598. package/scripts/bongos/move-escalation.js +141 -0
  599. package/scripts/{gds → bongos}/newcomer-floor.config.json +1 -1
  600. package/scripts/bongos/newcomer-floor.js +421 -0
  601. package/scripts/{gds → bongos}/newcomer-floor.templates.json +13 -13
  602. package/scripts/bongos/newcomer-floor.test.js +235 -0
  603. package/scripts/bongos/next-number.js +163 -0
  604. package/scripts/bongos/oauth-secret.js +202 -0
  605. package/scripts/bongos/onboard.js +267 -0
  606. package/scripts/bongos/onboarding-analyze.js +226 -0
  607. package/scripts/bongos/onboarding-config.js +179 -0
  608. package/scripts/bongos/onboarding-ship.js +92 -0
  609. package/scripts/bongos/pack-env-prefix.js +73 -0
  610. package/scripts/bongos/package-core.js +1200 -0
  611. package/scripts/bongos/page-inventory.js +348 -0
  612. package/scripts/bongos/page-reader.js +722 -0
  613. package/scripts/bongos/paste-token.js +212 -0
  614. package/scripts/bongos/plain-cards.js +351 -0
  615. package/scripts/bongos/portability-smoke-art.sh +85 -0
  616. package/scripts/bongos/portability-smoke-memory.sh +176 -0
  617. package/scripts/bongos/preflight.js +214 -0
  618. package/scripts/bongos/probe-cache.js +246 -0
  619. package/scripts/bongos/prove-strict-versioning.js +284 -0
  620. package/scripts/bongos/provision-auto-upgrade.js +162 -0
  621. package/scripts/bongos/provision-byo-host.js +68 -0
  622. package/scripts/bongos/provision-capacity.js +79 -0
  623. package/scripts/bongos/provision-caps.js +63 -0
  624. package/scripts/bongos/provision-config.js +233 -0
  625. package/scripts/bongos/provision-core-upgrade.js +1166 -0
  626. package/scripts/bongos/provision-disconnect.js +88 -0
  627. package/scripts/bongos/provision-github-setup.js +168 -0
  628. package/scripts/bongos/provision-net.js +357 -0
  629. package/scripts/bongos/provision-pin-key.js +176 -0
  630. package/scripts/bongos/provision-pin-replay.js +126 -0
  631. package/scripts/bongos/provision-process.js +170 -0
  632. package/scripts/bongos/provision-render.js +201 -0
  633. package/scripts/bongos/provision-repo-auth.js +164 -0
  634. package/scripts/bongos/provision-repo-private.js +98 -0
  635. package/scripts/bongos/provision-repo.js +1337 -0
  636. package/scripts/bongos/provision-teardown.js +106 -0
  637. package/scripts/bongos/provision-units.js +469 -0
  638. package/scripts/bongos/provision.js +1479 -0
  639. package/scripts/bongos/publish-credential-check.js +66 -0
  640. package/scripts/bongos/publish-integration-gate.js +142 -0
  641. package/scripts/bongos/publish-manifest.js +440 -0
  642. package/scripts/bongos/publish-watch.js +268 -0
  643. package/scripts/bongos/pulse.js +336 -0
  644. package/scripts/bongos/push-main.js +56 -0
  645. package/scripts/bongos/push-path-brief.js +100 -0
  646. package/scripts/bongos/read-session-export.js +359 -0
  647. package/scripts/bongos/reauth.js +155 -0
  648. package/scripts/bongos/recall.js +183 -0
  649. package/scripts/bongos/redteam-patrol.js +437 -0
  650. package/scripts/bongos/regen-instance-docs.js +47 -0
  651. package/scripts/bongos/release-core.js +80 -0
  652. package/scripts/bongos/release-notes.js +271 -0
  653. package/scripts/bongos/release.js +139 -0
  654. package/scripts/bongos/rename-goals.js +109 -0
  655. package/scripts/bongos/rename-history-check.js +369 -0
  656. package/scripts/bongos/rename-ledger-prose.js +482 -0
  657. package/scripts/bongos/render-api.js +197 -0
  658. package/scripts/bongos/render-check.js +206 -0
  659. package/scripts/bongos/render-ideas.js +226 -0
  660. package/scripts/bongos/render-payload.js +85 -0
  661. package/scripts/bongos/repo-metrics.js +363 -0
  662. package/scripts/bongos/review-sheet.js +108 -0
  663. package/scripts/bongos/role-pack-guard.js +142 -0
  664. package/scripts/bongos/route-shadow-guard.js +175 -0
  665. package/scripts/bongos/routine-schedule.js +283 -0
  666. package/scripts/bongos/run-routine.js +257 -0
  667. package/scripts/bongos/run-unit-tests.js +654 -0
  668. package/scripts/bongos/runner-drift.js +151 -0
  669. package/scripts/bongos/sandbox-stage.js +459 -0
  670. package/scripts/bongos/scope-map-guard.js +300 -0
  671. package/scripts/bongos/scope-roster-guard.js +118 -0
  672. package/scripts/bongos/seam-wiring-guard.js +275 -0
  673. package/scripts/bongos/search-ingest-db.js +288 -0
  674. package/scripts/bongos/search-ingest-md.js +269 -0
  675. package/scripts/bongos/seed-adopt-tasks.js +75 -0
  676. package/scripts/bongos/seed-api-contract-goal-tasks.js +141 -0
  677. package/scripts/bongos/seed-board-room-tasks.js +196 -0
  678. package/scripts/bongos/seed-bongos-app-tasks.js +294 -0
  679. package/scripts/bongos/seed-bongos-coreB-tranche1-tasks.js +81 -0
  680. package/scripts/bongos/seed-bongos-coreB-tranche2-tasks.js +100 -0
  681. package/scripts/bongos/seed-bongos-goal-hierarchy-tasks.js +307 -0
  682. package/scripts/bongos/seed-bongos-module-system-tasks.js +346 -0
  683. package/scripts/bongos/seed-bongos-tasks.js +214 -0
  684. package/scripts/bongos/seed-bongos-v1-idea-routing-tasks.js +152 -0
  685. package/scripts/bongos/seed-bongos-v1-seo-tasks.js +104 -0
  686. package/scripts/bongos/seed-bv1-community-tasks.js +131 -0
  687. package/scripts/bongos/seed-bv1-privacy-invites-tasks.js +133 -0
  688. package/scripts/bongos/seed-bv1-profile-page-tasks.js +161 -0
  689. package/scripts/bongos/seed-bv1-profiles-tasks.js +135 -0
  690. package/scripts/bongos/seed-bv1-r01-delta-tasks.js +154 -0
  691. package/scripts/bongos/seed-bv2-ideator-surface-tasks.js +390 -0
  692. package/scripts/bongos/seed-bv2-tweak-mode-tasks.js +277 -0
  693. package/scripts/bongos/seed-gds-v4-tasks.js +624 -0
  694. package/scripts/bongos/seed-governance-tasks.js +340 -0
  695. package/scripts/bongos/seed-medusa-tasks.js +440 -0
  696. package/scripts/bongos/seed-module-upstreaming.js +219 -0
  697. package/scripts/bongos/seed-productization-tasks.js +235 -0
  698. package/scripts/bongos/seed-project-admin-console-tasks.js +88 -0
  699. package/scripts/bongos/seed-provisioning-tasks.js +83 -0
  700. package/scripts/bongos/seed-staging.sh +117 -0
  701. package/scripts/bongos/seed-strict-versioning-tasks.js +275 -0
  702. package/scripts/bongos/seed-v2-world-tasks.js +283 -0
  703. package/scripts/bongos/seed-verify-criteria.js +124 -0
  704. package/scripts/bongos/sequence.js +597 -0
  705. package/scripts/bongos/session-cost.js +430 -0
  706. package/scripts/bongos/session-digest-build.js +293 -0
  707. package/scripts/bongos/session-digest.js +200 -0
  708. package/scripts/bongos/session-eval.js +631 -0
  709. package/scripts/bongos/session-transcript-build.js +155 -0
  710. package/scripts/bongos/session-transcript-upload.js +29 -0
  711. package/scripts/bongos/session.js +404 -0
  712. package/scripts/bongos/setup.js +646 -0
  713. package/scripts/bongos/ship-check.js +89 -0
  714. package/scripts/bongos/ship-deploy-target.js +393 -0
  715. package/scripts/bongos/ship-escalation.js +167 -0
  716. package/scripts/bongos/ship-finish.js +420 -0
  717. package/scripts/bongos/ship-flow.js +1217 -0
  718. package/scripts/bongos/ship-git.js +100 -0
  719. package/scripts/bongos/ship-grade-steps.js +547 -0
  720. package/scripts/bongos/ship-grade.js +426 -0
  721. package/scripts/bongos/ship-honesty.js +314 -0
  722. package/scripts/bongos/ship-io.js +139 -0
  723. package/scripts/bongos/ship-land.js +1130 -0
  724. package/scripts/bongos/ship-memory-sync.js +249 -0
  725. package/scripts/bongos/ship-merge-gate.js +225 -0
  726. package/scripts/bongos/ship-merge.js +466 -0
  727. package/scripts/bongos/ship-mode-ask.js +138 -0
  728. package/scripts/bongos/ship-mode-manual.js +54 -0
  729. package/scripts/bongos/ship-preflight-steps.js +399 -0
  730. package/scripts/bongos/ship-regen.js +292 -0
  731. package/scripts/bongos/ship-resume.js +201 -0
  732. package/scripts/bongos/ship-session-upload.js +313 -0
  733. package/scripts/bongos/ship-state.js +110 -0
  734. package/scripts/bongos/ship-surprise.js +84 -0
  735. package/scripts/bongos/ship-visual.js +75 -0
  736. package/scripts/bongos/ship.js +382 -0
  737. package/scripts/bongos/shipping-mode.js +71 -0
  738. package/scripts/bongos/skill-lint.js +250 -0
  739. package/scripts/bongos/skill-preflight.js +361 -0
  740. package/scripts/bongos/smoke-batch-claim.sh +238 -0
  741. package/scripts/bongos/smoke-batch-release.sh +316 -0
  742. package/scripts/bongos/smoke-claim-consistency.sh +222 -0
  743. package/scripts/bongos/smoke-dependencies.sh +143 -0
  744. package/scripts/bongos/smoke-gds.js +256 -0
  745. package/scripts/bongos/smoke-write.sh +275 -0
  746. package/scripts/bongos/source-text-test-scan.js +106 -0
  747. package/scripts/bongos/spark.js +294 -0
  748. package/scripts/bongos/stale-worktrees.js +212 -0
  749. package/scripts/bongos/start-goals.js +102 -0
  750. package/scripts/bongos/start.js +1090 -0
  751. package/scripts/bongos/status.js +545 -0
  752. package/scripts/bongos/strand-watch.js +315 -0
  753. package/scripts/bongos/stranded-rows.js +200 -0
  754. package/scripts/bongos/stray-unit-sweep.sh +71 -0
  755. package/scripts/bongos/surface.js +101 -0
  756. package/scripts/bongos/task.js +273 -0
  757. package/scripts/bongos/test-path-guard.js +99 -0
  758. package/scripts/bongos/triage.js +742 -0
  759. package/scripts/bongos/turn-end-sound.js +74 -0
  760. package/scripts/bongos/tweak-halt.js +70 -0
  761. package/scripts/bongos/tweak-renders.js +200 -0
  762. package/scripts/bongos/uat.js +120 -0
  763. package/scripts/bongos/ui-kit-transform.js +43 -0
  764. package/scripts/bongos/update-channel.js +512 -0
  765. package/scripts/bongos/update-sweep.js +829 -0
  766. package/scripts/bongos/upgrade-artist-gate.js +46 -0
  767. package/scripts/bongos/upgrade-commit-identity.js +32 -0
  768. package/scripts/bongos/upgrade-migrate.js +224 -0
  769. package/scripts/bongos/upgrade-operator.js +73 -0
  770. package/scripts/bongos/upgrade-outcome.js +174 -0
  771. package/scripts/bongos/upgrade.js +1499 -0
  772. package/scripts/bongos/validate-design.js +12 -0
  773. package/scripts/bongos/version-close.js +621 -0
  774. package/scripts/bongos/version-literal-guard.js +140 -0
  775. package/scripts/bongos/version-select.js +79 -0
  776. package/scripts/bongos/visual-review-nudge.js +39 -0
  777. package/scripts/bongos/wedge-remedy.js +174 -0
  778. package/scripts/bongos/worktree-claim-guard.js +130 -0
  779. package/scripts/bongos/worktree.js +449 -0
  780. package/scripts/discord/post.js +1 -1
  781. package/scripts/gds/README.md +9 -0
  782. package/scripts/gds/abandon.js +6 -136
  783. package/scripts/gds/accessibility-replay.js +5 -194
  784. package/scripts/gds/address-park-sweep.sh +7 -90
  785. package/scripts/gds/adopt-detect.js +5 -121
  786. package/scripts/gds/adopt-import.js +5 -109
  787. package/scripts/gds/adopt-preflight.js +5 -185
  788. package/scripts/gds/adr-namespace.js +5 -271
  789. package/scripts/gds/agent-invoke.js +5 -687
  790. package/scripts/gds/agents-sync.js +5 -500
  791. package/scripts/gds/api-alias-caller-scan.js +5 -255
  792. package/scripts/gds/api-artifacts.js +6 -0
  793. package/scripts/gds/api.js +6 -85
  794. package/scripts/gds/api.ps1 +8 -26
  795. package/scripts/gds/architect-audit.js +6 -293
  796. package/scripts/gds/artifact-audit.js +5 -324
  797. package/scripts/gds/artifact-format.js +6 -203
  798. package/scripts/gds/artifact-staleness.js +5 -265
  799. package/scripts/gds/ask.js +5 -229
  800. package/scripts/gds/audit-authorship.sh +7 -328
  801. package/scripts/gds/audit-deps.js +6 -188
  802. package/scripts/gds/audit-gate.js +5 -119
  803. package/scripts/gds/audit-rank-roles.js +6 -171
  804. package/scripts/gds/audit-replay.sh +7 -204
  805. package/scripts/gds/autobongos-digest.js +5 -121
  806. package/scripts/gds/autobongos-grade-cap.js +5 -186
  807. package/scripts/gds/autobongos-loop.js +5 -208
  808. package/scripts/gds/autobongos-run.js +5 -1359
  809. package/scripts/gds/autobongos-service.cmd +8 -49
  810. package/scripts/gds/autobongos-service.ps1 +8 -270
  811. package/scripts/gds/autobongos-tweak-lane.js +5 -261
  812. package/scripts/gds/autobongos-verify.js +5 -616
  813. package/scripts/gds/autonomy-gate.js +5 -133
  814. package/scripts/gds/autonomy-gauge-statusline.js +5 -122
  815. package/scripts/gds/autonomy-gauge.js +5 -82
  816. package/scripts/gds/backfill-cost.js +6 -175
  817. package/scripts/gds/backfill-disciplines.js +5 -142
  818. package/scripts/gds/backfill-session-earnings.js +5 -121
  819. package/scripts/gds/backfill-session-types.js +5 -85
  820. package/scripts/gds/backfill-sessions.js +5 -409
  821. package/scripts/gds/backfill-subagent-rewards.js +5 -483
  822. package/scripts/gds/backfill-task-rewards.js +5 -411
  823. package/scripts/gds/backlog-review.js +6 -398
  824. package/scripts/gds/backup-dropin-sweep.sh +7 -94
  825. package/scripts/gds/backup.js +6 -115
  826. package/scripts/gds/baseline-staleness.js +5 -215
  827. package/scripts/gds/bfg-principal-token.js +6 -64
  828. package/scripts/gds/bug-attachment-audit.js +5 -147
  829. package/scripts/gds/bug-attachment-recover.js +5 -196
  830. package/scripts/gds/bug-triage.js +6 -291
  831. package/scripts/gds/build-bongos.js +5 -92
  832. package/scripts/gds/bump-version.js +5 -219
  833. package/scripts/gds/capture.js +6 -480
  834. package/scripts/gds/category-advisory-guard.js +5 -205
  835. package/scripts/gds/ci-grade.js +6 -81
  836. package/scripts/gds/claim-recall.js +6 -111
  837. package/scripts/gds/claim.js +6 -788
  838. package/scripts/gds/classify-discipline.js +6 -152
  839. package/scripts/gds/claude-materialize.js +5 -1198
  840. package/scripts/gds/claude-md-template.js +5 -140
  841. package/scripts/gds/cleanup-orphans.js +6 -73
  842. package/scripts/gds/cli-lib.js +6 -1204
  843. package/scripts/gds/client-baseurl-guard.js +5 -114
  844. package/scripts/gds/codemod-rename-src-gds.js +6 -144
  845. package/scripts/gds/conductor-dispatch.js +6 -534
  846. package/scripts/gds/context-pack.js +6 -325
  847. package/scripts/gds/control-manifest.js +5 -197
  848. package/scripts/gds/copy-apply.js +5 -756
  849. package/scripts/gds/copy-inventory.js +5 -569
  850. package/scripts/gds/core-origin-follow.js +5 -184
  851. package/scripts/gds/core-pin-sync.js +5 -258
  852. package/scripts/gds/coreB-carve-proof.sh +7 -724
  853. package/scripts/gds/cost-report.js +6 -62
  854. package/scripts/gds/cost.js +6 -46
  855. package/scripts/gds/credential-presence.js +5 -294
  856. package/scripts/gds/cutover-codemod.js +5 -239
  857. package/scripts/gds/db-backup-nightly.sh +7 -231
  858. package/scripts/gds/db-backup-offsite.sh +7 -266
  859. package/scripts/gds/demote.js +6 -44
  860. package/scripts/gds/deploy-divergence.js +6 -445
  861. package/scripts/gds/deploy-main-audit-gate.sh +7 -96
  862. package/scripts/gds/design-sync.js +5 -10
  863. package/scripts/gds/dev-box-guard.js +5 -164
  864. package/scripts/gds/dev-lib.js +5 -721
  865. package/scripts/gds/dev.js +5 -643
  866. package/scripts/gds/device-flow-help.js +5 -70
  867. package/scripts/gds/diagnose-achievements.js +5 -116
  868. package/scripts/gds/diagram-facts.js +6 -355
  869. package/scripts/gds/discord-channels.js +5 -75
  870. package/scripts/gds/do-api.js +6 -109
  871. package/scripts/gds/doc-cli-guard.js +5 -266
  872. package/scripts/gds/doc-comment-coverage.js +5 -137
  873. package/scripts/gds/doc-control-claims.js +5 -166
  874. package/scripts/gds/docs-entropy.js +5 -443
  875. package/scripts/gds/doctor.js +6 -492
  876. package/scripts/gds/enroll-control-plane.js +5 -99
  877. package/scripts/gds/exec-path-guard.js +5 -562
  878. package/scripts/gds/feedback-send.js +5 -247
  879. package/scripts/gds/fetch-art-key.js +5 -104
  880. package/scripts/gds/fetch-sound-prefs.js +5 -124
  881. package/scripts/gds/figma-design-sync.js +5 -9
  882. package/scripts/gds/find-untested.js +5 -369
  883. package/scripts/gds/fitness-checks-error-envelope.js +5 -133
  884. package/scripts/gds/fitness-checks-identity.js +5 -342
  885. package/scripts/gds/fitness-checks-packaging.js +5 -131
  886. package/scripts/gds/fitness-checks-route-pins.js +5 -75
  887. package/scripts/gds/fitness-checks-vocabulary.js +5 -391
  888. package/scripts/gds/fitness-checks-write-validation.js +5 -156
  889. package/scripts/gds/fitness-lib.js +5 -126
  890. package/scripts/gds/fitness-migration-contraction.js +5 -49
  891. package/scripts/gds/fitness-ratchets.js +5 -563
  892. package/scripts/gds/fitness.js +5 -1493
  893. package/scripts/gds/gate-review.js +5 -328
  894. package/scripts/gds/gating.js +5 -346
  895. package/scripts/gds/gds-literal-scan.js +5 -194
  896. package/scripts/gds/gen-api-client.js +5 -555
  897. package/scripts/gds/gen-api-docs.js +5 -1100
  898. package/scripts/gds/gen-atlas.js +5 -364
  899. package/scripts/gds/gen-diagrams.js +6 -607
  900. package/scripts/gds/gen-file-map.js +5 -335
  901. package/scripts/gds/gen-freshness.js +5 -31
  902. package/scripts/gds/gen-interaction-posture.js +5 -120
  903. package/scripts/gds/gen-repo-map.js +5 -538
  904. package/scripts/gds/gen-role-responsibilities.js +5 -133
  905. package/scripts/gds/gen-session-index.js +5 -241
  906. package/scripts/gds/gen-sounds.js +5 -174
  907. package/scripts/gds/git-merge-regen.js +6 -291
  908. package/scripts/gds/go-live.js +5 -1069
  909. package/scripts/gds/goal-names.js +5 -100
  910. package/scripts/gds/goal.js +6 -271
  911. package/scripts/gds/government-walk.js +5 -1475
  912. package/scripts/gds/grade-correlation-audit.js +5 -308
  913. package/scripts/gds/grade-replay.js +5 -235
  914. package/scripts/gds/grade-smoke.js +6 -204
  915. package/scripts/gds/home-isolation-guard.js +5 -125
  916. package/scripts/gds/hooks/pre-push.js +5 -209
  917. package/scripts/gds/howto.js +5 -143
  918. package/scripts/gds/http-api-client.js +6 -81
  919. package/scripts/gds/identity-strip.js +5 -74
  920. package/scripts/gds/init.js +5 -1157
  921. package/scripts/gds/install-git-hooks.js +5 -198
  922. package/scripts/gds/instance-apex.js +5 -37
  923. package/scripts/gds/intent-retry.js +5 -51
  924. package/scripts/gds/key-card.js +6 -221
  925. package/scripts/gds/key-status.js +5 -124
  926. package/scripts/gds/land-watch.js +5 -199
  927. package/scripts/gds/leak-scan-allowlist.js +5 -291
  928. package/scripts/gds/learning-capture.js +6 -144
  929. package/scripts/gds/lib.sh +7 -73
  930. package/scripts/gds/linkify-refs.js +6 -654
  931. package/scripts/gds/lint-infra-exec.js +5 -68
  932. package/scripts/gds/lint-migrations.js +5 -100
  933. package/scripts/gds/lint-workflow-exec.js +5 -61
  934. package/scripts/gds/local-preview-lib.js +6 -128
  935. package/scripts/gds/local-preview.js +6 -81
  936. package/scripts/gds/login.js +5 -358
  937. package/scripts/gds/main-audit.js +5 -623
  938. package/scripts/gds/me.js +6 -96
  939. package/scripts/gds/memory-map.js +5 -174
  940. package/scripts/gds/merge-lock.js +6 -70
  941. package/scripts/gds/migration-namespace.js +5 -311
  942. package/scripts/gds/mirror-lineage-check.js +5 -341
  943. package/scripts/gds/mirror-redact.js +5 -348
  944. package/scripts/gds/module-artifact.js +6 -289
  945. package/scripts/gds/module-assess-docs.js +6 -237
  946. package/scripts/gds/module-assess-installs.js +6 -227
  947. package/scripts/gds/module-assess-score.js +6 -110
  948. package/scripts/gds/module-assess-security.js +6 -251
  949. package/scripts/gds/module-assess-tests.js +6 -277
  950. package/scripts/gds/module-assess-version.js +6 -91
  951. package/scripts/gds/module-error-report.js +5 -146
  952. package/scripts/gds/module-test-runner.js +6 -413
  953. package/scripts/gds/module.js +5 -1374
  954. package/scripts/gds/move-escalation.js +5 -140
  955. package/scripts/gds/newcomer-floor.js +6 -420
  956. package/scripts/gds/newcomer-floor.test.js +6 -234
  957. package/scripts/gds/next-number.js +6 -162
  958. package/scripts/gds/oauth-secret.js +5 -200
  959. package/scripts/gds/onboard.js +5 -265
  960. package/scripts/gds/onboarding-analyze.js +6 -225
  961. package/scripts/gds/onboarding-config.js +6 -179
  962. package/scripts/gds/onboarding-ship.js +6 -91
  963. package/scripts/gds/pack-env-prefix.js +5 -72
  964. package/scripts/gds/package-core.js +5 -1102
  965. package/scripts/gds/page-inventory.js +5 -346
  966. package/scripts/gds/page-reader.js +5 -720
  967. package/scripts/gds/paste-token.js +6 -211
  968. package/scripts/gds/plain-cards.js +5 -350
  969. package/scripts/gds/portability-smoke-art.sh +7 -84
  970. package/scripts/gds/portability-smoke-memory.sh +7 -175
  971. package/scripts/gds/preflight.js +5 -212
  972. package/scripts/gds/probe-cache.js +5 -244
  973. package/scripts/gds/prove-strict-versioning.js +6 -283
  974. package/scripts/gds/provision-auto-upgrade.js +5 -161
  975. package/scripts/gds/provision-byo-host.js +5 -67
  976. package/scripts/gds/provision-capacity.js +5 -78
  977. package/scripts/gds/provision-caps.js +5 -62
  978. package/scripts/gds/provision-config.js +5 -232
  979. package/scripts/gds/provision-core-upgrade.js +5 -1165
  980. package/scripts/gds/provision-disconnect.js +5 -87
  981. package/scripts/gds/provision-github-setup.js +5 -167
  982. package/scripts/gds/provision-net.js +5 -356
  983. package/scripts/gds/provision-pin-key.js +5 -175
  984. package/scripts/gds/provision-pin-replay.js +5 -125
  985. package/scripts/gds/provision-process.js +5 -169
  986. package/scripts/gds/provision-render.js +5 -200
  987. package/scripts/gds/provision-repo-auth.js +5 -163
  988. package/scripts/gds/provision-repo-private.js +5 -97
  989. package/scripts/gds/provision-repo.js +5 -1336
  990. package/scripts/gds/provision-teardown.js +5 -105
  991. package/scripts/gds/provision-units.js +5 -468
  992. package/scripts/gds/provision.js +5 -1477
  993. package/scripts/gds/publish-credential-check.js +6 -65
  994. package/scripts/gds/publish-integration-gate.js +5 -140
  995. package/scripts/gds/publish-manifest.js +5 -437
  996. package/scripts/gds/publish-watch.js +5 -266
  997. package/scripts/gds/pulse.js +6 -335
  998. package/scripts/gds/push-main.js +5 -54
  999. package/scripts/gds/push-path-brief.js +5 -98
  1000. package/scripts/gds/read-session-export.js +5 -357
  1001. package/scripts/gds/reauth.js +6 -154
  1002. package/scripts/gds/recall.js +6 -182
  1003. package/scripts/gds/redteam-patrol.js +5 -435
  1004. package/scripts/gds/regen-instance-docs.js +5 -45
  1005. package/scripts/gds/release-core.js +5 -78
  1006. package/scripts/gds/release-notes.js +5 -270
  1007. package/scripts/gds/release.js +6 -138
  1008. package/scripts/gds/rename-goals.js +5 -107
  1009. package/scripts/gds/rename-history-check.js +5 -367
  1010. package/scripts/gds/rename-ledger-prose.js +5 -480
  1011. package/scripts/gds/render-api.js +5 -196
  1012. package/scripts/gds/render-check.js +5 -204
  1013. package/scripts/gds/render-ideas.js +6 -225
  1014. package/scripts/gds/render-payload.js +5 -84
  1015. package/scripts/gds/repo-metrics.js +6 -362
  1016. package/scripts/gds/review-sheet.js +5 -106
  1017. package/scripts/gds/role-pack-guard.js +5 -141
  1018. package/scripts/gds/route-shadow-guard.js +5 -173
  1019. package/scripts/gds/routine-schedule.js +5 -281
  1020. package/scripts/gds/run-routine.js +5 -255
  1021. package/scripts/gds/run-unit-tests.js +6 -659
  1022. package/scripts/gds/runner-drift.js +5 -149
  1023. package/scripts/gds/sandbox-stage.js +6 -458
  1024. package/scripts/gds/scope-map-guard.js +5 -299
  1025. package/scripts/gds/scope-roster-guard.js +5 -117
  1026. package/scripts/gds/seam-wiring-guard.js +5 -274
  1027. package/scripts/gds/search-ingest-db.js +6 -287
  1028. package/scripts/gds/search-ingest-md.js +6 -268
  1029. package/scripts/gds/seed-adopt-tasks.js +6 -74
  1030. package/scripts/gds/seed-api-contract-goal-tasks.js +6 -140
  1031. package/scripts/gds/seed-board-room-tasks.js +6 -196
  1032. package/scripts/gds/seed-bongos-app-tasks.js +6 -293
  1033. package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +6 -80
  1034. package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +6 -99
  1035. package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +6 -306
  1036. package/scripts/gds/seed-bongos-module-system-tasks.js +6 -345
  1037. package/scripts/gds/seed-bongos-tasks.js +6 -213
  1038. package/scripts/gds/seed-bongos-v1-idea-routing-tasks.js +6 -152
  1039. package/scripts/gds/seed-bongos-v1-seo-tasks.js +6 -104
  1040. package/scripts/gds/seed-bv1-community-tasks.js +6 -131
  1041. package/scripts/gds/seed-bv1-privacy-invites-tasks.js +6 -133
  1042. package/scripts/gds/seed-bv1-profile-page-tasks.js +6 -161
  1043. package/scripts/gds/seed-bv1-profiles-tasks.js +6 -135
  1044. package/scripts/gds/seed-bv1-r01-delta-tasks.js +6 -154
  1045. package/scripts/gds/seed-bv2-ideator-surface-tasks.js +6 -390
  1046. package/scripts/gds/seed-bv2-tweak-mode-tasks.js +6 -277
  1047. package/scripts/gds/seed-gds-v4-tasks.js +6 -623
  1048. package/scripts/gds/seed-governance-tasks.js +6 -339
  1049. package/scripts/gds/seed-medusa-tasks.js +6 -439
  1050. package/scripts/gds/seed-module-upstreaming.js +5 -217
  1051. package/scripts/gds/seed-productization-tasks.js +6 -234
  1052. package/scripts/gds/seed-project-admin-console-tasks.js +6 -88
  1053. package/scripts/gds/seed-provisioning-tasks.js +6 -82
  1054. package/scripts/gds/seed-staging.sh +7 -116
  1055. package/scripts/gds/seed-strict-versioning-tasks.js +6 -274
  1056. package/scripts/gds/seed-v2-world-tasks.js +6 -282
  1057. package/scripts/gds/seed-verify-criteria.js +6 -124
  1058. package/scripts/gds/sequence.js +6 -596
  1059. package/scripts/gds/session-cost.js +6 -429
  1060. package/scripts/gds/session-digest-build.js +6 -293
  1061. package/scripts/gds/session-digest.js +6 -199
  1062. package/scripts/gds/session-eval.js +6 -630
  1063. package/scripts/gds/session-transcript-build.js +6 -155
  1064. package/scripts/gds/session-transcript-upload.js +6 -29
  1065. package/scripts/gds/session.js +6 -403
  1066. package/scripts/gds/setup.js +6 -645
  1067. package/scripts/gds/ship-check.js +5 -85
  1068. package/scripts/gds/ship-deploy-target.js +5 -392
  1069. package/scripts/gds/ship-escalation.js +5 -166
  1070. package/scripts/gds/ship-finish.js +5 -419
  1071. package/scripts/gds/ship-flow.js +5 -1216
  1072. package/scripts/gds/ship-git.js +5 -99
  1073. package/scripts/gds/ship-grade-steps.js +5 -546
  1074. package/scripts/gds/ship-grade.js +5 -425
  1075. package/scripts/gds/ship-honesty.js +5 -312
  1076. package/scripts/gds/ship-io.js +5 -138
  1077. package/scripts/gds/ship-land.js +5 -1129
  1078. package/scripts/gds/ship-memory-sync.js +5 -248
  1079. package/scripts/gds/ship-merge-gate.js +5 -224
  1080. package/scripts/gds/ship-merge.js +5 -465
  1081. package/scripts/gds/ship-mode-ask.js +5 -137
  1082. package/scripts/gds/ship-mode-manual.js +5 -53
  1083. package/scripts/gds/ship-preflight-steps.js +5 -398
  1084. package/scripts/gds/ship-regen.js +5 -313
  1085. package/scripts/gds/ship-resume.js +5 -200
  1086. package/scripts/gds/ship-session-upload.js +5 -312
  1087. package/scripts/gds/ship-state.js +5 -109
  1088. package/scripts/gds/ship-surprise.js +5 -83
  1089. package/scripts/gds/ship-visual.js +5 -74
  1090. package/scripts/gds/ship.js +5 -380
  1091. package/scripts/gds/shipping-mode.js +5 -69
  1092. package/scripts/gds/skill-lint.js +5 -248
  1093. package/scripts/gds/skill-preflight.js +5 -359
  1094. package/scripts/gds/smoke-batch-claim.sh +7 -237
  1095. package/scripts/gds/smoke-batch-release.sh +7 -315
  1096. package/scripts/gds/smoke-claim-consistency.sh +7 -221
  1097. package/scripts/gds/smoke-dependencies.sh +7 -142
  1098. package/scripts/gds/smoke-gds.js +6 -255
  1099. package/scripts/gds/smoke-write.sh +7 -274
  1100. package/scripts/gds/source-text-test-scan.js +7 -0
  1101. package/scripts/gds/spark.js +6 -293
  1102. package/scripts/gds/stale-worktrees.js +5 -210
  1103. package/scripts/gds/start-goals.js +5 -101
  1104. package/scripts/gds/start.js +6 -1089
  1105. package/scripts/gds/status.js +6 -544
  1106. package/scripts/gds/strand-watch.js +5 -313
  1107. package/scripts/gds/stranded-rows.js +5 -198
  1108. package/scripts/gds/stray-unit-sweep.sh +7 -70
  1109. package/scripts/gds/surface.js +5 -100
  1110. package/scripts/gds/task.js +6 -272
  1111. package/scripts/gds/test-path-guard.js +5 -97
  1112. package/scripts/gds/triage.js +6 -741
  1113. package/scripts/gds/turn-end-sound.js +5 -72
  1114. package/scripts/gds/tweak-halt.js +5 -68
  1115. package/scripts/gds/tweak-renders.js +5 -198
  1116. package/scripts/gds/uat.js +6 -119
  1117. package/scripts/gds/ui-kit-transform.js +5 -41
  1118. package/scripts/gds/update-channel.js +5 -510
  1119. package/scripts/gds/update-sweep.js +5 -827
  1120. package/scripts/gds/upgrade-artist-gate.js +5 -45
  1121. package/scripts/gds/upgrade-commit-identity.js +5 -31
  1122. package/scripts/gds/upgrade-migrate.js +5 -223
  1123. package/scripts/gds/upgrade-operator.js +6 -73
  1124. package/scripts/gds/upgrade-outcome.js +5 -173
  1125. package/scripts/gds/upgrade.js +5 -1497
  1126. package/scripts/gds/validate-design.js +5 -10
  1127. package/scripts/gds/version-close.js +6 -620
  1128. package/scripts/gds/version-literal-guard.js +5 -138
  1129. package/scripts/gds/version-select.js +5 -77
  1130. package/scripts/gds/visual-review-nudge.js +5 -38
  1131. package/scripts/gds/wedge-remedy.js +5 -173
  1132. package/scripts/gds/worktree-claim-guard.js +5 -129
  1133. package/scripts/gds/worktree.js +6 -448
  1134. package/scripts/hall-preview/refresh-fixtures.js +1 -1
  1135. package/scripts/hall-preview/server.js +2 -2
  1136. package/scripts/migrate.sh +3 -3
  1137. package/scripts/public-mirror-export.js +3 -3
  1138. package/scripts/setup-builder.js +7 -7
  1139. package/src/bongos/api-errors.js +1 -0
  1140. package/src/bongos/api-prefix.js +1 -1
  1141. package/src/bongos/auth-github.js +19 -3
  1142. package/src/bongos/auth-idp.js +7 -0
  1143. package/src/bongos/claim-attribution.js +2 -2
  1144. package/src/bongos/core-moves.js +1 -1
  1145. package/src/bongos/core-update.js +3 -3
  1146. package/src/bongos/craft-mode.js +2 -2
  1147. package/src/bongos/db-kernel.js +15 -3
  1148. package/src/bongos/db.js +30 -4
  1149. package/src/bongos/github-repos.js +1 -1
  1150. package/src/bongos/llm-cache.js +1 -1
  1151. package/src/bongos/llm-pricing.js +1 -1
  1152. package/src/bongos/metrics.js +1 -1
  1153. package/src/bongos/middleware/rate-limit.js +2 -2
  1154. package/src/bongos/module-listing.js +93 -1
  1155. package/src/bongos/module-scope-map.js +90 -85
  1156. package/src/bongos/module-store.js +1 -1
  1157. package/src/bongos/module-submissions.js +1 -1
  1158. package/src/bongos/optimizer.js +1 -1
  1159. package/src/bongos/package-registry.js +1 -1
  1160. package/src/bongos/path-match.js +1 -1
  1161. package/src/bongos/permission-path-check.js +3 -3
  1162. package/src/bongos/platform-visibility-gate.js +3 -5
  1163. package/src/bongos/prelaunch-gate.js +7 -6
  1164. package/src/bongos/route-rank-check.js +2 -2
  1165. package/src/bongos/routes/auth.js +15 -2
  1166. package/src/bongos/routes/backup.js +1 -1
  1167. package/src/bongos/routes/builders.js +29 -1
  1168. package/src/bongos/routes/me.js +49 -18
  1169. package/src/bongos/routes/modules.js +29 -9
  1170. package/src/bongos/routes/public.js +3 -72
  1171. package/src/bongos/schema-lag.js +1 -1
  1172. package/src/bongos/sensitive-surfaces.js +1 -1
  1173. package/src/bongos/serve-internal.js +65 -16
  1174. package/src/bongos/software-update.js +2 -2
  1175. package/src/bongos/upgrade-hold.js +3 -3
  1176. package/src/hall-look.js +6 -5
  1177. package/src/module-api.js +3 -3
  1178. package/src/module-loader/catalog.js +1 -1
  1179. package/src/module-loader/error-tally.js +3 -3
  1180. package/src/module-loader/manifest-schema.js +3 -3
  1181. package/src/platform-server.js +10 -0
  1182. package/src/role-responsibilities.js +1 -1
  1183. package/src/stale-timer.js +2 -2
  1184. package/tests/accessibility_replay.mjs +1 -1
  1185. package/tests/activity_rollup_order_db.mjs +1 -1
  1186. package/tests/activity_snapshots_db.mjs +1 -1
  1187. package/tests/adopt_detect.mjs +1 -1
  1188. package/tests/adopt_import.mjs +1 -1
  1189. package/tests/adopt_preflight.mjs +1 -1
  1190. package/tests/adr_0089_scope.mjs +10 -7
  1191. package/tests/adr_0162_citation.mjs +1 -1
  1192. package/tests/adr_0162_gate_citation.mjs +1 -1
  1193. package/tests/agent_invoke.mjs +1 -1
  1194. package/tests/agent_invoke_live.mjs +3 -3
  1195. package/tests/agents_cron_trigger.mjs +2 -2
  1196. package/tests/agents_roster.mjs +1 -1
  1197. package/tests/agents_sync.mjs +4 -4
  1198. package/tests/allow_rule_guard.mjs +20 -20
  1199. package/tests/api_alias_caller_ratchet.mjs +9 -10
  1200. package/tests/api_alias_exceptions.mjs +7 -6
  1201. package/tests/api_artifacts.mjs +214 -0
  1202. package/tests/api_cli_flags.mjs +3 -3
  1203. package/tests/api_client.mjs +56 -54
  1204. package/tests/api_docs.mjs +30 -9
  1205. package/tests/api_error_envelope_buildcheck.mjs +1 -1
  1206. package/tests/api_msys_path_repair.mjs +2 -2
  1207. package/tests/architect.mjs +1 -1
  1208. package/tests/art_stage.mjs +2 -2
  1209. package/tests/artifact_audit.mjs +2 -2
  1210. package/tests/artifact_staleness.mjs +9 -9
  1211. package/tests/atlas.mjs +1 -1
  1212. package/tests/audit_deps.mjs +1 -1
  1213. package/tests/audit_gate.mjs +1 -1
  1214. package/tests/auth_flow_slice1.mjs +3 -3
  1215. package/tests/auth_inactive_builder.mjs +43 -5
  1216. package/tests/auth_session_expiry_db.mjs +16 -4
  1217. package/tests/auth_session_ttl.mjs +23 -0
  1218. package/tests/authorship_audit.mjs +2 -2
  1219. package/tests/autobongos_cadence.mjs +1 -1
  1220. package/tests/autobongos_fence.mjs +1 -1
  1221. package/tests/autobongos_grade_cap.mjs +2 -2
  1222. package/tests/autobongos_heartbeat_head.mjs +1 -1
  1223. package/tests/autobongos_host_access.mjs +3 -3
  1224. package/tests/autobongos_loop.mjs +17 -17
  1225. package/tests/autobongos_service_ps1.mjs +3 -3
  1226. package/tests/autobongos_tweak_lane.mjs +2 -2
  1227. package/tests/autobongos_verify.mjs +2 -2
  1228. package/tests/autonomous_fit_protected_paths.mjs +3 -3
  1229. package/tests/autonomy_config_idle.mjs +1 -1
  1230. package/tests/autonomy_digest.mjs +1 -1
  1231. package/tests/autonomy_gate.mjs +1 -1
  1232. package/tests/autonomy_gauge.mjs +2 -2
  1233. package/tests/autonomy_runner_identity.mjs +1 -1
  1234. package/tests/backfill_sessions.mjs +8 -8
  1235. package/tests/backfill_subagent_rewards.mjs +1 -1
  1236. package/tests/backfill_task_rewards.mjs +2 -2
  1237. package/tests/backlog_review.mjs +3 -3
  1238. package/tests/baseline_staleness.mjs +2 -2
  1239. package/tests/bfg.mjs +11 -5
  1240. package/tests/bfg_write.mjs +20 -11
  1241. package/tests/blockers_hall.mjs +17 -5
  1242. package/tests/blockers_page_ui.mjs +56 -12
  1243. package/tests/bongos_dispatch.mjs +5 -5
  1244. package/tests/bongos_login.mjs +2 -2
  1245. package/tests/bug_attachment_audit.mjs +1 -1
  1246. package/tests/bug_attachment_recover.mjs +2 -2
  1247. package/tests/bug_triage.mjs +3 -3
  1248. package/tests/build_info.mjs +1 -1
  1249. package/tests/builder_tester_tag.mjs +184 -0
  1250. package/tests/bump_version.mjs +3 -3
  1251. package/tests/canonical_profile_url.mjs +1 -1
  1252. package/tests/capture_error_format.mjs +1 -1
  1253. package/tests/capture_goal_routing.mjs +2 -2
  1254. package/tests/category_advisory_guard.mjs +3 -3
  1255. package/tests/claim_error_surface.mjs +2 -2
  1256. package/tests/claim_from_task_and_home.mjs +1 -1
  1257. package/tests/claim_gate_open_blocker.mjs +7 -4
  1258. package/tests/claim_gate_rebase.mjs +12 -8
  1259. package/tests/claim_recall.mjs +4 -4
  1260. package/tests/claim_resolve_worktree_lock.mjs +1 -1
  1261. package/tests/claude_materialize.mjs +58 -58
  1262. package/tests/clawback_confirmed_backlog.mjs +8 -5
  1263. package/tests/cli_allowlist_permissions.mjs +11 -11
  1264. package/tests/cli_dispatcher.mjs +2 -2
  1265. package/tests/cli_err_code.mjs +6 -6
  1266. package/tests/cli_exit_no_abort.mjs +7 -7
  1267. package/tests/cli_lib_formatters.mjs +2 -2
  1268. package/tests/cli_sessions.mjs +5 -5
  1269. package/tests/cli_surface.mjs +15 -15
  1270. package/tests/cli_token_reissue.mjs +2 -2
  1271. package/tests/client_baseurl_guard.mjs +1 -1
  1272. package/tests/client_bundle_route.mjs +35 -4
  1273. package/tests/cloud_bongos_pack.mjs +1 -1
  1274. package/tests/community_leaderboard_db.mjs +1 -1
  1275. package/tests/conductor.mjs +18 -18
  1276. package/tests/conflict_resolve.mjs +14 -16
  1277. package/tests/consumer_layout_boot.mjs +12 -12
  1278. package/tests/context_pack.mjs +6 -6
  1279. package/tests/control_manifest.mjs +4 -4
  1280. package/tests/copy_desk_page_reads.mjs +1 -1
  1281. package/tests/copy_desk_page_release.mjs +192 -0
  1282. package/tests/copy_inventory.mjs +2 -2
  1283. package/tests/core_auto_upgrade.mjs +1 -1
  1284. package/tests/core_move_notice.mjs +2 -2
  1285. package/tests/core_origin_follow.mjs +7 -7
  1286. package/tests/core_pin_sync.mjs +3 -3
  1287. package/tests/core_upgrade_door.mjs +2 -2
  1288. package/tests/core_upgrade_owner_door.mjs +34 -1
  1289. package/tests/core_upgrade_runner.mjs +19 -19
  1290. package/tests/credential_presence.mjs +2 -2
  1291. package/tests/credit_grant.mjs +3 -3
  1292. package/tests/credit_log_single_writer.mjs +5 -5
  1293. package/tests/criterion_ref_status.mjs +2 -2
  1294. package/tests/cross_discipline_priority_direction.mjs +3 -3
  1295. package/tests/cutover_codemod.mjs +2 -4
  1296. package/tests/db_backup_nightly.mjs +1 -1
  1297. package/tests/db_backup_offsite.mjs +1 -1
  1298. package/tests/db_ownership_handover.mjs +2 -2
  1299. package/tests/dead_deps.mjs +3 -3
  1300. package/tests/dependency_advisory.mjs +1 -1
  1301. package/tests/deploy_audit_gate.mjs +4 -4
  1302. package/tests/deploy_divergence_line.mjs +36 -16
  1303. package/tests/deploy_page_projects.mjs +35 -4
  1304. package/tests/dev_lib.mjs +2 -2
  1305. package/tests/dev_windows.mjs +2 -2
  1306. package/tests/diagram_facts_offset.mjs +1 -1
  1307. package/tests/diagram_repo_slug.mjs +1 -1
  1308. package/tests/diff_derived_signals.mjs +2 -2
  1309. package/tests/dns_preflight.mjs +2 -2
  1310. package/tests/do_api.mjs +3 -3
  1311. package/tests/doc_cli_guard.mjs +28 -28
  1312. package/tests/doc_comment_coverage.mjs +1 -1
  1313. package/tests/doc_control_claims.mjs +11 -11
  1314. package/tests/docs_entropy.mjs +2 -2
  1315. package/tests/docs_openapi_route.mjs +8 -6
  1316. package/tests/doctor_hooks.mjs +1 -1
  1317. package/tests/edit_hints.mjs +3 -2
  1318. package/tests/effective_visibility_predicate.mjs +3 -3
  1319. package/tests/env_knobs_neutral_prefix.mjs +13 -13
  1320. package/tests/exec_path_guard.mjs +4 -4
  1321. package/tests/executed_closure.mjs +18 -12
  1322. package/tests/file_map.mjs +1 -1
  1323. package/tests/find_untested.mjs +13 -13
  1324. package/tests/first_admin_bootstrap.mjs +1 -1
  1325. package/tests/fitness.mjs +16 -16
  1326. package/tests/fitness_gds_ratchet.mjs +2 -2
  1327. package/tests/fitness_installed_core.mjs +3 -3
  1328. package/tests/fitness_no_raw_nul.mjs +1 -1
  1329. package/tests/fitness_ratchets.mjs +3 -3
  1330. package/tests/fitness_scope_roster.mjs +2 -2
  1331. package/tests/fitness_two_root.mjs +8 -8
  1332. package/tests/fixtures/accessibility-replay.json +5 -5
  1333. package/tests/fixtures/grade-replay/recorded-panel-grades.json +1 -1
  1334. package/tests/fixtures/ideator-prefold/README.md +1 -1
  1335. package/tests/fixtures/page-ledger.mjs +22 -0
  1336. package/tests/fixtures/session-506-digest.json +5 -5
  1337. package/tests/gate_approvals.mjs +50 -0
  1338. package/tests/gate_review.mjs +8 -8
  1339. package/tests/gating_review_retired.mjs +2 -2
  1340. package/tests/gen_freshness.mjs +3 -3
  1341. package/tests/git_guard.mjs +3 -3
  1342. package/tests/git_merge_regen.mjs +41 -65
  1343. package/tests/github_one_trip.mjs +4 -4
  1344. package/tests/github_pick_up.mjs +2 -2
  1345. package/tests/github_push_merge_driver.mjs +1 -1
  1346. package/tests/go_live.mjs +3 -2
  1347. package/tests/goal_category.mjs +1 -1
  1348. package/tests/goal_cli.mjs +2 -2
  1349. package/tests/goal_create_spec_entry.mjs +1 -1
  1350. package/tests/goal_invite_consent.mjs +3 -1
  1351. package/tests/goal_move.mjs +1 -1
  1352. package/tests/goal_names.mjs +2 -2
  1353. package/tests/goal_working_area.mjs +1 -1
  1354. package/tests/government_agent_asks.mjs +7 -7
  1355. package/tests/government_board_room_copy.mjs +6 -1
  1356. package/tests/government_board_view.mjs +39 -0
  1357. package/tests/government_board_vote_ui.mjs +39 -1
  1358. package/tests/government_config.mjs +2 -2
  1359. package/tests/government_live_proof_shape.mjs +1 -1
  1360. package/tests/government_protected_surfaces.mjs +54 -53
  1361. package/tests/government_require_permission.mjs +9 -1
  1362. package/tests/government_vanilla_boot.mjs +1 -1
  1363. package/tests/government_vocabulary.mjs +2 -2
  1364. package/tests/government_walk.mjs +2 -2
  1365. package/tests/grade_body_limit.mjs +6 -4
  1366. package/tests/grade_correlation_audit.mjs +3 -3
  1367. package/tests/grade_diff_hash.mjs +1 -1
  1368. package/tests/grade_engine_replay.mjs +2 -2
  1369. package/tests/grade_server_authoritative.mjs +1 -1
  1370. package/tests/grade_unavailable_display.mjs +1 -1
  1371. package/tests/grader.mjs +8 -24
  1372. package/tests/grader_generated_provenance.mjs +14 -14
  1373. package/tests/grader_no_artifact.mjs +1 -1
  1374. package/tests/grader_prompt_fencing.mjs +1 -1
  1375. package/tests/grader_quality_window_overflow.mjs +1 -1
  1376. package/tests/grader_reader_lens.mjs +3 -3
  1377. package/tests/grader_regrade_delta.mjs +1 -1
  1378. package/tests/grader_unbacked_score.mjs +2 -2
  1379. package/tests/hall_acclaim_vote_control.mjs +113 -0
  1380. package/tests/hall_asset_cache.mjs +127 -0
  1381. package/tests/hall_audit.mjs +63 -0
  1382. package/tests/hall_board_world.mjs +20 -0
  1383. package/tests/hall_city_card_words.mjs +61 -0
  1384. package/tests/hall_client_request_shape.mjs +5 -1
  1385. package/tests/hall_home_ships_bounded.mjs +160 -0
  1386. package/tests/hall_landing_boot.mjs +15 -2
  1387. package/tests/hall_nav.mjs +33 -0
  1388. package/tests/hall_onboarding_bongos_setup.mjs +1 -1
  1389. package/tests/hall_oversight_world.mjs +44 -4
  1390. package/tests/hall_palette.mjs +12 -9
  1391. package/tests/hall_preview_security.mjs +1 -1
  1392. package/tests/hall_profile_world.mjs +17 -0
  1393. package/tests/hall_reading_world.mjs +16 -1
  1394. package/tests/hall_roster_world.mjs +2 -1
  1395. package/tests/hall_studio_world.mjs +4 -1
  1396. package/tests/hall_work_history_id.mjs +39 -0
  1397. package/tests/held_tweak_reporting.mjs +4 -4
  1398. package/tests/helpers.mjs +3 -3
  1399. package/tests/home_attention_cards.mjs +2 -0
  1400. package/tests/home_bounded_reads.mjs +1 -1
  1401. package/tests/home_isolation_guard.mjs +1 -1
  1402. package/tests/hook_core_resolve.mjs +7 -7
  1403. package/tests/hook_user_agents.mjs +1 -1
  1404. package/tests/hooks_drain.mjs +2 -2
  1405. package/tests/host_topology_skips.mjs +1 -1
  1406. package/tests/howto_guide.mjs +1 -1
  1407. package/tests/howto_moments.mjs +3 -3
  1408. package/tests/hub_account_page.mjs +1 -1
  1409. package/tests/idea_capture_grades.mjs +2 -2
  1410. package/tests/idea_develop_spark.mjs +1 -1
  1411. package/tests/idea_filed_via.mjs +2 -2
  1412. package/tests/idea_ideate_full.mjs +1 -1
  1413. package/tests/idea_spark_mark.mjs +1 -1
  1414. package/tests/idea_spark_queue.mjs +1 -1
  1415. package/tests/idea_triage.mjs +1 -1
  1416. package/tests/idea_triage_grade.mjs +3 -3
  1417. package/tests/idea_vote_permission.mjs +113 -0
  1418. package/tests/idea_votes_fold_db.mjs +123 -0
  1419. package/tests/infra_exec_paths.mjs +12 -12
  1420. package/tests/init.mjs +2 -2
  1421. package/tests/instance_db_migrated_on_code_move.mjs +5 -5
  1422. package/tests/instance_feedback.mjs +9 -9
  1423. package/tests/instance_mismatch_guard.mjs +2 -2
  1424. package/tests/integration_parked_ratchet.mjs +24 -2
  1425. package/tests/intent_retry.mjs +1 -1
  1426. package/tests/interaction_posture.mjs +3 -3
  1427. package/tests/invite_expiry_db.mjs +1 -1
  1428. package/tests/joinability_invite_only.mjs +1 -1
  1429. package/tests/karma_vote_lists.mjs +270 -0
  1430. package/tests/key-card.mjs +2 -2
  1431. package/tests/key-status.mjs +2 -2
  1432. package/tests/knip_ratchet_ambient.mjs +2 -2
  1433. package/tests/known_failing_ratchet.mjs +1 -1
  1434. package/tests/land_watch.mjs +2 -2
  1435. package/tests/landing_legal_pages.mjs +1 -1
  1436. package/tests/landing_page.mjs +2 -2
  1437. package/tests/leaderboard_roles.mjs +4 -1
  1438. package/tests/leak_scan_allowlist.mjs +8 -8
  1439. package/tests/leak_scan_fail_closed.mjs +1 -1
  1440. package/tests/lib_sh_resolution.mjs +9 -9
  1441. package/tests/lifecycle-card.mjs +7 -7
  1442. package/tests/linkify_check.mjs +2 -2
  1443. package/tests/linkify_refs.mjs +1 -1
  1444. package/tests/llm-cache-route.mjs +1 -1
  1445. package/tests/local_preview.mjs +2 -2
  1446. package/tests/main_audit.mjs +3 -3
  1447. package/tests/main_audit_attestation.mjs +1 -1
  1448. package/tests/main_audit_db_roster.mjs +2 -2
  1449. package/tests/main_worktree_busy_reason.mjs +3 -3
  1450. package/tests/markdown_line_endings.mjs +2 -2
  1451. package/tests/me_streak_leaderboard_queries.mjs +193 -0
  1452. package/tests/membership_kind_lockstep.mjs +1 -1
  1453. package/tests/memory_isolation.mjs +16 -8
  1454. package/tests/memory_map.mjs +2 -2
  1455. package/tests/memory_pull.mjs +33 -25
  1456. package/tests/merge_driver_register.mjs +3 -3
  1457. package/tests/migration_allocation.mjs +2 -2
  1458. package/tests/migration_namespace.mjs +1 -1
  1459. package/tests/migrations_forward_safe.mjs +1 -1
  1460. package/tests/mingle.mjs +1 -1
  1461. package/tests/mirror_lineage_fulltree.mjs +3 -3
  1462. package/tests/mirror_lineage_proof.mjs +2 -2
  1463. package/tests/mirror_redact.mjs +4 -4
  1464. package/tests/missing_dep_classify.mjs +3 -3
  1465. package/tests/module-scope-map.mjs +22 -22
  1466. package/tests/module_api_lazy.mjs +1 -1
  1467. package/tests/module_assess_docs.mjs +2 -2
  1468. package/tests/module_assess_installs.mjs +3 -3
  1469. package/tests/module_assess_price_parity.mjs +5 -5
  1470. package/tests/module_assess_score.mjs +5 -5
  1471. package/tests/module_assess_security.mjs +4 -4
  1472. package/tests/module_assess_tests.mjs +3 -3
  1473. package/tests/module_assessments_migration.mjs +1 -1
  1474. package/tests/module_catalog.mjs +2 -2
  1475. package/tests/module_cli.mjs +3 -3
  1476. package/tests/module_entitlements.mjs +1 -1
  1477. package/tests/module_error_counts.mjs +1 -1
  1478. package/tests/module_install.mjs +4 -4
  1479. package/tests/module_loader.mjs +3 -1
  1480. package/tests/module_owned_tests.mjs +1 -1
  1481. package/tests/module_release_channels.mjs +1 -1
  1482. package/tests/module_store_howto.mjs +3 -3
  1483. package/tests/module_store_listing.mjs +139 -2
  1484. package/tests/module_store_publish.mjs +4 -4
  1485. package/tests/module_store_publish_route.mjs +1 -1
  1486. package/tests/module_store_reassess.mjs +1 -1
  1487. package/tests/module_store_registry_migration.mjs +1 -1
  1488. package/tests/module_test_runner.mjs +3 -3
  1489. package/tests/module_update.mjs +4 -4
  1490. package/tests/module_upgrade_updates.mjs +1 -1
  1491. package/tests/move_escalation.mjs +2 -2
  1492. package/tests/nav_permission_atoms.mjs +3 -2
  1493. package/tests/needs_rebase.mjs +7 -3
  1494. package/tests/newcomer_floor.mjs +1 -1
  1495. package/tests/newcomer_generator.mjs +48 -0
  1496. package/tests/next_number.mjs +1 -1
  1497. package/tests/no_artifact_escalation.mjs +3 -3
  1498. package/tests/npm_release_where.mjs +1 -1
  1499. package/tests/oauth_secret.mjs +2 -2
  1500. package/tests/onboard.mjs +6 -6
  1501. package/tests/onboarding_gate.mjs +2 -2
  1502. package/tests/opt_in_skill_modules.mjs +3 -3
  1503. package/tests/own_scope_guard.mjs +1 -1
  1504. package/tests/own_session_revoke.mjs +4 -1
  1505. package/tests/package_core.mjs +10 -11
  1506. package/tests/page_inventory.mjs +5 -5
  1507. package/tests/page_reader.mjs +7 -7
  1508. package/tests/page_tweak_hold.mjs +6 -6
  1509. package/tests/parallel-load.mjs +1 -1
  1510. package/tests/paste_token_api_base.mjs +1 -1
  1511. package/tests/paste_token_session_store.mjs +2 -2
  1512. package/tests/pending_invite_purge_db.mjs +1 -1
  1513. package/tests/permission_path.mjs +1 -1
  1514. package/tests/pin_replay.mjs +1 -1
  1515. package/tests/pin_write_key.mjs +7 -7
  1516. package/tests/plain_cards.mjs +15 -15
  1517. package/tests/pr_assign.mjs +1 -1
  1518. package/tests/pre_push_hook.mjs +3 -3
  1519. package/tests/preflight_checks.mjs +2 -2
  1520. package/tests/prelaunch_gate.mjs +1 -1
  1521. package/tests/privacy_matrix.mjs +1 -1
  1522. package/tests/probe_cache.mjs +6 -6
  1523. package/tests/profile_connections_ui.mjs +4 -3
  1524. package/tests/profile_fields.mjs +11 -1
  1525. package/tests/profile_privacy_owner_view.mjs +1 -1
  1526. package/tests/profile_role_headline.mjs +10 -9
  1527. package/tests/project_admin_console_model.mjs +1 -1
  1528. package/tests/projects_hub.mjs +1 -1
  1529. package/tests/projects_hub_cloud_hop.mjs +1 -1
  1530. package/tests/promote_goal_id.mjs +6 -6
  1531. package/tests/promotion_sweep.mjs +1 -1
  1532. package/tests/provision.mjs +24 -24
  1533. package/tests/provision_account_identity.mjs +2 -2
  1534. package/tests/provision_byo_host.mjs +1 -1
  1535. package/tests/provision_caddy_wiring.mjs +2 -2
  1536. package/tests/provision_disconnect.mjs +2 -2
  1537. package/tests/provision_dns_first.mjs +1 -1
  1538. package/tests/provision_render.mjs +9 -9
  1539. package/tests/provision_repo_private.mjs +3 -3
  1540. package/tests/provision_resource_caps.mjs +5 -5
  1541. package/tests/provision_restart.mjs +3 -3
  1542. package/tests/provision_settings_apply.mjs +7 -7
  1543. package/tests/provision_teardown_address.mjs +2 -2
  1544. package/tests/provisioning_capacity.mjs +5 -5
  1545. package/tests/provisioning_credential_preflight.mjs +1 -1
  1546. package/tests/provisioning_disconnect.mjs +1 -1
  1547. package/tests/provisioning_error_note_scope.mjs +4 -4
  1548. package/tests/provisioning_paid_shape_gate.mjs +2 -2
  1549. package/tests/provisioning_planet_physics.mjs +16 -0
  1550. package/tests/provisioning_runner_drift_notice.mjs +2 -2
  1551. package/tests/provisioning_settings_env.mjs +1 -1
  1552. package/tests/pruned_lockfile_integrity.mjs +1 -1
  1553. package/tests/public_mirror_export.mjs +1 -1
  1554. package/tests/publish_integration_gate.mjs +2 -2
  1555. package/tests/publish_manifest.mjs +6 -6
  1556. package/tests/publish_watch.mjs +1 -1
  1557. package/tests/push_path_brief.mjs +2 -2
  1558. package/tests/raise.mjs +248 -0
  1559. package/tests/rank_roles_audit.mjs +1 -1
  1560. package/tests/rank_tier_single_source.mjs +2 -2
  1561. package/tests/rate_limit_public.mjs +1 -1
  1562. package/tests/read_default_meter.mjs +2 -2
  1563. package/tests/read_session_export.mjs +1 -1
  1564. package/tests/rebase_gate_set_aside.mjs +1 -1
  1565. package/tests/recall_cli.mjs +1 -1
  1566. package/tests/recall_corpus_health.mjs +3 -2
  1567. package/tests/recall_hints.mjs +1 -1
  1568. package/tests/recruiter_sliver_boundary.mjs +1 -1
  1569. package/tests/redteam_patrol.mjs +2 -2
  1570. package/tests/regrade_actual_minutes.mjs +2 -2
  1571. package/tests/regrade_eligibility.mjs +2 -2
  1572. package/tests/release_core.mjs +2 -2
  1573. package/tests/release_notes.mjs +4 -4
  1574. package/tests/rename_history_restraint.mjs +3 -3
  1575. package/tests/rename_ledger_prose.mjs +6 -6
  1576. package/tests/render_api.mjs +1 -1
  1577. package/tests/render_check.mjs +4 -4
  1578. package/tests/render_deploy.mjs +1 -1
  1579. package/tests/repo_info_binding.mjs +1 -1
  1580. package/tests/repo_map.mjs +5 -5
  1581. package/tests/reward_mode_cost_plus_only.mjs +7 -3
  1582. package/tests/reward_mode_surfacing.mjs +1 -1
  1583. package/tests/role_responsibilities.mjs +1 -1
  1584. package/tests/rot_inbox.mjs +10 -7
  1585. package/tests/route_permission_check.mjs +1 -1
  1586. package/tests/route_shadow_guard.mjs +1 -1
  1587. package/tests/route_validation.mjs +1 -1
  1588. package/tests/routine_capture_kind.mjs +6 -6
  1589. package/tests/routine_schedule.mjs +9 -9
  1590. package/tests/run_routine.mjs +5 -5
  1591. package/tests/run_routine_two_root.mjs +5 -5
  1592. package/tests/runner_drift.mjs +17 -17
  1593. package/tests/sandbox_stage.mjs +5 -5
  1594. package/tests/scope_map_guard.mjs +7 -7
  1595. package/tests/scouting_owner_access_and_reach.mjs +1 -1
  1596. package/tests/scripts_gds_forwarders.mjs +224 -0
  1597. package/tests/scrubber_corpus.mjs +2 -2
  1598. package/tests/seam_wiring_guard.mjs +1 -1
  1599. package/tests/search_ingest.mjs +2 -2
  1600. package/tests/search_ingest_db.mjs +1 -1
  1601. package/tests/search_ingest_nul.mjs +6 -6
  1602. package/tests/seed_verify_criteria.mjs +3 -3
  1603. package/tests/sequence.mjs +3 -3
  1604. package/tests/served_paths_shipped.mjs +3 -3
  1605. package/tests/session_506_regression.mjs +1 -1
  1606. package/tests/session_accessibility.mjs +1 -1
  1607. package/tests/session_caps.mjs +1 -1
  1608. package/tests/session_cost.mjs +1 -1
  1609. package/tests/session_digest.mjs +2 -2
  1610. package/tests/session_eval.mjs +6 -6
  1611. package/tests/session_index.mjs +1 -1
  1612. package/tests/session_records.mjs +4 -4
  1613. package/tests/session_rename_fallback.mjs +2 -2
  1614. package/tests/session_reward_on_land.mjs +7 -149
  1615. package/tests/session_reward_on_land_db.mjs +191 -0
  1616. package/tests/session_reward_reconcile.mjs +6 -88
  1617. package/tests/session_reward_reconcile_db.mjs +132 -0
  1618. package/tests/session_transcript.mjs +1 -1
  1619. package/tests/sessions_kit_pager.mjs +2 -1
  1620. package/tests/setup_device_flow_fallback.mjs +2 -2
  1621. package/tests/setup_smoke.mjs +12 -5
  1622. package/tests/setup_status_facts.mjs +54 -12
  1623. package/tests/ship_autolinkify.mjs +2 -2
  1624. package/tests/ship_automerge_injection.mjs +1 -1
  1625. package/tests/ship_base_flag.mjs +1 -1
  1626. package/tests/ship_broadcast_routing.mjs +1 -1
  1627. package/tests/ship_broadcast_server.mjs +2 -2
  1628. package/tests/ship_builder_model.mjs +1 -1
  1629. package/tests/ship_cannot_lie.mjs +9 -9
  1630. package/tests/ship_card.mjs +2 -2
  1631. package/tests/ship_ci_deploy.mjs +7 -7
  1632. package/tests/ship_error_shape.mjs +1 -1
  1633. package/tests/ship_land_ci_not_started.mjs +3 -3
  1634. package/tests/ship_land_conflict.mjs +3 -3
  1635. package/tests/ship_land_proof.mjs +3 -3
  1636. package/tests/ship_land_red_pr.mjs +5 -5
  1637. package/tests/ship_merge_gate.mjs +14 -14
  1638. package/tests/ship_mode.mjs +1 -1
  1639. package/tests/ship_mode_first_ship.mjs +4 -4
  1640. package/tests/ship_mode_hands_off.mjs +8 -8
  1641. package/tests/ship_post_land_verify.mjs +1 -1
  1642. package/tests/ship_preflight.mjs +19 -19
  1643. package/tests/ship_premerge.mjs +1 -1
  1644. package/tests/ship_publish_error.mjs +2 -2
  1645. package/tests/ship_push_diagnosis.mjs +1 -1
  1646. package/tests/ship_regen_copy_inventory.mjs +3 -3
  1647. package/tests/ship_resume.mjs +4 -4
  1648. package/tests/ship_smoke_gate.mjs +1 -1
  1649. package/tests/ship_upload_skip.mjs +1 -1
  1650. package/tests/ship_upstream_deletion.mjs +1 -1
  1651. package/tests/ship_vendored_instance.mjs +8 -8
  1652. package/tests/ship_visual_regrade.mjs +3 -3
  1653. package/tests/ship_zero_commit.mjs +1 -1
  1654. package/tests/skill_docs_unavailable_regrade.mjs +3 -3
  1655. package/tests/skill_friction_kind.mjs +1 -1
  1656. package/tests/skill_grade_recover.mjs +1 -1
  1657. package/tests/skill_grader_health.mjs +1 -1
  1658. package/tests/skill_lint.mjs +1 -1
  1659. package/tests/skill_menu_ruling.mjs +1 -1
  1660. package/tests/skill_preflight.mjs +7 -7
  1661. package/tests/skip_is_not_pass.mjs +22 -1
  1662. package/tests/smoke_dependencies_witness.mjs +2 -2
  1663. package/tests/smoke_preflight.mjs +1 -1
  1664. package/tests/smoke_target.mjs +2 -2
  1665. package/tests/smoke_write_contract.mjs +2 -2
  1666. package/tests/sound_player.mjs +8 -8
  1667. package/tests/source_text_test_ratchet.mjs +97 -0
  1668. package/tests/speciality_corpus_scope.mjs +1 -1
  1669. package/tests/speciality_session_skills.mjs +1 -1
  1670. package/tests/sso_checkin_legacy_field_gone.mjs +4 -2
  1671. package/tests/stale_timer.mjs +3 -3
  1672. package/tests/stale_worktrees.mjs +1 -1
  1673. package/tests/start_env_guard.mjs +1 -1
  1674. package/tests/start_goal_first.mjs +3 -3
  1675. package/tests/start_inbox_count.mjs +1 -1
  1676. package/tests/start_rebase_warning.mjs +1 -1
  1677. package/tests/status_default_version.mjs +3 -3
  1678. package/tests/status_mirror_sync.mjs +1 -1
  1679. package/tests/status_page_no_dead_uptime.mjs +80 -0
  1680. package/tests/stop_refcheck.mjs +1 -1
  1681. package/tests/strand_watch.mjs +1 -1
  1682. package/tests/stranded_rows.mjs +1 -1
  1683. package/tests/stuck_detector.mjs +14 -14
  1684. package/tests/sweep_app_account.mjs +3 -3
  1685. package/tests/task_cli.mjs +2 -2
  1686. package/tests/task_detail_ui.mjs +11 -1
  1687. package/tests/task_discipline_required.mjs +2 -2
  1688. package/tests/task_list_paging.mjs +1 -1
  1689. package/tests/task_visual_slots.mjs +2 -2
  1690. package/tests/terms_acceptance.mjs +1 -1
  1691. package/tests/terms_grandfather_db.mjs +1 -1
  1692. package/tests/test_path_guard.mjs +1 -1
  1693. package/tests/triage_development.mjs +1 -1
  1694. package/tests/tweak_batch_apply.mjs +2 -2
  1695. package/tests/tweak_land_heal.mjs +3 -3
  1696. package/tests/tweak_unattended.mjs +3 -3
  1697. package/tests/ui_design_skills.mjs +2 -2
  1698. package/tests/unit_report_status.mjs +2 -2
  1699. package/tests/unit_runner_serial.mjs +2 -2
  1700. package/tests/unit_runner_teardown_abort.mjs +1 -1
  1701. package/tests/update_channel.mjs +2 -2
  1702. package/tests/update_channel_db.mjs +4 -4
  1703. package/tests/update_subscription_engine.mjs +26 -26
  1704. package/tests/update_sweep_home.mjs +14 -14
  1705. package/tests/upgrade.mjs +11 -11
  1706. package/tests/upgrade_artist_gate.mjs +4 -4
  1707. package/tests/upgrade_commit_identity.mjs +2 -2
  1708. package/tests/upgrade_migrate.mjs +7 -7
  1709. package/tests/upgrade_operator.mjs +2 -2
  1710. package/tests/upgrade_outcome.mjs +2 -2
  1711. package/tests/upgrade_persist_pin.mjs +5 -5
  1712. package/tests/upkeep_home.mjs +1 -1
  1713. package/tests/version_close.mjs +2 -2
  1714. package/tests/version_close_cli.mjs +2 -2
  1715. package/tests/version_close_route.mjs +1 -1
  1716. package/tests/version_literals.mjs +23 -23
  1717. package/tests/visual_review.mjs +5 -5
  1718. package/tests/wedge_remedy.mjs +5 -5
  1719. package/tests/welcome_boot.mjs +116 -0
  1720. package/tests/welcome_boot_browser.mjs +271 -0
  1721. package/tests/wizard_born_first_board.mjs +3 -3
  1722. package/tests/wizard_demo.mjs +1 -1
  1723. package/tests/wizard_one_page.mjs +4 -3
  1724. package/tests/wizard_physics.mjs +3 -1
  1725. package/tests/wizard_physics_more.mjs +42 -3
  1726. package/tests/work_title_wrap.mjs +1 -1
  1727. package/tests/workflow_exec_paths.mjs +6 -6
  1728. package/tests/worktree_claim_guard.mjs +1 -1
  1729. package/tests/worktree_helper.mjs +2 -2
  1730. package/tests/write_validation_gaps.mjs +3 -3
  1731. package/scripts/gds/api.sh +0 -6
  1732. package/scripts/gds/audit-exports.js +0 -128
  1733. package/scripts/gds/resolve-skill-model.js +0 -57
  1734. package/scripts/gds/security-baseline-check.sh +0 -180
  1735. package/scripts/gds/spike-task-map.js +0 -899
  1736. package/scripts/status-probe.mjs +0 -213
  1737. package/src/bongos/uptime-poller.js +0 -93
  1738. package/tests/government_fold_bench.mjs +0 -50
  1739. package/tests/spike_task_map.mjs +0 -422
  1740. package/tests/status-probe.mjs +0 -89
  1741. /package/{scripts/gds → modules/raise}/CLAUDE.md +0 -0
  1742. /package/scripts/{gds → bongos}/cloudbongos-token-isolation.conf +0 -0
  1743. /package/scripts/{gds → bongos}/discipline-modes.json +0 -0
  1744. /package/scripts/{gds → bongos}/retired-core-skills.json +0 -0
@@ -0,0 +1,1337 @@
1
+ // scripts/bongos/provision-repo.js — everything that makes an instance's own repo
2
+ // and database real: the db create/migrate/seed commands, the standalone repo
3
+ // scaffold, adopt-vs-greenfield detection, the private-repo deploy key, and the
4
+ // re-provision core-pin refresh.
5
+ //
6
+ // Carved out of scripts/bongos/provision.js by task 1003206 (goal 1000079, the R03
7
+ // de-monolith plan) to bring the control-plane CLI under the 1,500-line ratchet.
8
+ // A STRUCTURAL move: bodies unchanged, provision.js requires every name back, so
9
+ // the intent runner and its unit templates behave exactly as before.
10
+
11
+ 'use strict';
12
+
13
+ const crypto = require('node:crypto');
14
+ const fs = require('node:fs');
15
+ const os = require('node:os');
16
+ const path = require('node:path');
17
+ const { CONFIG, REPO_ROOT, coreVersionSafe, provisionerBotEmail, provisionerBotName } = require('./provision-config.js');
18
+ // The repo-identity + owner-credential leaf (task 1003081). Carved OUT of this file so
19
+ // upgrade.js can reach these three without pulling provision*.js into the merge
20
+ // resolver's executed closure; re-exported below so every existing caller is unchanged.
21
+ const { instanceRepoRemote, parseTargetRef, resolveOwnerGithubToken } = require('./provision-repo-auth.js');
22
+ const { normalizePackEnvPrefix } = require('./pack-env-prefix.js'); // an adopted pack's envPrefix (task 1004573)
23
+ const { CORE_PKG, compareSemver, isStable } = require('./update-channel.js');
24
+
25
+ function dbName(inst) { return inst.db_name || inst.slug; }
26
+
27
+ // Escalate a DB command to run as the postgres OS user (→ PG superuser via peer auth,
28
+ // the Ubuntu default) when privileged — the DB-auth fix (task 2066). The runner's
29
+ // `lars` is not a usable PG role, so bare peer-auth-as-lars fails; `sudo -u postgres`
30
+ // sidesteps it without granting lars a role. PURE given the flag.
31
+ function asPostgres(cmd, privileged) { return privileged ? `sudo -u postgres ${cmd}` : cmd; }
32
+
33
+ function dbCreateCmd(inst, { privileged = false } = {}) {
34
+ // Idempotent: the caller runs it allowFail — "database already exists" is success.
35
+ // Non-privileged = bare `createdb` (peer auth as the running user, same as the
36
+ // standup runbook / OTB); privileged = `sudo -u postgres createdb` (task 2066).
37
+ return asPostgres(`createdb ${dbName(inst)}`, privileged);
38
+ }
39
+ function migrateCmd(inst, { privileged = false } = {}) {
40
+ // GDS-only schema — skips migrations/game/*.sql (no game tables on a vanilla
41
+ // instance), per the standup runbook (ADR 0105 instance seeds off). Privileged
42
+ // runs migrate as postgres (task 2066): `sudo` resets env, so the PGDATABASE/
43
+ // MEDUSA_GDS_ONLY vars ride `env …` and the script runs via `bash …` (a bare
44
+ // `VAR=val ./script` would be parsed BY sudo as a command). Precondition: the
45
+ // control-plane checkout must be readable by the postgres user (ADR 0128).
46
+ return privileged
47
+ ? `sudo -u postgres env PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 bash ./scripts/migrate.sh`
48
+ : `PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 ./scripts/migrate.sh`;
49
+ }
50
+
51
+ // Seed the instance's FIRST version so it can take tasks from minute one (task 2119,
52
+ // goal 35). A freshly-migrated instance DB comes up with an EMPTY versions table —
53
+ // migrate runs instance-seeds-off (ADR 0105) and `bongos init` ran --no-seed — yet
54
+ // every task requires a version (tasks.version_id NOT NULL) and POST /versions is
55
+ // Archon-only (no Archon exists until first sign-in). Without a seed here, a brand-new
56
+ // project has NOWHERE to file a task. This runs on the box's DB path (no rank gate),
57
+ // ONLY IF the versions table is empty: idempotent, safe to re-run, and it never
58
+ // disturbs a project that already has versions. Mirrors seedViaApi's shape — V1
59
+ // (status 'building') + one done-when criterion so /status has a finish line from day
60
+ // one. No `track` column: the two-track model was retired (task 1002006, unwound
61
+ // by task 1003665). The seed runs on a freshly-MIGRATED database, so core_205's
62
+ // DEFAULT 'product' is in place and an INSERT that omits the column is legal.
63
+ //
64
+ // The id/name/done_when come from instanceInitSpec so the starter version has ONE
65
+ // source of truth (the same spec the greenfield `bongos init` seed uses). SQL string
66
+ // literals are DOLLAR-QUOTED ($v$…$v$) so the whole -c argument is single-quoted —
67
+ // the one form that is safe UNCHANGED across both executors: inline (/bin/sh) and
68
+ // over SSH (bash -s), with no nested-quote or $-expansion hazard.
69
+ function seedFirstVersionCmd(inst, { privileged = false } = {}) {
70
+ const v = instanceInitSpec(inst).version;
71
+ const q = (s) => `$v$${s}$v$`; // dollar-quoted literal (no value contains a single quote or $)
72
+ const sql =
73
+ `INSERT INTO versions (id, name, status, done_when) ` +
74
+ `SELECT ${q(v.id)}, ${q(v.name)}, $v$building$v$, ${q(v.done_when)} ` +
75
+ `WHERE NOT EXISTS (SELECT 1 FROM versions); ` +
76
+ `INSERT INTO done_when_criteria (version_id, criterion_id, criterion_md, sort_order) ` +
77
+ `SELECT ${q(v.id)}, $v$first-goal$v$, ${q(v.done_when)}, 10 ` +
78
+ `WHERE EXISTS (SELECT 1 FROM versions WHERE id = ${q(v.id)}) ` +
79
+ `AND NOT EXISTS (SELECT 1 FROM done_when_criteria WHERE version_id = ${q(v.id)} AND criterion_id = $v$first-goal$v$);`;
80
+ return asPostgres(`psql -v ON_ERROR_STOP=1 -d ${dbName(inst)} -c '${sql}'`, privileged);
81
+ }
82
+
83
+ // ── The CLOUD-HOST shape (hosting_shape='cloud-host', ADR 0108, task 1978) ────
84
+ //
85
+ // TWO SENSES OF "STANDALONE", AND THIS FILE USES BOTH. The ADR 0108 sense is a repo
86
+ // TOPOLOGY: an instance running from its own checkout with the core as an npm dependency,
87
+ // as opposed to co-tenant's shared control-plane checkout. That is what `standaloneRoot`,
88
+ // `standaloneBase` and `PROVISION_STANDALONE_BASE` name, and those keep their spelling —
89
+ // `PROVISION_STANDALONE_BASE` in particular is a live environment variable on the control
90
+ // plane and is documented in the /new-project skill. The other sense was the HOSTING SHAPE
91
+ // once stored as 'standalone'; task 1004182 renamed that value to 'cloud-host'. Only the
92
+ // stored value moved. Do not "finish the rename" by touching the topology names.
93
+ //
94
+ // A cloud-host instance runs from its OWN repo checkout (host content: config/,
95
+ // migrations/instance/, the core PIN in package.json) with @bongos/core installed
96
+ // as an npm DEPENDENCY — NOT the shared control-plane checkout the co-tenant shape uses.
97
+ // It is the ADR 0108 DESTINATION model; these generators make provision.js drive the
98
+ // scripts/deploy/deploy-instance.sh runbook API-driven instead of by hand.
99
+ const CORE_PKG_DIR = 'node_modules/@bongos/core';
100
+ // The plane's app user: the unix account that owns the control-plane checkout, owns the
101
+ // backup dir, runs the backup timers, and whose GROUP every instance account joins to read
102
+ // the checkout. One resolver, so those four can't drift apart.
103
+ //
104
+ // Derived, never named (task 1002719). It used to be the founder's own account as a
105
+ // literal, and a standup on any host that account did not exist on died `invalid user` at
106
+ // the backup step. PROVISION_APP_USER pins it; otherwise it is the account this runner
107
+ // executes as, which on a plane IS the app user (the runner unit runs as it), so the live
108
+ // box resolves exactly what the literal said. A runner driving a different box over SSH
109
+ // (remoteHost) should pin it. root is refused, not resolved: the instance
110
+ // accounts join this account's group, and group root is not a read grant to hand a tenant.
111
+ // Validated because it is interpolated into shell commands and unit files.
112
+ const UNIX_ACCOUNT_RE = /^[a-z_][a-z0-9_-]{0,31}$/;
113
+ function appUser({ env = process.env, userInfo = os.userInfo } = {}) {
114
+ let name = String(env.PROVISION_APP_USER || '').trim();
115
+ if (!name) {
116
+ try { name = userInfo().username; } catch { name = ''; } // a uid with no passwd entry
117
+ }
118
+ if (name === 'root' || !UNIX_ACCOUNT_RE.test(name)) {
119
+ const why = name === 'root' ? 'is root' : 'is not a usable unix account name';
120
+ throw new Error(`provisioning: the app user ${JSON.stringify(name)} ${why} — set PROVISION_APP_USER to the unprivileged account that owns this checkout`);
121
+ }
122
+ return name;
123
+ }
124
+ // The app user's PRIMARY group, looked up by the shell on the box the command runs on. It
125
+ // need not share the account's name (a service account created into `users`), so it is
126
+ // never spelled as the account name.
127
+ function appGroupExpr() { return `$(id -gn ${appUser()})`; }
128
+ function standaloneRoot(inst, { base = CONFIG.standaloneBase } = {}) { return path.posix.join(base, inst.slug); }
129
+ // WHERE THE INSTANCE THIS CORE SERVES ACTUALLY LIVES (task 1004123).
130
+ //
131
+ // REPO_ROOT is `path.resolve(__dirname, '..', '..')` — it points at whatever copy of
132
+ // the core is EXECUTING, which is not the same place as the instance being upgraded.
133
+ // Two shapes, and the code had only ever met the first:
134
+ //
135
+ // • a core CHECKOUT run in place (`/home/lars/cloudbongos`) — the instance root IS
136
+ // the checkout, so REPO_ROOT was right by coincidence;
137
+ // • a VENDORED core (`<instance>/node_modules/@bongos/core`) — REPO_ROOT is the
138
+ // package, and the instance is three directories up.
139
+ //
140
+ // The live control plane became the second shape when its runner unit was re-homed
141
+ // (`WorkingDirectory=/home/lars/cloudbongos-instance`, ExecStart into the vendored
142
+ // core), and nothing noticed: REPO_ROOT still resolved, just to the wrong tree. So
143
+ // `readInstalledCoreVersion` answered null instead of 1.19.917, every guard that
144
+ // reads `installed` silently stopped guarding, and an upgrade would have pinned and
145
+ // npm-installed INSIDE the core package while the instance's own pin stayed put.
146
+ //
147
+ // This is a string question, not a filesystem one: the vendored layout is the only
148
+ // way a core ends up under `node_modules/`, so the suffix IS the answer. Keeping it
149
+ // pure means the control-plane path is testable without a box.
150
+ // Either separator counts as a segment boundary (task 1004663): a suffix built from
151
+ // path.sep alone never matched a POSIX-shaped path on Windows, so the suite went red
152
+ // there while the Linux box (the only place this runs for real) stayed green.
153
+ function selfInstanceRoot(repoRoot = REPO_ROOT) {
154
+ const segs = CORE_PKG_DIR.split('/');
155
+ const tail = repoRoot.split(/[\\/]/).slice(-(segs.length + 1));
156
+ const isVendored = tail.length === segs.length + 1 && segs.every((s, i) => tail[i + 1] === s);
157
+ return isVendored ? repoRoot.slice(0, -(segs.join('/').length + 1)) : repoRoot;
158
+ }
159
+ // Sync the instance's own repo (host content + the core pin). Mirrors deploy-instance.sh
160
+ // step 1 — assumes the repo is already cloned at standaloneRoot (initial clone is a
161
+ // precondition, like the runbook's `git pull`). Run with cwd = standaloneRoot.
162
+ function standalonePullCmd() { return `git pull --ff-only origin main`; }
163
+ // Install the pinned core + instance deps — replaces co-tenant's shared checkout
164
+ // (deploy-instance.sh step 2). Run with cwd = standaloneRoot.
165
+ function standaloneInstallCmd() { return `npm ci --omit=dev`; }
166
+ // Migrate core + instance from the INSTANCE root's copy of the three-root migrate.sh
167
+ // (the installed core's, falling back to an in-tree copy — deploy-instance.sh step 3).
168
+ // Run with cwd = standaloneRoot.
169
+ function standaloneMigrateCmd(inst, { privileged = false } = {}) {
170
+ // Privileged: run as postgres (task 2066), passing env via `env …` so it survives
171
+ // sudo's env reset (the instance root must be postgres-readable, ADR 0128).
172
+ return privileged
173
+ ? `sudo -u postgres env PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 bash ${CORE_PKG_DIR}/scripts/migrate.sh`
174
+ : `PGDATABASE=${dbName(inst)} MEDUSA_GDS_ONLY=1 bash ${CORE_PKG_DIR}/scripts/migrate.sh`;
175
+ }
176
+ // Regenerate the instance's whole-file navigation docs (session-log index) from the pinned core,
177
+ // AFTER the pull+install+migrate — the standalone analogue of the monorepo's deploy-prod.sh regen
178
+ // block (ADR 0110 §2, task 1002314). Best-effort + LOUD (the script always exits 0 + prints
179
+ // ::ERROR:: on a generator failure) so a stale/absent nav doc never fails a deploy. Run with
180
+ // cwd = standaloneRoot so resolveInstanceRoot() writes into the instance repo. Without this a
181
+ // standalone instance's docs never refresh on deploy — the gap the owner hit ("docs didn't
182
+ // transfer from primea"): its bespoke pull-deploy regenerated nothing.
183
+ function standaloneRegenDocsCmd() { return `node ${CORE_PKG_DIR}/scripts/bongos/regen-instance-docs.js`; }
184
+
185
+ // ── Per-instance identity: its OWN unix account + its OWN password-authed PG role ──
186
+ // (task 1003369, audit ref B3 of the 2026-08-29 security audit.)
187
+ //
188
+ // THE HOLE THIS CLOSES. Every instance service used to run as the one shared app user, and
189
+ // co-tenant is the DEFAULT shape — so on a shared box every instance ran as the SAME
190
+ // unix user. A unix user may read /proc/<pid>/environ of its own processes, which means
191
+ // any instance could read every SIBLING's environment: GITHUB_APP_PRIVATE_KEY, the
192
+ // <PREFIX>_HUB_CLIENT_SECRET, every OAuth secret web.env carries. The DB half was the
193
+ // same shape one layer down: pool.js connected by PASSWORDLESS peer auth, so any code
194
+ // running as lars could open a pool against any sibling database — including the control
195
+ // plane's own `builders` and `builder_sessions`. Neither was a bug in a route; it was the
196
+ // identity every instance shared. Giving each instance its own unix account and its own
197
+ // password-authed role is what makes the boundary real.
198
+ //
199
+ // NAME SHAPE. A unix account name is capped at 32 chars (utmp) and must match
200
+ // [a-z_][a-z0-9_-]*; a slug is 2–40 of [a-z0-9-] (isValidSlug). So the name cannot be
201
+ // the slug verbatim, and it must not be the BARE slug either — that would let a project
202
+ // named `postgres` or `lars` claim an existing account. Prefixed + length-folded:
203
+ // short slug → bongos-<slug>
204
+ // long slug → bongos-<slug truncated>-<8 hex of sha256(slug)> (still <= 32)
205
+ // The hash suffix keeps two slugs that share a 16-char prefix from folding onto one
206
+ // account — which would hand each the other's identity, the very thing being fixed.
207
+ const INSTANCE_USER_PREFIX = 'bongos-';
208
+ const INSTANCE_USER_MAX = 32; // utmp's cap; useradd enforces it
209
+
210
+ // The unix account an instance's service (and only that instance's service) runs as.
211
+ // Deterministic — the runner derives it fresh every run rather than storing it, so a
212
+ // re-provision can never address a different account than the one it created. PURE.
213
+ function instanceUser(inst) {
214
+ const slug = String((inst && inst.slug) || '');
215
+ if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(slug)) {
216
+ throw new Error(`instanceUser: refusing to derive an account name from an invalid slug ${JSON.stringify(slug)}`);
217
+ }
218
+ const whole = INSTANCE_USER_PREFIX + slug;
219
+ if (whole.length <= INSTANCE_USER_MAX) return whole;
220
+ const digest = crypto.createHash('sha256').update(slug).digest('hex').slice(0, 8);
221
+ // Budget: prefix + head + '-' + 8 hex === INSTANCE_USER_MAX. Trailing dashes are
222
+ // stripped so the fold can never emit `--` (which useradd's NAME_REGEX rejects).
223
+ const head = slug.slice(0, INSTANCE_USER_MAX - INSTANCE_USER_PREFIX.length - 9).replace(/-+$/, '');
224
+ return `${INSTANCE_USER_PREFIX}${head}-${digest}`;
225
+ }
226
+
227
+ // The instance account's HOME — and the ONE directory the hardened unit leaves
228
+ // writable. It is deliberately NOT under /home: the unit sets ProtectHome=read-only
229
+ // (so one instance cannot read the operator's or a sibling's home), and the server DOES
230
+ // write inside its config home at runtime — src/bongos/secret-box.js provisions
231
+ // builder-secret.key under ~/.config/<configDir> on first boot. A /home account would
232
+ // make that write EROFS and the instance would come up unable to hold a secret. PURE.
233
+ function instanceStateDir(inst) { return path.posix.join('/var/lib', instanceUser(inst)); }
234
+
235
+ // The instance's OWN Postgres login role. Same name as the unix account so an operator
236
+ // reading `ps` and `pg_stat_activity` sees one identity, not two — a dash is legal in a
237
+ // quoted PG identifier, and every SQL site below quotes it. PURE.
238
+ function instanceDbRole(inst) { return instanceUser(inst); }
239
+
240
+ // A fresh password for that role. base64url ([A-Za-z0-9_-]) on purpose: it needs no
241
+ // escaping in the env file that carries it, in the dollar-quoted SQL literal that sets
242
+ // it, or in a connection URL — the three places it travels. 32 bytes ~ 256 bits.
243
+ // Generated per provision run, not stored: every run ALTERs the role and rewrites
244
+ // web.env in the same pass, so the credential rotates on its own.
245
+ function generateDbPassword() { return crypto.randomBytes(32).toString('base64url'); }
246
+
247
+ // Create the instance's unix account if it is not already there, with its state dir.
248
+ // IDEMPOTENT by construction — `id -u` short-circuits an existing account, and the
249
+ // `install -d` re-asserts ownership and mode on every run, so a re-provision repairs a
250
+ // hand-edited box instead of failing on it. A SYSTEM account with /usr/sbin/nologin:
251
+ // nothing should ever log in as an instance.
252
+ function ensureInstanceUserCmd(inst, { privileged = false } = {}) {
253
+ const user = instanceUser(inst), dir = instanceStateDir(inst), sudo = privileged ? 'sudo ' : '';
254
+ return `id -u ${user} >/dev/null 2>&1 || ${sudo}useradd --system --shell /usr/sbin/nologin --home-dir ${dir} ${user}; ` +
255
+ `${sudo}install -d -o ${user} -g ${user} -m 0700 ${dir}`;
256
+ }
257
+
258
+ // Let the instance account READ the checkout it runs from. The co-tenant shape's
259
+ // WorkingDirectory is this control-plane checkout and the standalone shape's is
260
+ // /srv/<base>/<slug>; both are owned by the app user, and a fresh system account is in
261
+ // neither the owner nor the group, so without this the unit fails at exec with EACCES.
262
+ // Group membership grants exactly the read the app user's group already has — it does
263
+ // NOT re-open the two vectors this task closes: /proc/<pid>/environ stays readable only
264
+ // by the process's own uid, and the DB now demands a password no sibling holds.
265
+ function grantInstanceRepoReadCmd(inst, { privileged = false } = {}) {
266
+ return `${privileged ? 'sudo ' : ''}usermod -aG ${appGroupExpr()} ${instanceUser(inst)}`;
267
+ }
268
+
269
+ // Can this plane give a child instance its OWN unix account AT ALL? (task 1004018, ADR 0317)
270
+ //
271
+ // ensureInstanceUserCmd above needs uid 0 — directly, or through the `privileged` sudo
272
+ // prefix. The self-host image runs `USER node` and installs no sudo, so there it cannot
273
+ // run, and every child on that plane would share one uid: exactly the /proc/<pid>/environ
274
+ // vector ADR 0281 exists to close. Probed, not configured — a runner either reaches root
275
+ // on this plane or it does not, and an operator who wants provisioning to proceed needs
276
+ // privilege, not a flag that says to pretend.
277
+ //
278
+ // The probe runs the SAME escalation the mutation will, so a pass is evidence rather than
279
+ // inference. Read-only, no mutation, and no pipeline: a pipeline's exit status is the last
280
+ // stage's, which would erase the difference below.
281
+ //
282
+ // A shell that RAN AND ANSWERED is the only failure that proves the plane cannot. Under
283
+ // remote exec boxExec is `ssh <host> bash -s`, so an unreachable co-hosting box throws
284
+ // here too — and answering that with "this plane cannot create accounts, provision
285
+ // elsewhere" would be a confident wrong diagnosis that burns the intent's retry budget on
286
+ // a network blip. Exit 1 (not root) and 127 (no sudo binary) are read as proof; ssh
287
+ // reports its own failures as 255, so both still mean what they say across the hop.
288
+ const ACCOUNT_PROOF_STATUS = new Set([1, 127, 9009]);
289
+ function accountPlaneGap(exec, { privileged = false } = {}) {
290
+ // sh -c, not a bare test: under sudo the outer (unprivileged) shell would expand
291
+ // $(id -u) before sudo ever ran, and the probe would answer about the wrong user.
292
+ const cmd = `${privileged ? 'sudo ' : ''}sh -c 'test "$(id -u)" = 0'`;
293
+ try { exec(cmd); return null; } catch (e) {
294
+ // e.stderr is a zero-length Buffer when the command failed SILENTLY — truthy, and
295
+ // String()s to '' — so it has to be length-checked or it shadows the only cause there
296
+ // is. `test` exiting 1 writes nothing, and that is this probe's commonest refusal.
297
+ const stderr = e && e.stderr && e.stderr.length ? e.stderr : null;
298
+ const why = String(stderr || (e && e.message) || e).trim().slice(0, 200) || `exit status ${e && e.status}`;
299
+ if (!ACCOUNT_PROOF_STATUS.has(e && e.status)) {
300
+ return `could not confirm the runner can create a per-instance unix account on this plane: '${cmd}' failed (${why}). Every instance is given its own account, so provisioning refuses rather than assume — re-run once the box answers.`;
301
+ }
302
+ return `this plane cannot create a per-instance unix account, so every child instance would run as one uid and could read its siblings' secrets: '${cmd}' failed (${why}). Provision from a host where the runner is root or holds sudo (PROVISION_SUDO=1) — a shared uid is the isolation ADR 0281 exists to provide, not a detail.`;
303
+ }
304
+ }
305
+
306
+ // The preflight around accountPlaneGap: the narration its sibling preflights carry,
307
+ // including the dry-run line without which the plan prints step 1c's useradd as though it
308
+ // will work. Returns the refusal, or null to proceed. Lives here beside the account it guards.
309
+ function accountPreflight({ exec, apply, log, privileged = false, probe = accountPlaneGap }) {
310
+ if (!apply) { log(' [preflight] would probe whether the runner can create this instance\'s own unix account'); return null; }
311
+ const missing = probe(exec, { privileged });
312
+ if (missing) return `Account preflight failed: ${missing}`;
313
+ log(' [preflight] the runner can create the instance\'s own unix account ✓');
314
+ return null;
315
+ }
316
+
317
+ // Create-or-rotate the instance's PG login role and hand it its OWN database — and
318
+ // nothing else. Three statements, one psql, ON_ERROR_STOP:
319
+ // 1. CREATE ROLE ... LOGIN PASSWORD, or ALTER ... PASSWORD when it already exists (the
320
+ // re-provision path — this is what rotates the credential).
321
+ // 2. ALTER DATABASE ... OWNER TO — the role owns its own DB, so the app's DDL needs no
322
+ // superuser, and in PG15+ it inherits the public schema through pg_database_owner.
323
+ // 3. REVOKE CONNECT ... FROM PUBLIC — the half that actually isolates. Postgres grants
324
+ // CONNECT on every database to PUBLIC by default, so per-role passwords ALONE would
325
+ // still leave a sibling one peer-auth hop from any other instance's data.
326
+ // NOT a superuser and NOT CREATEDB/CREATEROLE: an instance that is compromised gets its
327
+ // own data and no path to anyone else's.
328
+ //
329
+ // Runs against `postgres`, not the instance DB: ALTER DATABASE ... OWNER cannot run from
330
+ // inside the database it renames the owner of.
331
+ //
332
+ // QUOTING, which is load-bearing in a way that is easy to undo by accident. Every literal
333
+ // is DOLLAR-QUOTED and every identifier goes through format(%I), so the rendered command
334
+ // contains no single quote and NO DOUBLE QUOTE, so it survives both executors unchanged —
335
+ // inline /bin/sh and `bash -s` over SSH. seedFirstVersionCmd dollar-quotes for the same
336
+ // reason; a test asserts the absence.
337
+ function dbRoleCmd(inst, { password = null, privileged = false } = {}) {
338
+ const role = instanceDbRole(inst), db = dbName(inst);
339
+ if (!/^[A-Za-z0-9_-]+$/.test(String(password || ''))) {
340
+ throw new Error('dbRoleCmd: password must be a non-empty base64url string (generateDbPassword)');
341
+ }
342
+ const sql =
343
+ `DO $do$ BEGIN ` +
344
+ `IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = $r$${role}$r$) THEN ` +
345
+ `EXECUTE format($f$ALTER ROLE %I WITH LOGIN PASSWORD %L$f$, $r$${role}$r$, $p$${password}$p$); ` +
346
+ `ELSE ` +
347
+ `EXECUTE format($f$CREATE ROLE %I WITH LOGIN PASSWORD %L$f$, $r$${role}$r$, $p$${password}$p$); ` +
348
+ `END IF; ` +
349
+ `EXECUTE format($f$ALTER DATABASE %I OWNER TO %I$f$, $d$${db}$d$, $r$${role}$r$); ` +
350
+ `EXECUTE format($f$REVOKE CONNECT ON DATABASE %I FROM PUBLIC$f$, $d$${db}$d$); ` +
351
+ `END $do$;`;
352
+ return asPostgres(`psql -v ON_ERROR_STOP=1 -d postgres -c '${sql}'`, privileged);
353
+ }
354
+
355
+ // What the runner LOGS in place of dbRoleCmd, whose SQL embeds the password. Lives here,
356
+ // beside the command, so the two cannot drift: a `shown` that outlived the secret it hides
357
+ // would be a silent re-leak. Names every effect the real command has. PURE.
358
+ function dbRoleCmdShown(inst) {
359
+ return `psql -d postgres -c '<upsert role ${instanceDbRole(inst)} + own ${dbName(inst)} + revoke PUBLIC connect>' (password redacted)`;
360
+ }
361
+
362
+ // Hand the instance role the objects the migrate just created. This is NOT belt-and-braces:
363
+ // the privileged migrate runs as `postgres`, so every table and sequence it creates is
364
+ // OWNED BY postgres. Owning the DATABASE does not carry ownership of objects inside it, so
365
+ // without these grants the instance would authenticate perfectly and then fail its first
366
+ // SELECT. The ALTER DEFAULT PRIVILEGES pair is the same statement aimed forward: it applies
367
+ // to objects the CURRENT role creates later, which is exactly what the next migrate does —
368
+ // so a schema migration shipped six months from now needs no second visit here.
369
+ //
370
+ // Runs against the instance DB (schema grants are per-database), and AFTER the migrate.
371
+ function dbRoleGrantsCmd(inst, { privileged = false } = {}) {
372
+ const role = instanceDbRole(inst), db = dbName(inst);
373
+ // Same quoting rule as dbRoleCmd: format(%I), never a double-quoted identifier.
374
+ const g = (stmt) => `EXECUTE format($f$${stmt} %I$f$, $r$${role}$r$); `;
375
+ const sql =
376
+ `DO $do$ BEGIN ` +
377
+ g('GRANT ALL ON SCHEMA public TO') +
378
+ g('GRANT ALL ON ALL TABLES IN SCHEMA public TO') +
379
+ g('GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO') +
380
+ g('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO') +
381
+ g('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO') +
382
+ `END $do$;`;
383
+ return asPostgres(`psql -v ON_ERROR_STOP=1 -d ${db} -c '${sql}'`, privileged);
384
+ }
385
+
386
+ // Hand OWNERSHIP of everything in the schema to the instance role (task 1004735). The grants
387
+ // above are not enough: GRANT ALL carries no right to ALTER a table or CREATE OR REPLACE a
388
+ // function, which only the OWNER has. Later core moves migrate AS the instance role
389
+ // (--migrate-as, task 1004512), so the first migration that changed an existing table failed
390
+ // with "must be owner of table tasks" and the move rolled back — toast, 2026-10-14.
391
+ //
392
+ // Object by object, never REASSIGN OWNED BY postgres: that also reassigns every SHARED object
393
+ // postgres owns — sibling projects' whole databases included. Skipped on purpose:
394
+ // • objects an EXTENSION owns (pg_depend deptype e) — not the project's, and ALTER errors;
395
+ // • sequences linked to a table column (deptype a/i) — they follow their table, and an
396
+ // ALTER SEQUENCE on one errors;
397
+ // • row types of tables and auto-made array types — they follow their table / element.
398
+ // Idempotent: only objects not already owned by the role are touched, so a re-run is a no-op.
399
+ //
400
+ // Same quoting rule as the commands above: dollar-quoted literals, format(%I)/regclass for
401
+ // identifiers — no single or double quote, so it rides psql -c '…' and bash -s unchanged. And
402
+ // never the text $p$: that marks a password for the runner's log redaction (provision.mjs), which
403
+ // is why kind lists are array literals and a procedure is the ELSE of f/w/a.
404
+ // Run as postgres, in the instance's own database: provision.js step 2b (right after the
405
+ // grants), and the migrate_not_owner remedy in wedge-remedy.js for projects provisioned before.
406
+ function dbOwnershipSql(role, schema = 'public') {
407
+ if (!/^[A-Za-z0-9_-]{1,63}$/.test(String(role || ''))) throw new Error(`dbOwnershipSql: role ${JSON.stringify(role)} is not a plain role name`);
408
+ if (!/^[a-z_][a-z0-9_]{0,62}$/.test(String(schema))) throw new Error(`dbOwnershipSql: schema ${JSON.stringify(schema)} is not a plain schema name`);
409
+ const r = `$r$${role}$r$`;
410
+ const ns = `$s$${schema}$s$::regnamespace`;
411
+ const notExt = (cat, col, types = '$e$e$e$') =>
412
+ `NOT EXISTS (SELECT 1 FROM pg_depend d WHERE d.classid = $c$${cat}$c$::regclass AND d.objid = ${col} AND d.deptype IN (${types}))`;
413
+ const k = (x) => `$k$${x}$k$`;
414
+ const t = (x) => `$t$${x}$t$`;
415
+ const anyOf = (list) => '$k${' + list + '}$k$::char[]'; // an array literal, not $k$p$k$ — see the $p$ note above
416
+ return `DO $own$ DECLARE o record; BEGIN ` +
417
+ // tables, partitions, views, materialized views, foreign tables — their indexes, row types
418
+ // and column-owned sequences follow them
419
+ `FOR o IN SELECT c.oid::regclass AS obj, c.relkind FROM pg_class c WHERE c.relnamespace = ${ns} ` +
420
+ `AND c.relkind = ANY (${anyOf('r,p,v,m,f')}) AND c.relowner <> ${r}::regrole AND ${notExt('pg_class', 'c.oid')} LOOP ` +
421
+ `EXECUTE format($f$ALTER %s %s OWNER TO %I$f$, CASE o.relkind WHEN ${k('v')} THEN ${t('VIEW')} WHEN ${k('m')} THEN ${t('MATERIALIZED VIEW')} WHEN ${k('f')} THEN ${t('FOREIGN TABLE')} ELSE ${t('TABLE')} END, o.obj, ${r}); END LOOP; ` +
422
+ // free-standing sequences
423
+ `FOR o IN SELECT c.oid::regclass AS obj FROM pg_class c WHERE c.relnamespace = ${ns} AND c.relkind = ${k('S')} ` +
424
+ `AND c.relowner <> ${r}::regrole AND ${notExt('pg_class', 'c.oid', '$e$e$e$, $e$a$e$, $e$i$e$')} LOOP ` +
425
+ `EXECUTE format($f$ALTER SEQUENCE %s OWNER TO %I$f$, o.obj, ${r}); END LOOP; ` +
426
+ // functions, procedures, aggregates — what CREATE OR REPLACE needs to own
427
+ `FOR o IN SELECT p.oid::regprocedure AS obj, p.prokind FROM pg_proc p WHERE p.pronamespace = ${ns} ` +
428
+ `AND p.proowner <> ${r}::regrole AND ${notExt('pg_proc', 'p.oid')} LOOP ` +
429
+ `EXECUTE format($f$ALTER %s %s OWNER TO %I$f$, CASE o.prokind WHEN ${k('f')} THEN ${t('FUNCTION')} WHEN ${k('w')} THEN ${t('FUNCTION')} WHEN ${k('a')} THEN ${t('AGGREGATE')} ELSE ${t('PROCEDURE')} END, o.obj, ${r}); END LOOP; ` +
430
+ // free-standing types: enums, domains, ranges, and composite types made by CREATE TYPE
431
+ `FOR o IN SELECT y.oid::regtype AS obj, y.typtype FROM pg_type y WHERE y.typnamespace = ${ns} AND y.typowner <> ${r}::regrole ` +
432
+ `AND (y.typtype = ANY (${anyOf('e,d,r')}) OR (y.typtype = ${k('c')} AND EXISTS (SELECT 1 FROM pg_class c WHERE c.oid = y.typrelid AND c.relkind = ${k('c')}))) ` +
433
+ `AND ${notExt('pg_type', 'y.oid')} LOOP ` +
434
+ `EXECUTE format($f$ALTER %s %s OWNER TO %I$f$, CASE o.typtype WHEN ${k('d')} THEN ${t('DOMAIN')} ELSE ${t('TYPE')} END, o.obj, ${r}); END LOOP; ` +
435
+ `END $own$;`;
436
+ }
437
+
438
+ // The ownership handover as a command (task 1004735): provisioning runs it after the grants,
439
+ // and the migrate_not_owner remedy runs it for a project provisioned before that. PURE.
440
+ function dbOwnershipCmd(inst, { privileged = false } = {}) {
441
+ return asPostgres(`psql -v ON_ERROR_STOP=1 -d ${dbName(inst)} -c '${dbOwnershipSql(instanceDbRole(inst))}'`, privileged);
442
+ }
443
+
444
+ // ── STANDALONE scaffold (ADR 0108 / 0125, task 2055) ──────────────────────────
445
+ // The MISSING first leg of the greenfield "one guided flow": provision.js used to
446
+ // THROW if the instance repo wasn't already cloned. These generate + run the scaffold
447
+ // that clones an EMPTY target_ref and turns it into an installable, bootable instance.
448
+
449
+ // parseTargetRef + instanceRepoRemote (and resolveOwnerGithubToken, below the scaffold)
450
+ // now live in the ./provision-repo-auth.js leaf and are re-exported unchanged from here —
451
+ // upgrade.js needs them and is inside the merge resolver's executed closure, which a require of
452
+ // this file would have widened by five (task 1003081; see that module's header).
453
+
454
+ // A minimal, VALID `bongos init` spec (init.validateSpec greenfield) from the
455
+ // provisioning row + the owner's github login. The scaffold produces a bootable,
456
+ // NEUTRAL instance; the owner brands + scopes it later via init's kickoff tasks.
457
+ function instanceInitSpec(inst, { ownerLogin } = {}) {
458
+ const repo = parseTargetRef(inst.target_ref);
459
+ return {
460
+ identity: { productName: inst.slug },
461
+ repo: repo || { owner: ownerLogin || 'owner', name: inst.slug },
462
+ firstAdmin: ownerLogin || (repo && repo.owner) || 'owner',
463
+ currency: { label: 'credits' },
464
+ domains: { publicOrigin: inst.domain ? `https://${inst.domain}` : `http://localhost:${inst.port || 3000}` },
465
+ modules: [],
466
+ version: { id: 'V1', name: 'V1', done_when: 'A task goes claim → grade → ship.' },
467
+ };
468
+ }
469
+
470
+ // The .gitignore a scaffolded instance repo carries: keep node_modules + the local
471
+ // dist/ + secrets OUT of the commit, while the vendored core tarball (vendor/*.tgz)
472
+ // and package-lock.json stay IN — `npm ci` on deploy resolves the pinned core from
473
+ // the committed tarball (ADR 0108), so they MUST be tracked.
474
+ //
475
+ // docs/session-log-index.md is a WHOLE-FILE generated navigation doc (ADR 0110): every byte is
476
+ // machine-owned, regenerated at deploy by regen-instance-docs.js (task 1002314) into the served
477
+ // tree. Untracked so `git reset --hard origin/main` never wipes it and no branch can diff it —
478
+ // the same reasoning the core's own .gitignore applies to it. (docs/repo-map.md is listed for
479
+ // parity/forward-compat; it is core-scoped today so a standalone instance doesn't generate its
480
+ // own — see regenerateNavDocs() in upgrade.js — but ignoring it costs nothing and prevents a
481
+ // stray future write from entering the tree.)
482
+ const INSTANCE_GITIGNORE = [
483
+ 'node_modules/', 'dist/', '.env', '.env.local', '*.log',
484
+ '# Whole-file generated navigation docs — regenerated at deploy (ADR 0110, task 1002314).',
485
+ 'docs/session-log-index.md', 'docs/repo-map.md', '',
486
+ ].join('\n');
487
+
488
+ // A PRE-BUILT @bongos/core tarball this control plane can vendor into a NEW instance
489
+ // WITHOUT rebuilding from source — the consumer-control-plane fix (task 1002275).
490
+ //
491
+ // The scaffold's default core-install path rebuilds the tarball with package-core.js,
492
+ // which enumerates GIT-TRACKED files (`git -C REPO_ROOT ls-tree`). That works from a core
493
+ // SOURCE checkout, but NOT when this runner is ITSELF a consumer instance whose core is an
494
+ // installed npm dependency: provision.js then runs out of node_modules/@bongos/core, so
495
+ // REPO_ROOT points INSIDE gitignored node_modules, git sees ZERO tracked files there, and
496
+ // package-core aborts ("the core set is EMPTY") — the scaffold never pushes and the freshly
497
+ // created repo is left EMPTY (seen live: trial/test on cloudbongos.com).
498
+ //
499
+ // But the exact pinned tarball this instance installed FROM is on disk, named by its own
500
+ // package.json ("@bongos/core": "file:vendor/bongos-core-<v>.tgz"). Reuse it: byte-identical,
501
+ // already proven, and the child pins the SAME core the hub runs. Returns an absolute .tgz
502
+ // path, or null on a source checkout (no `file:` core pin) — where package-core.js is the
503
+ // right, unchanged path. `instanceRoot` defaults to $BONGOS_INSTANCE_ROOT (what
504
+ // infra/core-node.sh resolves against) else process.cwd() (the runner's WorkingDirectory).
505
+ // fs injectable for tests. PURE-ish (fs reads only, no mutation → dry-run safe).
506
+ function installedCoreTarball({ instanceRoot, fsImpl = fs } = {}) {
507
+ const root = instanceRoot || process.env.BONGOS_INSTANCE_ROOT || process.cwd();
508
+ let pkg;
509
+ try { pkg = JSON.parse(fsImpl.readFileSync(path.join(root, 'package.json'), 'utf8')); }
510
+ catch { return null; } // no/unreadable package.json → treat as a source checkout
511
+ const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
512
+ // Both scope aliases the loader accepts (infra/core-node.sh checks the same two).
513
+ for (const name of ['@bongos/core', '@cloudbongos/core']) {
514
+ const m = /^file:(.+\.tgz)$/.exec(String(deps[name] || '').trim());
515
+ if (m) {
516
+ const abs = path.resolve(root, m[1]);
517
+ if (fsImpl.existsSync(abs)) return abs; // a dangling pin (tarball pruned) → fall through to null
518
+ }
519
+ }
520
+ return null;
521
+ }
522
+
523
+ // The REGISTRY-pinned sibling of installedCoreTarball (task 1002706). After a control
524
+ // plane moves its own core pin off the vendored tarball onto the npm registry (the
525
+ // cloudbongos.com cutover, task 1002621), its package.json holds a semver pin — no
526
+ // `file:` — so installedCoreTarball returns null and the scaffold would fall into the
527
+ // package-core.js source rebuild, which ABORTS on a consumer instance (the empty-set
528
+ // abort of task 1002275, resurrected by the cutover). The installed package itself is
529
+ // still the exact core this hub runs: return its node_modules dir + version + the
530
+ // `npm pack` tarball name so the scaffold can pack it into a vendorable .tgz. The child
531
+ // stays file:-pinned — its owner needs no npm token. (That was originally because the
532
+ // registry package was PRIVATE; since ADR 0283 it is public and a token would buy
533
+ // nothing either way. The file: pin is kept for the reason in ADR 0168 — a child is born
534
+ // on the core its parent actually runs — not for access control.) Returns { dir, version, tarballName } or null
535
+ // (a file: pin, an uninstalled dep, or a source checkout). fs reads only → dry-run safe.
536
+ function installedCorePackDir({ instanceRoot, fsImpl = fs } = {}) {
537
+ const root = instanceRoot || process.env.BONGOS_INSTANCE_ROOT || process.cwd();
538
+ let pkg;
539
+ try { pkg = JSON.parse(fsImpl.readFileSync(path.join(root, 'package.json'), 'utf8')); }
540
+ catch { return null; } // no/unreadable package.json → a source checkout
541
+ const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
542
+ for (const name of ['@bongos/core', '@cloudbongos/core']) {
543
+ const spec = String(deps[name] || '').trim();
544
+ if (!spec || spec.startsWith('file:')) continue; // file: pins belong to installedCoreTarball
545
+ const dir = path.join(root, 'node_modules', ...name.split('/'));
546
+ try {
547
+ const installed = JSON.parse(fsImpl.readFileSync(path.join(dir, 'package.json'), 'utf8'));
548
+ const version = installed && installed.version ? String(installed.version) : null;
549
+ if (!version) continue;
550
+ // npm pack names a scoped tarball <scope>-<name>-<version>.tgz (no @, / → -).
551
+ const base = String(installed.name || name).replace(/^@/, '').replace(/\//g, '-');
552
+ return { dir, version, tarballName: `${base}-${version}.tgz` };
553
+ } catch { /* pinned but not installed → fall through */ }
554
+ }
555
+ return null;
556
+ }
557
+
558
+ // Can package-core.js enumerate this checkout from GIT? It builds the core set from
559
+ // `git ls-tree` at a ref, so a checkout with no `.git` aborts at `git rev-parse HEAD` —
560
+ // and that is not a broken install, it is the SHIPPED SELF-HOST IMAGE: .dockerignore
561
+ // excludes .git by design, so /app is a complete core source tree with no history
562
+ // (task 1003995). The presence of `.git` — a directory in a clone, a file in a linked
563
+ // worktree — is the whole question. fs injectable for tests; read-only → dry-run safe.
564
+ function coreCheckoutHasGit({ repoRoot = REPO_ROOT, fsImpl = fs } = {}) {
565
+ return fsImpl.existsSync(path.join(repoRoot, '.git'));
566
+ }
567
+
568
+ // The core version THIS control plane runs — what the ladder below would vend: the
569
+ // pinned tarball's version, else the registry-installed package's, else the checkout's
570
+ // CORE_VERSION. fs reads only → dry-run safe.
571
+ function runningCoreVersion({ instanceRoot, coreV }) {
572
+ const pinned = installedCoreTarball({ instanceRoot });
573
+ if (pinned) return require('./upgrade').versionFromTgzPath(path.basename(pinned)) || coreV;
574
+ const packSrc = installedCorePackDir({ instanceRoot });
575
+ return packSrc ? packSrc.version : coreV;
576
+ }
577
+
578
+ // Which core a hosted project is given (task 1004299, ADR 0361 D5). PURE. Since merges
579
+ // publish as candidates, the platform hall runs ahead of the registry's `latest` label by
580
+ // design — and the ladder below vends whatever the hall runs, so every new project would
581
+ // be born on a version nobody released. When the hall is AHEAD of the release, the project
582
+ // gets the release instead. Equal → the running core, byte-exact, as before.
583
+ //
584
+ // BEHIND the release is real too (task 1004556): the runner that scaffolds is not always
585
+ // the platform hall. On cloudbongos.com it ran from a patch-channel install that could not
586
+ // cross the 1.21.0 minor, so it sat on 1.20.82 and every new project was born there,
587
+ // without founding mode (1.21+). The release is what ADR 0361 D5 promises a new project,
588
+ // and a released core has already run on the platform hall, so the runner's lag must not
589
+ // set the project's version: behind → the release, packed from the registry, too.
590
+ //
591
+ // No readable label → the running core, deliberately (fail-open). Until candidates are
592
+ // armed the running core IS the release; vendoring is the path that works with no
593
+ // registry at all (ADR 0292); and refusing project creation over a registry blip is worse
594
+ // for the person creating it than a newer-than-released core in the rare outage window.
595
+ function projectCoreDecision({ running, released }) {
596
+ if (!released || !isStable(released)) return { version: running, source: 'running', reason: 'no readable released (latest) label — using the core this hall runs' };
597
+ if (compareSemver(running, released) > 0) return { version: released, source: 'released', reason: `this hall runs ${running}, ahead of the release` };
598
+ if (compareSemver(running, released) < 0) return { version: released, source: 'released', reason: `this runner is on ${running}, behind the release` };
599
+ return { version: running, source: 'running', reason: `this hall runs the released core ${released}` };
600
+ }
601
+
602
+ // The I/O half: read the `latest` label through the injected exec (so a dry run and the
603
+ // test harness's stub exec never touch the network), then decide. `npm view` is fine
604
+ // here — provisioning is a runner script, not a request handler (ADR 0293's objection).
605
+ function projectCoreTarget({ instanceRoot, coreV, exec, log, tag = '[core]' }) {
606
+ const running = runningCoreVersion({ instanceRoot, coreV });
607
+ const res = exec(`npm view ${CORE_PKG} dist-tags.latest`, { allowFail: true });
608
+ const label = String((res && res.ok !== false && res.stdout) || '').trim();
609
+ const decision = projectCoreDecision({ running, released: label || null });
610
+ log(` ${tag} ${decision.source === 'released' ? `giving the project the RELEASED core ${decision.version}` : `giving the project core ${decision.version}`} — ${decision.reason} (task 1004299)`);
611
+ return decision;
612
+ }
613
+
614
+ // Resolve a vendorable @bongos/core tarball — the four-rung ladder, freshest-correct
615
+ // first, shared by the fresh scaffold and the re-provision pin refresh (task 1003050).
616
+ // A `target` from projectCoreTarget with source 'released' short-circuits it: the
617
+ // project gets the released version packed from the registry, not this hall's core.
618
+ // 1. a `file:`-pinned control plane reuses its own pinned tarball (task 1002275 —
619
+ // rebuilding via package-core.js aborts with an EMPTY core set when this runner
620
+ // IS a consumer instance, leaving new repos empty);
621
+ // 2. a REGISTRY-pinned consumer (the post-1002621 mothership shape) packs the core
622
+ // it has INSTALLED — the child pins the SAME, CURRENT core the hub runs, ending
623
+ // the frozen-old-tarball births (task 1002706, gap 13) without requiring the
624
+ // child's owner to hold a token for the still-private registry package;
625
+ // 3. a genuine core source checkout rebuilds fresh via package-core.js;
626
+ // 4. a core checkout with NO git history — the self-host image — packs its working
627
+ // tree via package-core.js --from-worktree (task 1003995). LAST because it is the
628
+ // only rung with no commit to anchor the artifact to: whenever history is there,
629
+ // rung 3's reproducible build is the better answer for the identical file set.
630
+ // Returns { tgz, packDest } — packDest (set only on rung 2) is a temp dir the CALLER
631
+ // must `rm -rf` once the tarball has been vendored.
632
+ function resolveVendorableCoreTarball({ instanceRoot, repoRoot = REPO_ROOT, exec, log, coreV, tag = '[core]', target = null }) {
633
+ if (target && target.source === 'released') {
634
+ // target.version passed isStable (digits and dots only), so it is shell-safe.
635
+ const v = target.version;
636
+ const packDest = path.join(os.tmpdir(), `bongos-core-released-${v}`);
637
+ log(` ${tag} packing the released core ${v} from the registry — not the candidate this hall runs (task 1004299)`);
638
+ exec(`mkdir -p ${packDest}`);
639
+ exec(`npm pack ${CORE_PKG}@${v} --pack-destination ${packDest}`);
640
+ return { tgz: path.join(packDest, `bongos-core-${v}.tgz`), packDest };
641
+ }
642
+ const pinned = installedCoreTarball({ instanceRoot });
643
+ if (pinned) {
644
+ log(` ${tag} reusing the pinned core tarball this control plane runs (${path.basename(pinned)}) — no source rebuild (task 1002275)`);
645
+ return { tgz: pinned, packDest: null };
646
+ }
647
+ const packSrc = installedCorePackDir({ instanceRoot });
648
+ if (packSrc) {
649
+ // Keyed by VERSION, not slug: no request-derived value enters this command, and
650
+ // two concurrent scaffolds of the same core pack identical bytes — collision-safe.
651
+ const packDest = path.join(os.tmpdir(), `bongos-core-pack-${packSrc.version}`);
652
+ log(` ${tag} packing the registry-installed core this control plane runs (v${packSrc.version}) — no source rebuild (task 1002706)`);
653
+ exec(`mkdir -p ${packDest}`);
654
+ exec(`npm pack ${packSrc.dir} --pack-destination ${packDest}`);
655
+ return { tgz: path.join(packDest, packSrc.tarballName), packDest };
656
+ }
657
+ if (coreCheckoutHasGit({ repoRoot })) {
658
+ log(` ${tag} building a fresh vendored tarball from source (package-core.js)`);
659
+ exec(`node scripts/bongos/package-core.js`); // cwd = REPO_ROOT → dist/bongos-core-<v>.tgz
660
+ } else {
661
+ log(` ${tag} this checkout IS the core and carries no git history — packing the working tree (package-core.js --from-worktree, task 1003995)`);
662
+ exec(`node scripts/bongos/package-core.js --from-worktree`);
663
+ }
664
+ return { tgz: path.join(repoRoot, 'dist', `bongos-core-${coreV}.tgz`), packDest: null };
665
+ }
666
+
667
+ // The onboard mode a provisioning row carries (migration provisioning_004, task 1002562).
668
+ // 'adopt' means target_ref is a repo the owner ALREADY has, so the scaffold must LAYER
669
+ // onto its history. Anything else — including a row written before the column existed, or
670
+ // an unrecognized value — resolves to 'greenfield', deliberately the SAFE direction:
671
+ // greenfield pushes a fresh tree and (carrying no --force) is REJECTED by a repo with
672
+ // history, whereas wrongly choosing 'adopt' would start rewriting a repo nobody declared.
673
+ function onboardMode(inst) {
674
+ return String((inst && inst.onboard_mode) || '') === 'adopt' ? 'adopt' : 'greenfield';
675
+ }
676
+
677
+ // Has the scaffold already completed in `root`? The marker is MODE-DEPENDENT, and reusing
678
+ // the greenfield one for adopt is a real bug: greenfield's marker is "package.json exists"
679
+ // (a fresh empty repo has none until we scaffold it), but an adopted repo is an existing
680
+ // project that almost certainly HAS a package.json already — so the greenfield check would
681
+ // report "already scaffolded" on the very first run and the instance would never become
682
+ // bootable. For adopt the honest marker is the thing the layering itself contributes: a
683
+ // @bongos/core dependency (both scope aliases the loader accepts, matching
684
+ // installedCoreTarball). fs injectable for tests; read-only, so dry-run safe.
685
+ function alreadyScaffolded(root, mode, { fsImpl = fs } = {}) {
686
+ const pkgPath = path.join(root, 'package.json');
687
+ if (!fsImpl.existsSync(pkgPath)) return false;
688
+ if (mode !== 'adopt') return true;
689
+ let pkg;
690
+ try { pkg = JSON.parse(fsImpl.readFileSync(pkgPath, 'utf8')); }
691
+ catch { return false; } // unparseable → let the adopt layer report it, don't skip
692
+ const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
693
+ return !!(deps['@bongos/core'] || deps['@cloudbongos/core']);
694
+ }
695
+
696
+ // Materialize + push the instance's own repo. TWO paths, chosen by onboard_mode:
697
+ //
698
+ // greenfield — the target_ref repo is EMPTY (github-repos.createUserRepo makes it with
699
+ // auto_init:false), so there is nothing to clone: `git init` locally,
700
+ // materialize, and push the first commit at main.
701
+ // adopt — the target_ref repo is the owner's OWN existing project. CLONE it (so its
702
+ // history and files come along), LAYER the instance config additively via
703
+ // `bongos init --adopt --accept` (which never overwrites a pre-existing
704
+ // file), and push the layer commit as an ordinary fast-forward on top.
705
+ //
706
+ // Both end at a repo carrying config/ + .claude/ + a file:-pinned @bongos/core + lockfile,
707
+ // which is what makes it `npm ci --omit=dev`-installable by the rest of provisioning.
708
+ //
709
+ // NEITHER PATH EVER USES --force. That is a safety property, not an oversight: since ADR
710
+ // 0155 the credential here can write to every repo the owner owns, so a force-push would
711
+ // be a route to destroying a user's work. A rejected push is the correct, recoverable
712
+ // outcome. Asserted by tests.
713
+ //
714
+ // Idempotent (see alreadyScaffolded) and dry-run safe (every mutation goes through the
715
+ // injected exec/writeFile). ADR 0108 / 0121 / 0125.
716
+ // The signed-in builder who owns the instance — the founding firstAdmin. Best-effort:
717
+ // no db, no row, or a query error → null, and callers fall back to the repo owner in
718
+ // target_ref. Shared by the scaffold's branding.firstAdmin and web.env's
719
+ // <prefix>_FIRST_ADMIN (task 1003240) so the two can never disagree.
720
+ async function ownerLoginOf(inst, db) {
721
+ if (!inst || !inst.owner_builder_id || !db || typeof db.query !== 'function') return null;
722
+ try {
723
+ const r = await db.query('SELECT github_login FROM builders WHERE id = $1', [inst.owner_builder_id]);
724
+ return r && r.rows && r.rows[0] && r.rows[0].github_login ? r.rows[0].github_login : null;
725
+ } catch { return null; }
726
+ }
727
+
728
+ async function scaffoldStandaloneRepo(inst, deps) {
729
+ // The scaffold BUILDS + pushes the instance's repo (git clone, package-core,
730
+ // `bongos init`, git push) — control-plane work that needs the packaging tools +
731
+ // the GitHub deploy key, so it runs LOCALLY even when box-mutating steps go
732
+ // remote (task 2065). Falls back to exec/writeFile when a caller passes no
733
+ // control executor (then all are the same local executor — unchanged behavior).
734
+ const { db, log } = deps;
735
+ const exec = deps.controlExec || deps.exec;
736
+ const writeFile = deps.controlWriteFile || deps.writeFile;
737
+ const root = standaloneRoot(inst);
738
+ const repo = parseTargetRef(inst.target_ref);
739
+ const mode = onboardMode(inst);
740
+ if (!repo) {
741
+ throw new Error(`standalone instance ${inst.slug}: target_ref "${inst.target_ref || '(unset)'}" is not an owner/repo — set it (e.g. <owner>/${inst.slug}) so the scaffold knows which repo to clone + push.`);
742
+ }
743
+ if (alreadyScaffolded(root, mode)) {
744
+ const resumed = deps.apply ? await resumeUnpushedScaffold(inst, deps, { root, repo, mode }) : null;
745
+ if (resumed) return resumed;
746
+ log(` [scaffold] ${root} already scaffolded (mode=${mode}) — skipping`);
747
+ return { already: true };
748
+ }
749
+ const coreV = coreVersionSafe();
750
+ if (!coreV) {
751
+ throw new Error('cannot resolve the core version (src/module-api CORE_VERSION) — run the scaffold from the control-plane checkout.');
752
+ }
753
+ const remote = instanceRepoRemote(repo);
754
+ // The released core, not this hall's candidate (task 1004299).
755
+ const target = projectCoreTarget({ instanceRoot: deps.instanceRoot, coreV, exec, log, tag: '[scaffold] core:' });
756
+ const pinV = target.version;
757
+ log(mode === 'adopt'
758
+ ? ` [scaffold] ${repo.owner}/${repo.name} → ${root} (ADOPT: clone → layer additively → pin core ${pinV} → commit → fast-forward push)`
759
+ : ` [scaffold] ${repo.owner}/${repo.name} → ${root} (clone → materialize → pin core ${pinV} → commit → push)`);
760
+
761
+ // Owner login = the founding firstAdmin. Best-effort; the spec falls back to the repo owner.
762
+ const ownerLogin = await ownerLoginOf(inst, db);
763
+
764
+ // Resolve the instance owner's OAuth token BEFORE any git network op.
765
+ //
766
+ // Greenfield only needed it at PUSH time, so the lookup used to live down beside the
767
+ // push. Adopt needs it EARLIER: its first act is to clone the owner's own repo, and a
768
+ // PRIVATE one is unreadable by the control plane's shared deploy key (a deploy key is
769
+ // per-repo). Since ADR 0155 this token carries the `repo` scope, so it can both read and
770
+ // write the owner's private repos — which is what makes adopt possible at all.
771
+ //
772
+ // A GitHub DEPLOY KEY is per-repo, so the box's shared key also can't WRITE to a new
773
+ // project repo — the second empty-repo cause (task 1002317) — hence preferring this
774
+ // token on both paths. It rides a chmod-600 git credential-store file, never the logged
775
+ // command or makeExec's env (makeExec surfaces both), and is removed in a `finally` so a
776
+ // failed git op can't leave it on disk. No token (org-owned repo, or secret-box off) →
777
+ // fall back to the SSH `origin` remote.
778
+ const cred = await scaffoldCredential(inst, repo, deps, writeFile);
779
+ const { credFile, httpsUrl, credOpt } = cred;
780
+
781
+ try {
782
+ // 1. Materialize the working tree.
783
+ //
784
+ // ADOPT — CLONE the owner's existing repo, so its history + files come along and the
785
+ // layer commit lands as an ordinary descendant of its current tip. This is the whole
786
+ // point: `git init`ing a fresh tree here produces an UNRELATED history, which the
787
+ // remote rejects as a non-fast-forward (and, were it ever forced, would erase the
788
+ // owner's work). Cloning over the token's HTTPS URL also covers the private case the
789
+ // shared deploy key cannot read.
790
+ //
791
+ // GREENFIELD — a freshly-created instance repo is EMPTY (github-repos.createUserRepo,
792
+ // auto_init:false), so there is NOTHING to clone; and the shared PROVISION_GIT_SSH_ALIAS
793
+ // deploy key can't even READ a consumer's brand-new repo under their own account
794
+ // (`git clone` fails "Repository not found" — the clone-side twin of the task-1002317
795
+ // push bug). So `git init` locally and set `origin` to the SSH remote for the no-token
796
+ // fallback push. Unchanged behavior.
797
+ //
798
+ // Either way a re-run after a partial attempt reuses the existing dir.
799
+ const hasGit = fs.existsSync(path.join(root, '.git'));
800
+ if (mode === 'adopt') {
801
+ if (!hasGit) {
802
+ exec(`mkdir -p ${root}`);
803
+ exec(`git ${credOpt}clone ${credFile ? httpsUrl : remote} ${root}`);
804
+ // Normalize `origin` to the SSH remote, matching what the greenfield path leaves
805
+ // behind. Two reasons: the clone URL would otherwise persist as origin, and the
806
+ // ongoing deploy (standalonePullCmd → `git pull --ff-only origin main`, and the box
807
+ // timer's `git reset --hard origin/main`) reads origin — it must be a form that can
808
+ // authenticate durably, which the one-shot token URL cannot (its credential file is
809
+ // deleted below). Nothing durable ever holds the token either way; the pushes here
810
+ // always pass an explicit URL.
811
+ if (credFile) exec(`git -C ${root} remote set-url origin ${remote}`);
812
+ } else {
813
+ exec(`git -C ${root} ${credOpt}fetch ${credFile ? httpsUrl : 'origin'} main`);
814
+ }
815
+ // The rest of provisioning is main-only (standalonePullCmd is `git pull --ff-only
816
+ // origin main`, and the box's deploy timer does `git reset --hard origin/main`), so a
817
+ // repo whose default branch is not `main` would be layered and pushed somewhere the
818
+ // deploy never reads. Fail loudly with the fix rather than silently half-working.
819
+ const head = exec(`git -C ${root} rev-parse --abbrev-ref HEAD`, { allowFail: true });
820
+ const branch = String((head && head.stdout) || '').trim();
821
+ if (branch && branch !== 'main' && branch !== 'HEAD') {
822
+ throw new Error(`standalone instance ${inst.slug}: adopting ${repo.owner}/${repo.name} but its default branch is "${branch}", not "main". The instance deploy only ever reads origin/main, so the layer would never be served. Rename the default branch to main (or point target_ref at a repo whose default is main) and re-run.`);
823
+ }
824
+ exec(`git -C ${root} checkout main`, { allowFail: true });
825
+ } else {
826
+ if (!hasGit) {
827
+ exec(`mkdir -p ${root}`);
828
+ exec(`git -C ${root} init -q`);
829
+ exec(`git -C ${root} remote add origin ${remote}`);
830
+ }
831
+ exec(`git -C ${root} checkout -B main`);
832
+ }
833
+
834
+ // Core-install path: vendor a tarball built from THIS control-plane checkout
835
+ // (offline, proven — task 2053).
836
+ //
837
+ // This used to be a fork: an opt-in env var selected the registry, and a missing npm
838
+ // credential silently failed it soft back to vendored. Both halves were auth-era and both
839
+ // are gone (task 1003878) — @bongos/core is public, so nothing needs a credential, and an
840
+ // env var that silently changed which install path ran is the divergence behind task
841
+ // 1003520. tests/provision.mjs pins both retired names and proves they are inert.
842
+ //
843
+ // Vendored stays the default here for a reason that has nothing to do with auth and
844
+ // survives the package going public: THIS CHECKOUT IS ROUTINELY AHEAD OF THE REGISTRY.
845
+ // A control plane on an unreleased CORE_VERSION has no published version to install, so
846
+ // the registry path would 404 on exactly the commits an operator most wants to stand up.
847
+ // Whether the vendored path still earns its keep at all is gate 6's call (task 1003879).
848
+
849
+ // materialize config/ + .claude/ + a pinned package.json + lockfile via `bongos init`.
850
+ // --no-seed: the instance's own DB is seeded by migrate, not here (and on the adopt path
851
+ // the REAL-state seeding + backlog import happen when the owner runs `bongos init --adopt`
852
+ // themselves, against the live instance — the step the wizard walks them through).
853
+ //
854
+ // MODE FLAGS. Greenfield passes --force (overwrite is safe on an empty repo). Adopt passes
855
+ // --adopt --accept instead and NEVER --force: --adopt takes init's layering path, which
856
+ // writes each config only where absent and leaves pre-existing files untouched, and
857
+ // --accept clears the interactive pre-flight gate for this non-interactive runner (a
858
+ // BLOCKING pre-flight finding still hard-stops — it cannot be waved through). init refuses
859
+ // --adopt together with --force, so the two flag sets are mutually exclusive by design.
860
+ //
861
+ // Core resolution is vendored (--vendor-core): a tarball built from this checkout,
862
+ // file:-pinned. init still accepts --registry-core for an operator who explicitly wants
863
+ // a published version, and that flag no longer needs any credential.
864
+ const modeFlags = mode === 'adopt' ? '--adopt --accept' : '--force';
865
+ const spec = instanceInitSpec(inst, { ownerLogin });
866
+ const specPath = path.join(os.tmpdir(), `bongos-init-${inst.slug}.json`);
867
+ writeFile(specPath, JSON.stringify(spec, null, 2));
868
+ // Vendored tarball — resolveVendorableCoreTarball's four-rung ladder (shared
869
+ // with the re-provision pin refresh, task 1003050).
870
+ const { tgz, packDest } = resolveVendorableCoreTarball({
871
+ instanceRoot: deps.instanceRoot, exec, log, coreV, tag: '[scaffold] core install:', target,
872
+ });
873
+ exec(`node scripts/bongos/init.js --from ${specPath} --dir ${root} --no-seed ${modeFlags} --vendor-core ${tgz}`);
874
+ // init --vendor-core COPIED the tarball into the child's vendor/ — the temp pack dir is spent.
875
+ if (packDest) exec(`rm -rf ${packDest}`);
876
+
877
+ // 4. commit + push to the instance's own main (keep node_modules out).
878
+ //
879
+ // .gitignore: greenfield OWNS the file. Adopt must not clobber the owner's existing
880
+ // ignores, so it only writes one where none exists — and where one does, it APPENDS the
881
+ // entries we actually require (node_modules/, the vendored-core exceptions, the generated
882
+ // nav docs) if they are missing, since `npm ci` + `git reset --hard` on the box depend on
883
+ // them. Duplicate lines in a .gitignore are harmless, so the append is idempotent-safe.
884
+ const giPath = path.join(root, '.gitignore');
885
+ if (mode === 'adopt' && fs.existsSync(giPath)) {
886
+ const existing = fs.readFileSync(giPath, 'utf8');
887
+ const missing = INSTANCE_GITIGNORE.split('\n')
888
+ .filter((l) => l.trim() && !l.startsWith('#'))
889
+ .filter((l) => !existing.split('\n').some((e) => e.trim() === l.trim()));
890
+ if (missing.length) {
891
+ writeFile(giPath, `${existing.replace(/\n*$/, '\n')}\n# Added by Cloud Bongos (adopt) — required by the instance deploy.\n${missing.join('\n')}\n`);
892
+ log(` [scaffold] appended ${missing.length} required .gitignore entr(ies) — existing rules left intact`);
893
+ } else {
894
+ log(' [scaffold] .gitignore already covers the required entries — left untouched');
895
+ }
896
+ } else {
897
+ writeFile(giPath, INSTANCE_GITIGNORE);
898
+ }
899
+ // Adopt layers config only where ABSENT, so an owner's existing config/branding.json
900
+ // keeps whatever envPrefix it carried — and web.env is written under the platform's.
901
+ // Mercury's said CLOUDBONGOS: it read none of its credentials or settings (task 1004573).
902
+ // Rewrite just that one value, in this same commit, so the repo and web.env agree.
903
+ const packPath = path.join(root, 'config', 'branding.json');
904
+ if (mode === 'adopt' && fs.existsSync(packPath)) {
905
+ const fixed = normalizePackEnvPrefix(fs.readFileSync(packPath, 'utf8'));
906
+ if (fixed) {
907
+ writeFile(packPath, fixed.text);
908
+ log(` [scaffold] config/branding.json envPrefix ${fixed.from} → the platform's, so the project reads the settings it is given`);
909
+ }
910
+ }
911
+ exec(`git -C ${root} config user.name "${provisionerBotName()}"`);
912
+ exec(`git -C ${root} config user.email "${provisionerBotEmail()}"`);
913
+ exec(`git -C ${root} add -A`);
914
+ const commitMsg = mode === 'adopt'
915
+ ? `chore: adopt Cloud Bongos onto ${inst.slug} on a pinned @bongos/core ${pinV} (provision.js, ADR 0121)`
916
+ : `chore: scaffold ${inst.slug} on a pinned @bongos/core ${pinV} (provision.js, ADR 0108)`;
917
+ exec(`git -C ${root} commit -m "${commitMsg}"`, { allowFail: true });
918
+
919
+ // Push. NO --force on either path, ever (see the function header): the ADR 0155 `repo`
920
+ // credential can write to every repo the owner owns, so a force here would be a route to
921
+ // destroying their work. Adopt's commit is a descendant of the tip it cloned, so an
922
+ // ordinary push fast-forwards; if the remote moved underneath us the rejection is the
923
+ // correct outcome and a re-run picks up the new tip.
924
+ pushScaffold({ exec, log, root, repo, mode, cred });
925
+ return { ok: true, scaffolded: true, mode };
926
+ } finally {
927
+ // Always remove the credential file — a failed clone/push must not leave it on disk.
928
+ if (credFile) exec(`rm -f ${credFile}`, { allowFail: true });
929
+ }
930
+ }
931
+
932
+ // The owner-token credential the scaffold's git network ops ride (rationale at the call site in
933
+ // scaffoldStandaloneRepo). The URL carries only the `x-access-token` username, so it is safe in a
934
+ // logged argv. credFile null → no token → SSH origin fallback; the caller `rm -f`s it in a finally.
935
+ async function scaffoldCredential(inst, repo, deps, writeFile) {
936
+ const ownerToken = await resolveOwnerGithubToken(inst, deps, { tag: '[scaffold]' });
937
+ const credFile = ownerToken ? path.join(os.tmpdir(), `bongos-gitcred-${inst.slug}-${process.pid}`) : null;
938
+ const httpsUrl = `https://x-access-token@github.com/${repo.owner}/${repo.name}.git`;
939
+ const credOpt = credFile ? `-c credential.helper='store --file=${credFile}' ` : '';
940
+ if (credFile) writeFile(credFile, `https://x-access-token:${ownerToken}@github.com\n`, { mode: 0o600 });
941
+ return { credFile, httpsUrl, credOpt };
942
+ }
943
+
944
+ function pushScaffold({ exec, log, root, repo, mode, cred }) {
945
+ if (cred.credFile) {
946
+ exec(`git -C ${root} ${cred.credOpt}push ${cred.httpsUrl} HEAD:main`);
947
+ log(` [scaffold] ${mode === 'adopt' ? 'pushed the adopt layer' : 'pushed'} to ${repo.owner}/${repo.name}@main via the owner's GitHub token (task 1002317)`);
948
+ } else {
949
+ exec(`git -C ${root} push -u origin main`);
950
+ log(` [scaffold] pushed ${mode === 'adopt' ? 'the adopt layer' : 'initial scaffold'} to ${repo.owner}/${repo.name}@main (SSH origin)`);
951
+ }
952
+ }
953
+
954
+ // alreadyScaffolded is a LOCAL marker, and a scaffold whose push failed leaves it behind: the
955
+ // re-run skipped the never-done push, then wedged at step 2's `git pull --ff-only` against the
956
+ // still-empty remote (task 1002718, F12). Only a remote that ANSWERS with no main re-pushes — an
957
+ // unreachable one keeps the old skip, as it can't tell "never pushed" from "offline". Returns
958
+ // null when there is nothing to resume.
959
+ async function resumeUnpushedScaffold(inst, deps, { root, repo, mode }) {
960
+ const exec = deps.controlExec || deps.exec;
961
+ const writeFile = deps.controlWriteFile || deps.writeFile;
962
+ let cred = null;
963
+ let probe = { softFailed: true };
964
+ try {
965
+ // This runs on every re-provision of a LIVE instance, so a probe that cannot even start
966
+ // (token lookup, credential write) must keep the skip — never fail a healthy instance.
967
+ try {
968
+ cred = await scaffoldCredential(inst, repo, deps, writeFile);
969
+ probe = exec(`git -C ${root} ${cred.credOpt}ls-remote --heads ${cred.credFile ? cred.httpsUrl : 'origin'} main`, { allowFail: true });
970
+ } catch { deps.log(` [scaffold] could not probe ${repo.owner}/${repo.name} for main — keeping the skip`); }
971
+ if (probe.softFailed || String(probe.stdout || '').trim()) return null;
972
+ deps.log(` [scaffold] ${root} is scaffolded but ${repo.owner}/${repo.name} has no main — re-pushing the scaffold that never landed`);
973
+ pushScaffold({ exec, log: deps.log, root, repo, mode, cred });
974
+ return { ok: true, resumed: true, mode };
975
+ } finally {
976
+ if (cred && cred.credFile) exec(`rm -f ${cred.credFile}`, { allowFail: true });
977
+ }
978
+ }
979
+
980
+ // ── private-repo deploy key (ADR 0176, task 1002735; walk finding N4) ────────────
981
+
982
+ function deployKeyTitle(inst) { return `cloudbongos-deploy-${inst.slug}`; }
983
+ // Beside the checkout's own metadata: untracked by definition, survives the deploy's
984
+ // `git reset --hard`, and dies with the checkout rather than accreting in ~/.ssh.
985
+ function deployKeyPath(inst) { return path.posix.join(standaloneRoot(inst), '.git', 'cloudbongos-deploy-key'); }
986
+
987
+ // Make a PRIVATE target_ref durably readable by the recurring deploy pull. The
988
+ // owner's token covers only the ONE-SHOT clone/push (its credential file is deleted
989
+ // after the scaffold), while `origin` rides the shared SSH alias — which GitHub
990
+ // answers "Repository not found" for a private repo it isn't registered on. That
991
+ // mismatch is walk finding N4: every private standup failed at `git pull --ff-only
992
+ // origin main`, token or not (seen live: mercury, 2026-08-17).
993
+ //
994
+ // The fix (ADR 0176): mint the instance its OWN ed25519 deploy key, register it
995
+ // READ-ONLY on the repo with the owner's token (idempotent by title; a stale
996
+ // same-title key is replaced), then wire the CHECKOUT — origin re-pointed at plain
997
+ // github.com and core.sshCommand pinned to the key with -F /dev/null so no
998
+ // ~/.ssh/config identity can shadow it. Public repos return untouched; no token →
999
+ // skip (the request-time refusal is task 1002909's preflight). Dry-run plans only.
1000
+ //
1001
+ // `deps.intendedPrivate` (task 1004167) declares that the repo is ABOUT to be made
1002
+ // private, so the wiring runs before the flip rather than after it. Opt-in, and it
1003
+ // widens only WHEN we wire: the key stays read-only and repository ADMIN is still
1004
+ // required. It also HARDENS the two soft skips — no credential, GitHub unreachable —
1005
+ // into refusals, because a caller that has committed to flipping cannot be handed an
1006
+ // unwired checkout. Its one caller is the `repo-private` runner leg
1007
+ // (provision-repo-private.js, task 1004193); the owner-facing route that enqueues that
1008
+ // intent is what remains (split (c) of task 1003078).
1009
+ async function ensurePrivateRepoAccess(inst, deps) {
1010
+ const { log, apply } = deps;
1011
+ const exec = deps.controlExec || deps.exec;
1012
+ if (inst.hosting_shape !== 'cloud-host') return { skipped: 'shape' };
1013
+ const repo = parseTargetRef(inst.target_ref);
1014
+ if (!repo) return { skipped: 'no_repo' };
1015
+ // Dry-run stays OFFLINE like every other plan path here (the checkDnsTokenScope
1016
+ // preflight is apply-gated for the same reason): no token read, no GitHub call —
1017
+ // a plan run must stay a safe inspection tool for a snagged instance.
1018
+ if (!apply) {
1019
+ log(deps.intendedPrivate === true
1020
+ ? ` [repo-auth] would treat ${repo.owner}/${repo.name} as ABOUT TO BE MADE PRIVATE (pre-flip) and mint + register a read-only deploy key, wiring origin + core.sshCommand to it before the flip (ADR 0176)`
1021
+ : ` [repo-auth] would check ${repo.owner}/${repo.name} visibility with the owner's credential and, if PRIVATE, mint + register a read-only deploy key and wire origin + core.sshCommand to it (ADR 0176)`);
1022
+ return { planned: true };
1023
+ }
1024
+ const ownerToken = await resolveOwnerGithubToken(inst, deps);
1025
+ if (!ownerToken) {
1026
+ // With an intent declared the caller is ABOUT to flip, so a soft skip hands back
1027
+ // an unwired checkout and the flip then breaks the recurring pull for good. The
1028
+ // same missing credential is only a warning when nobody has committed to flipping.
1029
+ if (deps.intendedPrivate === true) {
1030
+ throw new Error(`${repo.owner}/${repo.name} cannot be made private: there is no usable GitHub credential stored for this project, and without one its deploy key cannot be registered — which would break the next deploy. Reconnect the project's GitHub access, then try again.`);
1031
+ }
1032
+ log(' [repo-auth] no usable owner GitHub credential (none stored, or it could not be read) — a PRIVATE repo would fail at the deploy pull (public repos are unaffected)');
1033
+ return { skipped: 'no_token' };
1034
+ }
1035
+ const ghRepos = deps.githubRepos || require(path.join(REPO_ROOT, 'src/bongos/github-repos'));
1036
+ let info;
1037
+ try {
1038
+ info = await ghRepos.getUserRepo(ownerToken, { owner: repo.owner, name: repo.name, fetchImpl: deps.fetchImpl });
1039
+ } catch (e) {
1040
+ if (e && e.code === 'repo_not_found') {
1041
+ // Plain sentence: this rides error_note onto the owner's project card.
1042
+ throw new Error(`repository not found: GitHub answered 404 for ${repo.owner}/${repo.name} even with your granted credential — the repository may have been renamed or deleted, or the credential lost access. Check it on GitHub, then try again.`);
1043
+ }
1044
+ // Same asymmetry as the missing credential above: soft when nothing is committed,
1045
+ // hard once the caller has declared the flip.
1046
+ if (deps.intendedPrivate === true) {
1047
+ throw new Error(`${repo.owner}/${repo.name} cannot be made private right now: GitHub could not be reached to set up its deploy key (${e && e.message}). Nothing has been changed — try again.`);
1048
+ }
1049
+ log(` [repo-auth] could not read ${repo.owner}/${repo.name} (${e && e.message}) — continuing; a PRIVATE repo would fail at the deploy pull`);
1050
+ return { skipped: 'github_unreachable' };
1051
+ }
1052
+
1053
+ const root = standaloneRoot(inst);
1054
+ const keyPath = deployKeyPath(inst);
1055
+ const title = deployKeyTitle(inst);
1056
+ const fsImpl = deps.fsImpl || fs;
1057
+
1058
+ // A flip-to-private must wire BEFORE it flips — a deploy key authenticates fine
1059
+ // against a still-public repo, so wiring first leaves no broken window, while
1060
+ // flipping first breaks the very next deploy pull. At that moment GitHub still
1061
+ // answers `private: false`, so the observed value is the wrong thing to branch on:
1062
+ // untouched, this takes the public path and (worse, if a key file exists) actively
1063
+ // UN-wires. `intendedPrivate` is the caller declaring where the repo is going.
1064
+ // Opt-in: absent, every path below is exactly what it was.
1065
+ const treatPrivate = info.private || deps.intendedPrivate === true;
1066
+
1067
+ if (!treatPrivate) {
1068
+ // Flipped private → public since we wired it: un-wire, or the checkout stays
1069
+ // pinned (IdentitiesOnly + -F /dev/null admit no other identity) to a key the
1070
+ // owner is now free to delete from their repo settings. Keyed on our key file
1071
+ // so the common public path stays zero-mutation.
1072
+ if (fsImpl.existsSync(keyPath)) {
1073
+ log(` [repo-auth] ${repo.owner}/${repo.name} is public again — un-wiring its deploy key`);
1074
+ try {
1075
+ const keys = await ghRepos.listDeployKeys(ownerToken, { owner: repo.owner, name: repo.name, fetchImpl: deps.fetchImpl });
1076
+ const ours = keys.find((k) => k.title === title);
1077
+ if (ours) await ghRepos.deleteDeployKey(ownerToken, { owner: repo.owner, name: repo.name, keyId: ours.id, fetchImpl: deps.fetchImpl });
1078
+ } catch (e) { log(` [repo-auth] (remote key revoke skipped: ${e && e.message})`); }
1079
+ exec(`git -C ${root} remote set-url origin ${instanceRepoRemote(repo)}`);
1080
+ exec(`git -C ${root} config --unset core.sshCommand`, { allowFail: true });
1081
+ exec(`rm -f ${keyPath} ${keyPath}.pub`, { allowFail: true });
1082
+ return { private: false, unwired: true };
1083
+ }
1084
+ return { private: false }; // the shared-alias path reads public repos as-is
1085
+ }
1086
+
1087
+ // Registering a deploy key needs repository ADMIN; getUserRepo already told us.
1088
+ // Refuse up front with the fix, instead of a raw 403 mid-standup.
1089
+ if (info.admin === false) {
1090
+ throw new Error(`repository access is not admin: your granted credential can read ${repo.owner}/${repo.name}, but registering its deploy key needs repository ADMIN. Ask a repository admin to raise your access (or make the repository public), then try again.`);
1091
+ }
1092
+
1093
+ log(info.private
1094
+ ? ` [repo-auth] ${repo.owner}/${repo.name} is PRIVATE — wiring its own read-only deploy key (ADR 0176)`
1095
+ : ` [repo-auth] ${repo.owner}/${repo.name} is ABOUT TO BE MADE PRIVATE — wiring its own read-only deploy key first (ADR 0176)`);
1096
+ if (!fsImpl.existsSync(keyPath) || !fsImpl.existsSync(`${keyPath}.pub`)) {
1097
+ exec(`rm -f ${keyPath} ${keyPath}.pub`, { allowFail: true });
1098
+ exec(`ssh-keygen -q -t ed25519 -N '' -C ${title} -f ${keyPath}`);
1099
+ exec(`chmod 600 ${keyPath}`); // ssh-keygen's 0600 default, made an explicit invariant
1100
+ }
1101
+ const pubKey = String(fsImpl.readFileSync(`${keyPath}.pub`, 'utf8')).trim();
1102
+ // Compare algorithm+key only — GitHub stores the key without the local comment.
1103
+ const keyBody = (k) => String(k || '').trim().split(/\s+/).slice(0, 2).join(' ');
1104
+ let minted = false;
1105
+ try {
1106
+ const keys = await ghRepos.listDeployKeys(ownerToken, { owner: repo.owner, name: repo.name, fetchImpl: deps.fetchImpl });
1107
+ const ours = keys.find((k) => k.title === title);
1108
+ const current = !!(ours && keyBody(ours.key) === keyBody(pubKey));
1109
+ if (ours && !current) {
1110
+ // GitHub 422s a duplicate title/key, and a stale registration is a dead credential —
1111
+ // replace, never accumulate.
1112
+ await ghRepos.deleteDeployKey(ownerToken, { owner: repo.owner, name: repo.name, keyId: ours.id, fetchImpl: deps.fetchImpl });
1113
+ log(` [repo-auth] removed the stale "${title}" key on ${repo.owner}/${repo.name} (local key changed)`);
1114
+ }
1115
+ if (!current) {
1116
+ try {
1117
+ await ghRepos.addDeployKey(ownerToken, { owner: repo.owner, name: repo.name, title, key: pubKey, readOnly: true, fetchImpl: deps.fetchImpl });
1118
+ } catch (e) {
1119
+ if (!e || e.code !== 'key_in_use') throw e;
1120
+ // The keypair is slug-keyed but the repo can change underneath it (renamed
1121
+ // then recreated, target_ref re-pointed): the key is registered SOMEWHERE
1122
+ // else, so retrying with this pair can never succeed. Mint fresh, try once.
1123
+ log(' [repo-auth] GitHub holds this key elsewhere (422) — minting a fresh keypair and retrying once');
1124
+ exec(`rm -f ${keyPath} ${keyPath}.pub`, { allowFail: true });
1125
+ exec(`ssh-keygen -q -t ed25519 -N '' -C ${title} -f ${keyPath}`);
1126
+ exec(`chmod 600 ${keyPath}`);
1127
+ const freshPub = String(fsImpl.readFileSync(`${keyPath}.pub`, 'utf8')).trim();
1128
+ await ghRepos.addDeployKey(ownerToken, { owner: repo.owner, name: repo.name, title, key: freshPub, readOnly: true, fetchImpl: deps.fetchImpl });
1129
+ }
1130
+ minted = true;
1131
+ log(` [repo-auth] registered read-only deploy key "${title}" on ${repo.owner}/${repo.name}`);
1132
+ } else {
1133
+ log(` [repo-auth] deploy key "${title}" already registered — nothing to mint`);
1134
+ }
1135
+ } catch (e) {
1136
+ if (e && e.code === 'not_repo_admin') {
1137
+ throw new Error(`repository access is not admin: your granted credential can read ${repo.owner}/${repo.name}, but registering its deploy key needs repository ADMIN. Ask a repository admin to raise your access (or make the repository public), then try again.`);
1138
+ }
1139
+ // Plain sentence for the owner card — a transient GitHub hiccup must not read as jargon.
1140
+ throw new Error(`deploy-key setup failed for ${repo.owner}/${repo.name}: ${e && e.message}. Nothing is wrong on your side — try again, and if it keeps failing make the repository public and retry.`, { cause: e });
1141
+ }
1142
+ exec(`git -C ${root} remote set-url origin git@github.com:${repo.owner}/${repo.name}.git`);
1143
+ exec(`git -C ${root} config core.sshCommand "ssh -F /dev/null -i ${keyPath} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"`);
1144
+ return { private: true, minted };
1145
+ }
1146
+
1147
+ // ── re-provision core-pin refresh (gap-list W3, task 1003050) ────────────────────
1148
+ //
1149
+ // The hazard: a standalone instance's RUNNING core can move ahead of its repo's
1150
+ // committed `file:` pin (an out-of-band `bongos upgrade`, a pre-fix re-provision that
1151
+ // refreshed the box without committing) — seen live on Spike Harbor, running a modern
1152
+ // core while the repo still pinned 1.19.2. The deploy path is repo-driven
1153
+ // (`git reset --hard origin/main` + `npm ci`), so the NEXT deploy silently rolls the
1154
+ // instance back against a DB the newer core has already migrated. ADR 0168 fixed
1155
+ // fresh scaffolds only; this closes the re-provision leg: every provision of an
1156
+ // already-scaffolded instance heals the committed pin to the core this control plane
1157
+ // runs, as a commit pushed to the instance repo.
1158
+
1159
+ // The PURE decision half (tested in tests/provision.mjs). Registry pins are npm's to
1160
+ // resolve; an absent dep is not ours to invent; only `file:` pins are healed.
1161
+ function planCorePinRefresh({ pinnedDep, coreVersion }) {
1162
+ const dep = String(pinnedDep || '').trim();
1163
+ if (!dep) return { action: 'skip', reason: 'no @bongos/core dependency in the instance package.json' };
1164
+ if (!/^file:/.test(dep)) return { action: 'skip', reason: `registry pin (${dep}) — npm ci resolves it; nothing to vendor` };
1165
+ const upgrade = require('./upgrade');
1166
+ const pinned = upgrade.versionFromTgzPath(dep);
1167
+ if (pinned && pinned === coreVersion) return { action: 'current', pinned };
1168
+ return { action: 'refresh', pinned: pinned || null };
1169
+ }
1170
+
1171
+ // Coerce a version label to a semver-safe charset BEFORE it is interpolated into any
1172
+ // shell string. The "from" label ultimately derives from the INSTANCE's own repo
1173
+ // package.json `file:` pin (lower trust than the shared control plane) via
1174
+ // versionFromTgzPath, whose regex admits any char but `/` — including `"`, backtick,
1175
+ // `$()`, `;`. A crafted pin (e.g. `file:vendor/bongos-core-1.0.0";touch x;echo".tgz`)
1176
+ // would otherwise break out of a `git commit -m "…"` argument and run as the
1177
+ // control-plane user. This chokepoint rejects anything outside semver's own alphabet
1178
+ // (task 1003050 grade — command-injection block). PURE.
1179
+ function safeVersionLabel(v) {
1180
+ const s = String(v == null ? '' : v).trim();
1181
+ return /^[0-9A-Za-z.+-]{1,64}$/.test(s) ? s : '(unversioned)';
1182
+ }
1183
+
1184
+ // The commit + push command sequence for a pin refresh — PURE, so tests/provision.mjs
1185
+ // covers "the runner's commit step" (task 1003050 AC) deterministically. Version
1186
+ // labels pass through safeVersionLabel (the one interpolation-safety chokepoint); the
1187
+ // push prefers the owner's OAuth token URL (the shared deploy key can't write a
1188
+ // consumer repo — task 1002317) and falls back to the SSH origin.
1189
+ function buildCorePinRefreshCommit({ root, fromVersion, toVersion, repo, ownerToken, credFile }) {
1190
+ const from = safeVersionLabel(fromVersion);
1191
+ const to = safeVersionLabel(toVersion);
1192
+ const msg = `chore: re-provision refreshes the pinned @bongos/core ${from} -> ${to} (task 1003050)`;
1193
+ const push = (ownerToken && repo && credFile)
1194
+ ? `git -C ${root} -c credential.helper='store --file=${credFile}' push https://x-access-token@github.com/${repo.owner}/${repo.name}.git HEAD:main`
1195
+ : `git -C ${root} push origin main`;
1196
+ return {
1197
+ config: [
1198
+ `git -C ${root} config user.name "${provisionerBotName()}"`,
1199
+ `git -C ${root} config user.email "${provisionerBotEmail()}"`,
1200
+ ],
1201
+ add: `git -C ${root} add -A`,
1202
+ commit: `git -C ${root} commit -m "${msg}"`,
1203
+ push,
1204
+ };
1205
+ }
1206
+
1207
+ async function refreshStandaloneCorePin(inst, deps) {
1208
+ const { log } = deps;
1209
+ const exec = deps.controlExec || deps.exec;
1210
+ const writeFile = deps.controlWriteFile || deps.writeFile;
1211
+ const upgrade = require('./upgrade');
1212
+ const root = standaloneRoot(inst);
1213
+ const coreV = coreVersionSafe();
1214
+ if (!coreV) { log(' [core-pin] cannot resolve the running core version — leaving the committed pin alone'); return { action: 'skip', reason: 'no core version' }; }
1215
+ let pkg = null;
1216
+ try { pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); } catch { /* unreadable → skip below */ }
1217
+ const pinnedDep = pkg && ((pkg.dependencies || {})['@bongos/core'] || (pkg.devDependencies || {})['@bongos/core']);
1218
+ if (!/^file:/.test(String(pinnedDep || '').trim())) {
1219
+ const plan = planCorePinRefresh({ pinnedDep, coreVersion: coreV });
1220
+ log(` [core-pin] ${plan.reason}`); return plan;
1221
+ }
1222
+ // Heal toward the core a project is GIVEN — the release when this hall runs ahead of it —
1223
+ // or the pin a brand-new project was just born on is bumped straight back to the
1224
+ // candidate in the same provisioning run (task 1004299).
1225
+ const target = projectCoreTarget({ instanceRoot: deps.instanceRoot, coreV, exec, log, tag: '[core-pin]' });
1226
+ const plan = planCorePinRefresh({ pinnedDep, coreVersion: target.version });
1227
+ if (plan.action === 'current') { log(` [core-pin] committed pin already matches the core this project is given (${target.version})`); return plan; }
1228
+ log(` [core-pin] committed pin ${safeVersionLabel(plan.pinned)} differs from the core this project is given (${target.version}) — refreshing so the next repo-driven deploy cannot roll the instance back (task 1003050)`);
1229
+
1230
+ const { tgz, packDest } = resolveVendorableCoreTarball({
1231
+ instanceRoot: deps.instanceRoot, exec, log, coreV, tag: '[core-pin]', target,
1232
+ });
1233
+ try {
1234
+ const targetVersion = upgrade.versionFromTgzPath(path.basename(tgz)) || target.version;
1235
+ const vendorRel = upgrade.vendorTarball({ instanceDir: root, fromTgz: tgz, targetVersion });
1236
+ // Prune superseded vendored tarballs (+ manifests) so the repo carries exactly ONE
1237
+ // core. fs.rmSync, NOT exec — a filename comes off the instance's own (lower-trust)
1238
+ // vendor/ dir, so shelling `rm -f ${f}` would be the same injection vector as the
1239
+ // commit label (a committed file named `bongos-core-x";touch pwn;".tgz`). A direct
1240
+ // fs call takes the name as data, never as shell (task 1003050 grade).
1241
+ const vendorDir = path.join(root, 'vendor');
1242
+ const keep = new Set([`${upgrade.ARTIFACT}-${targetVersion}.tgz`, `${upgrade.ARTIFACT}-${targetVersion}.manifest.json`]);
1243
+ // vendorTarball just mkdirSync'd this, so it exists on the happy path; guard anyway
1244
+ // so an unexpectedly-absent dir prunes nothing instead of crashing the whole provision.
1245
+ if (fs.existsSync(vendorDir)) {
1246
+ for (const f of fs.readdirSync(vendorDir)) {
1247
+ if (f.startsWith(`${upgrade.ARTIFACT}-`) && !keep.has(f)) fs.rmSync(path.join(vendorDir, f), { force: true });
1248
+ }
1249
+ }
1250
+ upgrade.writePin(root, vendorRel);
1251
+ // `npm install` (NOT ci): the pin just changed, the lockfile must be regenerated.
1252
+ const instRes = upgrade.npmInstall(root);
1253
+ if (!instRes.ok) {
1254
+ throw new Error(`npm install after the core-pin refresh failed (${instRes.reason || `exit ${instRes.code}`}) — lockfile not regenerated; the pin refresh cannot be committed half-done.`);
1255
+ }
1256
+
1257
+ // Commit + push the refresh to the instance repo — the shared deploy key cannot
1258
+ // write a consumer's repo (task 1002317), so prefer the owner's OAuth token,
1259
+ // exactly like the scaffold's push.
1260
+ const ownerToken = await resolveOwnerGithubToken(inst, deps, { tag: '[core-pin]' });
1261
+ const repo = parseTargetRef(inst.target_ref);
1262
+ const credFile = ownerToken && repo ? path.join(os.tmpdir(), `bongos-gitcred-pin-${inst.slug}-${process.pid}`) : null;
1263
+ const cmd = buildCorePinRefreshCommit({ root, fromVersion: plan.pinned, toVersion: targetVersion, repo, ownerToken, credFile });
1264
+ try {
1265
+ cmd.config.forEach((c) => exec(c));
1266
+ exec(cmd.add);
1267
+ exec(cmd.commit, { allowFail: true });
1268
+ if (credFile) writeFile(credFile, `https://x-access-token:${ownerToken}@github.com\n`, { mode: 0o600 });
1269
+ exec(cmd.push);
1270
+ } finally {
1271
+ if (credFile) exec(`rm -f ${credFile}`, { allowFail: true });
1272
+ }
1273
+ log(` [core-pin] refreshed + pushed: @bongos/core ${safeVersionLabel(plan.pinned)} → ${targetVersion}`);
1274
+ return { ...plan, refreshed: true, toVersion: targetVersion };
1275
+ } finally {
1276
+ if (packDest) exec(`rm -rf ${packDest}`);
1277
+ }
1278
+ }
1279
+
1280
+ // The per-instance systemd unit (the infra/cloudbongos.service shape): one Node process
1281
+ // bound to the instance's port + DB + brand, secrets from its own env file. SHAPE-AWARE:
1282
+ // a STANDALONE instance runs the CORE PACKAGE's platform-server from its OWN repo dir
1283
+ // (WorkingDirectory=standaloneRoot → resolveInstanceRoot() resolves to the instance via
1284
+ // cwd, deploy-instance.sh §4); co-tenant runs this control-plane checkout's src/.
1285
+
1286
+ module.exports = {
1287
+ appGroupExpr,
1288
+ appUser,
1289
+ accountPlaneGap,
1290
+ accountPreflight,
1291
+ CORE_PKG_DIR,
1292
+ INSTANCE_GITIGNORE,
1293
+ alreadyScaffolded,
1294
+ ownerLoginOf,
1295
+ asPostgres,
1296
+ buildCorePinRefreshCommit,
1297
+ coreCheckoutHasGit,
1298
+ dbCreateCmd,
1299
+ dbName,
1300
+ dbRoleCmd,
1301
+ dbRoleCmdShown,
1302
+ dbRoleGrantsCmd,
1303
+ dbOwnershipCmd,
1304
+ dbOwnershipSql,
1305
+ deployKeyPath,
1306
+ deployKeyTitle,
1307
+ ensureInstanceUserCmd,
1308
+ ensurePrivateRepoAccess,
1309
+ generateDbPassword,
1310
+ grantInstanceRepoReadCmd,
1311
+ installedCorePackDir,
1312
+ installedCoreTarball,
1313
+ instanceDbRole,
1314
+ instanceInitSpec,
1315
+ instanceRepoRemote,
1316
+ instanceStateDir,
1317
+ instanceUser,
1318
+ migrateCmd,
1319
+ onboardMode,
1320
+ parseTargetRef,
1321
+ planCorePinRefresh,
1322
+ refreshStandaloneCorePin,
1323
+ resolveOwnerGithubToken,
1324
+ resolveVendorableCoreTarball,
1325
+ projectCoreDecision,
1326
+ projectCoreTarget,
1327
+ runningCoreVersion,
1328
+ safeVersionLabel,
1329
+ scaffoldStandaloneRepo,
1330
+ seedFirstVersionCmd,
1331
+ selfInstanceRoot,
1332
+ standaloneInstallCmd,
1333
+ standaloneMigrateCmd,
1334
+ standalonePullCmd,
1335
+ standaloneRegenDocsCmd,
1336
+ standaloneRoot,
1337
+ };