@bongos/core 1.18.3 → 1.18.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/.bongos-core.json +1575 -285
  2. package/.claude/skills/builder-box/SKILL.md +6 -0
  3. package/.claude/skills/builder-sequence/SKILL.md +97 -0
  4. package/.claude/skills/builder-ship/SKILL.md +8 -2
  5. package/.claude/skills/builder-start/SKILL.md +11 -0
  6. package/.claude/skills/builder-sync/SKILL.md +41 -0
  7. package/.claude/skills/fix-task/SKILL.md +64 -0
  8. package/.claude/skills/goal-create/SKILL.md +158 -0
  9. package/.claude/skills/goal-review/SKILL.md +3 -1
  10. package/.claude/skills/idea-triage/SKILL.md +14 -8
  11. package/.claude/skills/merge-mode/SKILL.md +2 -2
  12. package/.claude/skills/new-project/SKILL.md +1 -0
  13. package/.claude/skills/planning-session/SKILL.md +45 -18
  14. package/.env.local.example +4 -3
  15. package/README.md +4 -2
  16. package/bin/bongos.js +1 -0
  17. package/clients/bongos-client/README.md +2 -2
  18. package/clients/bongos-client/bongos-client.global.js +82 -18
  19. package/clients/bongos-client/index.cjs +82 -18
  20. package/clients/bongos-client/index.d.ts +131 -29
  21. package/clients/bongos-client/index.mjs +82 -18
  22. package/config/branding.neutral.json +6 -1
  23. package/config/governance.neutral.json +21 -0
  24. package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +24 -0
  25. package/docs/adr/0090-metic-task-authoring.md +1 -1
  26. package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +41 -0
  27. package/docs/adr/0144-federated-single-logout-backchannel.md +42 -0
  28. package/docs/adr/0145-devbox-app-branding-driven-module.md +44 -0
  29. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +62 -0
  30. package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +53 -0
  31. package/docs/adr/0147-standalone-live-docs-regen.md +39 -0
  32. package/docs/adr/0148-task-scoped-box-source-access.md +48 -0
  33. package/docs/adr/0149-bongos-dev-local-launcher.md +57 -0
  34. package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +68 -0
  35. package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +229 -0
  36. package/docs/adr/0152-landing-gate-served-by-the-core-app.md +92 -0
  37. package/docs/adr/0152-metic-task-abandonment.md +67 -0
  38. package/docs/adr/0153-sequential-chain-runner.md +51 -0
  39. package/docs/adr/0154-goal-owner-criterion-authoring.md +86 -0
  40. package/docs/adr/README.md +10 -0
  41. package/docs/api/openapi.json +843 -17
  42. package/docs/api-reference.md +23 -10
  43. package/docs/architecture.md +4 -2
  44. package/docs/branding-contract.md +3 -1
  45. package/docs/canonical-permissions.md +21 -8
  46. package/docs/file-map.md +8 -0
  47. package/docs/onboarding/diagrams/01-task-lifecycle.mmd +2 -2
  48. package/docs/onboarding/diagrams/02-rank-ladder.mmd +3 -1
  49. package/docs/onboarding/diagrams/05-versioning.mmd +1 -3
  50. package/docs/onboarding/diagrams/assertions.json +4 -7
  51. package/docs/onboarding/drachmae-and-karma.md +3 -3
  52. package/docs/onboarding/primer.md +17 -16
  53. package/docs/onboarding/slash-commands.md +124 -0
  54. package/docs/recipes/builder-box-lifecycle.md +23 -0
  55. package/docs/recipes/devbox-app-release.md +14 -4
  56. package/docs/recipes/instance-first-run.md +16 -0
  57. package/docs/recipes/local-dev.md +81 -0
  58. package/docs/recipes/ops-gotchas.md +19 -1
  59. package/docs/recipes/overnight-builder.md +1 -1
  60. package/docs/recipes/parallel-goal-waves.md +174 -0
  61. package/docs/recipes/ship-and-regrade.md +2 -2
  62. package/docs/recipes/standalone-live-docs.md +48 -0
  63. package/docs/recipes/work-on-a-task-from-your-dev-box.md +108 -0
  64. package/migrations/003_pms.sql +6 -7
  65. package/migrations/006_pms_v2.sql +3 -31
  66. package/migrations/core_193_task_needs_rebase.sql +28 -0
  67. package/migrations/core_194_confirmed_backlog_clawback.sql +63 -0
  68. package/migrations/core_195_idea_triage_provenance.sql +57 -0
  69. package/migrations/core_196_session_reward_topup.sql +69 -0
  70. package/modules/autonomy/pollers/routine-timers.js +9 -8
  71. package/modules/builder-settings/wandering-prefs.js +61 -18
  72. package/modules/dev-box/app/README.md +73 -0
  73. package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
  74. package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
  75. package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
  76. package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
  77. package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
  78. package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
  79. package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
  80. package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
  81. package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
  82. package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
  83. package/modules/dev-box/app/assets/tray/win-off.png +0 -0
  84. package/modules/dev-box/app/assets/tray/win-on.png +0 -0
  85. package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
  86. package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
  87. package/modules/dev-box/app/build-resources/icon.png +0 -0
  88. package/modules/dev-box/app/electron-builder.tmpl.yml +95 -0
  89. package/modules/dev-box/app/electron-builder.yml +95 -0
  90. package/modules/dev-box/app/package-lock.json +5291 -0
  91. package/modules/dev-box/app/package.json +30 -0
  92. package/modules/dev-box/app/renderer/app.js +319 -0
  93. package/modules/dev-box/app/renderer/index.html +45 -0
  94. package/modules/dev-box/app/renderer/styles.css +246 -0
  95. package/modules/dev-box/app/renderer/theme.generated.css +16 -0
  96. package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +82 -0
  97. package/modules/dev-box/app/scripts/dmgbuild-settings.py +39 -0
  98. package/modules/dev-box/app/scripts/gen-brand.js +138 -0
  99. package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +323 -0
  100. package/modules/dev-box/app/scripts/gen-icons.js +286 -0
  101. package/modules/dev-box/app/scripts/vendor-client.js +23 -0
  102. package/modules/dev-box/app/src/box-controller.js +287 -0
  103. package/modules/dev-box/app/src/brand.generated.json +13 -0
  104. package/modules/dev-box/app/src/brand.js +16 -0
  105. package/modules/dev-box/app/src/config.js +19 -0
  106. package/modules/dev-box/app/src/gds-client.js +90 -0
  107. package/modules/dev-box/app/src/main.js +610 -0
  108. package/modules/dev-box/app/src/pairing.js +57 -0
  109. package/modules/dev-box/app/src/preload.js +29 -0
  110. package/modules/dev-box/app/src/session-store.js +58 -0
  111. package/modules/dev-box/app/src/ssh-setup.js +569 -0
  112. package/modules/dev-box/app/src/task-picker.js +70 -0
  113. package/modules/dev-box/app/src/tray.js +99 -0
  114. package/modules/dev-box/app/src/updater.js +55 -0
  115. package/modules/dev-box/app/src/vendor/bongos-client.cjs +698 -0
  116. package/modules/dev-box/app/test/unit.mjs +526 -0
  117. package/modules/dev-box/box-access.js +121 -4
  118. package/modules/dev-box/boxes.js +20 -0
  119. package/modules/dev-box/module.json +1 -1
  120. package/modules/dev-box/routes/box.js +87 -3
  121. package/modules/economy/credits.js +116 -25
  122. package/modules/economy/reward.js +8 -0
  123. package/modules/economy/streak.js +2 -1
  124. package/modules/governance/CLAUDE.md +15 -0
  125. package/modules/governance/catalog.js +419 -0
  126. package/modules/governance/config.js +174 -0
  127. package/modules/governance/db.js +125 -0
  128. package/modules/governance/migrations/governance_001_tables.sql +81 -0
  129. package/modules/governance/migrations/governance_002_seed_and_backfill.sql +103 -0
  130. package/modules/governance/migrations/governance_003_task_abandon_metic.sql +28 -0
  131. package/modules/governance/migrations/governance_004_criterion_create.sql +39 -0
  132. package/modules/governance/migrations/governance_005_resync_builder_rank_roles.sql +56 -0
  133. package/modules/governance/module.json +10 -0
  134. package/modules/governance/protected-surfaces.js +88 -0
  135. package/modules/governance/protected-surfaces.json +80 -0
  136. package/modules/governance/rank-roles.js +83 -0
  137. package/modules/governance/resolver.js +221 -0
  138. package/modules/governance/routes/governance.js +89 -0
  139. package/modules/governance/routes/role-sync.js +49 -0
  140. package/modules/governance/session-scopes.js +127 -0
  141. package/modules/governance/session-scopes.json +38 -0
  142. package/modules/hall-ui/public/atlas.css +117 -0
  143. package/modules/hall-ui/public/atlas.html +3 -114
  144. package/modules/hall-ui/public/brand-apply.js +34 -6
  145. package/modules/hall-ui/public/builders.js +187 -219
  146. package/modules/hall-ui/public/diagrams.html +2 -0
  147. package/modules/hall-ui/public/dom-utils.js +27 -0
  148. package/modules/hall-ui/public/drachmae.html +2 -0
  149. package/modules/hall-ui/public/drachmae.js +54 -1
  150. package/modules/hall-ui/public/gate.css +98 -0
  151. package/modules/hall-ui/public/gate.html +3 -95
  152. package/modules/hall-ui/public/goal-inbox.js +4 -1
  153. package/modules/hall-ui/public/goals-page.js +626 -114
  154. package/modules/hall-ui/public/goals.css +79 -1
  155. package/modules/hall-ui/public/goals.html +38 -11
  156. package/modules/hall-ui/public/hall-widgets.js +62 -52
  157. package/modules/hall-ui/public/harbor.css +123 -0
  158. package/modules/hall-ui/public/harbor.html +3 -120
  159. package/modules/hall-ui/public/index.html +11 -4
  160. package/modules/hall-ui/public/instances-panel.js +4 -1
  161. package/modules/hall-ui/public/modules.css +63 -0
  162. package/modules/hall-ui/public/modules.html +3 -60
  163. package/modules/hall-ui/public/new-project-wizard.js +7 -3
  164. package/modules/hall-ui/public/not-ready.html +2 -0
  165. package/modules/hall-ui/public/pair.css +73 -0
  166. package/modules/hall-ui/public/pair.html +3 -70
  167. package/modules/hall-ui/public/palette.js +302 -0
  168. package/modules/hall-ui/public/primer.css +13 -0
  169. package/modules/hall-ui/public/primer.html +2 -0
  170. package/modules/hall-ui/public/profile.html +2 -0
  171. package/modules/hall-ui/public/ranks.html +2 -0
  172. package/modules/hall-ui/public/roadmap.css +179 -0
  173. package/modules/hall-ui/public/roadmap.html +9 -172
  174. package/modules/hall-ui/public/roadmap.js +329 -316
  175. package/modules/hall-ui/public/sessions.css +16 -0
  176. package/modules/hall-ui/public/sessions.html +3 -13
  177. package/modules/hall-ui/public/settings.html +2 -0
  178. package/modules/hall-ui/public/settings.js +4 -4
  179. package/modules/hall-ui/public/shell.js +143 -18
  180. package/modules/hall-ui/public/style.css +231 -39
  181. package/modules/hall-ui/public/task-detail.js +347 -26
  182. package/modules/hall-ui/public/task.html +6 -1
  183. package/modules/hall-ui/public/watch.html +2 -0
  184. package/modules/hall-ui/public/work.html +8 -6
  185. package/modules/hall-ui/public/work.js +1 -1
  186. package/modules/ideas/inbox.js +61 -11
  187. package/modules/ideas/routes/inbox.js +23 -0
  188. package/modules/lifecycle/claim-eligibility.js +72 -0
  189. package/modules/lifecycle/db.js +519 -139
  190. package/modules/lifecycle/done-when.js +18 -3
  191. package/modules/lifecycle/github-push.js +19 -2
  192. package/modules/lifecycle/goal-advisory.js +114 -0
  193. package/modules/lifecycle/goal-authz.js +62 -0
  194. package/modules/lifecycle/lifecycle.js +9 -0
  195. package/modules/lifecycle/publish-reconciler.js +109 -6
  196. package/modules/lifecycle/routes/claims.js +68 -23
  197. package/modules/lifecycle/routes/done-when.js +110 -15
  198. package/modules/lifecycle/routes/goals.js +8 -38
  199. package/modules/lifecycle/routes/tasks.js +38 -6
  200. package/modules/lifecycle/ship-card.js +6 -0
  201. package/modules/onboarding/onboarding-state.js +1 -1
  202. package/modules/platform-identity/CLAUDE.md +15 -0
  203. package/modules/platform-identity/hub-device.js +147 -0
  204. package/modules/platform-identity/hub-keys.js +151 -0
  205. package/modules/platform-identity/migrations/platform_identity_001_tables.sql +114 -0
  206. package/modules/platform-identity/migrations/platform_identity_002_client_origin_index.sql +18 -0
  207. package/modules/platform-identity/migrations/platform_identity_003_project_memberships.sql +42 -0
  208. package/modules/platform-identity/migrations/platform_identity_004_projects.sql +44 -0
  209. package/modules/platform-identity/migrations/platform_identity_005_builder_activity.sql +42 -0
  210. package/modules/platform-identity/module.json +18 -0
  211. package/modules/platform-identity/platform-identity.js +531 -0
  212. package/modules/platform-identity/pollers/code-sweep.js +43 -0
  213. package/modules/platform-identity/routes/my-projects.js +140 -0
  214. package/modules/platform-identity/routes/projects.js +107 -0
  215. package/modules/platform-identity/routes/scouting.js +35 -0
  216. package/modules/platform-identity/routes/sso.js +402 -0
  217. package/modules/platform-identity/tests/platform-identity.mjs +388 -0
  218. package/modules/platform-identity/ui/.gitkeep +0 -0
  219. package/modules/security/routes/reports.js +10 -0
  220. package/modules/sessions/db.js +166 -21
  221. package/modules/sessions/module.json +1 -1
  222. package/modules/sessions/pollers/session-reward-reconcile.js +78 -0
  223. package/modules/sessions/routes/sessions.js +33 -0
  224. package/package-lock.json +2 -2
  225. package/package.json +1 -1
  226. package/public-landing/assets/anim/idle/frame_00.webp +0 -0
  227. package/public-landing/assets/anim/idle/frame_01.webp +0 -0
  228. package/public-landing/assets/anim/idle/frame_02.webp +0 -0
  229. package/public-landing/assets/anim/idle/frame_03.webp +0 -0
  230. package/public-landing/assets/anim/idle/frame_04.webp +0 -0
  231. package/public-landing/assets/anim/idle/frame_05.webp +0 -0
  232. package/public-landing/assets/anim/idle/frame_06.webp +0 -0
  233. package/public-landing/assets/anim/idle/frame_07.webp +0 -0
  234. package/public-landing/assets/anim/idle/frame_08.webp +0 -0
  235. package/public-landing/assets/anim/idle/frame_09.webp +0 -0
  236. package/public-landing/assets/anim/idle/frame_10.webp +0 -0
  237. package/public-landing/assets/anim/idle/frame_11.webp +0 -0
  238. package/public-landing/assets/anim/idle/frame_12.webp +0 -0
  239. package/public-landing/assets/anim/idle/frame_13.webp +0 -0
  240. package/public-landing/assets/anim/idle/frame_14.webp +0 -0
  241. package/public-landing/assets/anim/idle/frame_15.webp +0 -0
  242. package/public-landing/assets/anim/idle/frame_16.webp +0 -0
  243. package/public-landing/assets/anim/idle/frame_17.webp +0 -0
  244. package/public-landing/assets/anim/idle/frame_18.webp +0 -0
  245. package/public-landing/assets/anim/idle/frame_19.webp +0 -0
  246. package/public-landing/assets/anim/idle/frame_20.webp +0 -0
  247. package/public-landing/assets/anim/idle/frame_21.webp +0 -0
  248. package/public-landing/assets/anim/idle/frame_22.webp +0 -0
  249. package/public-landing/assets/anim/idle/frame_23.webp +0 -0
  250. package/public-landing/assets/anim/idle/frame_24.webp +0 -0
  251. package/public-landing/assets/anim/idle/frame_25.webp +0 -0
  252. package/public-landing/assets/anim/idle/frame_26.webp +0 -0
  253. package/public-landing/assets/anim/idle/frame_27.webp +0 -0
  254. package/public-landing/assets/anim/idle/frame_28.webp +0 -0
  255. package/public-landing/assets/anim/idle/frame_29.webp +0 -0
  256. package/public-landing/assets/anim/idle/frame_30.webp +0 -0
  257. package/public-landing/assets/anim/idle/frame_31.webp +0 -0
  258. package/public-landing/assets/anim/idle/frame_32.webp +0 -0
  259. package/public-landing/assets/anim/idle/frame_33.webp +0 -0
  260. package/public-landing/assets/anim/idle/frame_34.webp +0 -0
  261. package/public-landing/assets/anim/idle/frame_35.webp +0 -0
  262. package/public-landing/assets/anim/idle/frame_36.webp +0 -0
  263. package/public-landing/assets/anim/idle/frame_37.webp +0 -0
  264. package/public-landing/assets/anim/idle/frame_38.webp +0 -0
  265. package/public-landing/assets/anim/idle/frame_39.webp +0 -0
  266. package/public-landing/assets/anim/idle/frame_40.webp +0 -0
  267. package/public-landing/assets/anim/idle/frame_41.webp +0 -0
  268. package/public-landing/assets/anim/idle/frame_42.webp +0 -0
  269. package/public-landing/assets/anim/idle/frame_43.webp +0 -0
  270. package/public-landing/create.html +51 -58
  271. package/public-landing/gate/README.md +137 -0
  272. package/public-landing/gate/dispmaps/bar.png +0 -0
  273. package/public-landing/gate/dispmaps/flat.png +0 -0
  274. package/public-landing/gate/dispmaps/key.png +0 -0
  275. package/public-landing/gate/index.html +587 -0
  276. package/public-landing/gate/previews/cloud-bongos.jpg +0 -0
  277. package/public-landing/gate/previews/off-the-boats.jpg +0 -0
  278. package/public-landing/gate/space.css +799 -0
  279. package/public-landing/gate/space.js +1826 -0
  280. package/public-landing/gate/sprites/enter.png +0 -0
  281. package/public-landing/gate/sprites/search.png +0 -0
  282. package/public-landing/gate/sprites/terminal-blank.png +0 -0
  283. package/public-landing/gate/sprites/tile.png +0 -0
  284. package/public-landing/world/assets/clouds/cloud-island-large.png +0 -0
  285. package/public-landing/world/assets/clouds/cloud-puff.png +0 -0
  286. package/public-landing/world/assets/creatures/creature-whale-cargo.png +0 -0
  287. package/public-landing/world/assets/creatures/creature-whale-glide.png +0 -0
  288. package/public-landing/world/assets/plates/plate-clouds-near.png +0 -0
  289. package/public-landing/world/assets/plates/scene-establishing.png +0 -0
  290. package/scripts/gds/abandon.js +6 -4
  291. package/scripts/gds/api.js +10 -5
  292. package/scripts/gds/api.ps1 +26 -0
  293. package/scripts/gds/api.sh +6 -0
  294. package/scripts/gds/audit-ships.js +7 -1
  295. package/scripts/gds/backfill-sessions.js +78 -18
  296. package/scripts/gds/backup.js +23 -1
  297. package/scripts/gds/box-connect-lib.js +124 -31
  298. package/scripts/gds/box-sync.js +66 -0
  299. package/scripts/gds/box.js +225 -53
  300. package/scripts/gds/cf-tunnel.js +145 -6
  301. package/scripts/gds/claim.js +34 -3
  302. package/scripts/gds/cli-lib.js +161 -27
  303. package/scripts/gds/code.js +6 -2
  304. package/scripts/gds/deploy-divergence.js +209 -0
  305. package/scripts/gds/dev-lib.js +309 -0
  306. package/scripts/gds/dev.js +322 -0
  307. package/scripts/gds/doctor.js +90 -61
  308. package/scripts/gds/fetch-art-key.js +1 -1
  309. package/scripts/gds/fetch-sound-prefs.js +2 -2
  310. package/scripts/gds/fitness.js +228 -12
  311. package/scripts/gds/gds-db-backup.service +16 -1
  312. package/scripts/gds/gds-db-offsite.service +9 -1
  313. package/scripts/gds/gen-api-client.js +1 -1
  314. package/scripts/gds/gen-api-docs.js +36 -14
  315. package/scripts/gds/gen-diagrams.js +45 -28
  316. package/scripts/gds/gen-file-map.js +2 -1
  317. package/scripts/gds/gen-freshness.js +32 -0
  318. package/scripts/gds/gen-repo-map.js +3 -2
  319. package/scripts/gds/gen-session-index.js +14 -5
  320. package/scripts/gds/hooks/pre-push.js +2 -2
  321. package/scripts/gds/init.js +60 -3
  322. package/scripts/gds/install-git-hooks.js +1 -1
  323. package/scripts/gds/key-card.js +15 -6
  324. package/scripts/gds/key-status.js +1 -1
  325. package/scripts/gds/linkify-refs.js +35 -9
  326. package/scripts/gds/login.js +16 -1
  327. package/scripts/gds/paste-token.js +7 -1
  328. package/scripts/gds/preflight.js +126 -0
  329. package/scripts/gds/provision.js +123 -12
  330. package/scripts/gds/publish-manifest.js +7 -1
  331. package/scripts/gds/reauth.js +7 -3
  332. package/scripts/gds/regen-instance-docs.js +47 -0
  333. package/scripts/gds/run-unit-tests.js +4 -2
  334. package/scripts/gds/seed-bongos-v1-seo-tasks.js +104 -0
  335. package/scripts/gds/seed-bv1-profiles-tasks.js +126 -0
  336. package/scripts/gds/sequence.js +425 -0
  337. package/scripts/gds/session-cost.js +10 -1
  338. package/scripts/gds/setup.js +31 -5
  339. package/scripts/gds/shell.js +5 -3
  340. package/scripts/gds/ship.js +319 -29
  341. package/scripts/gds/smoke-gds.js +139 -8
  342. package/scripts/gds/start.js +171 -22
  343. package/scripts/gds/status.js +1 -1
  344. package/scripts/gds/triage.js +10 -9
  345. package/scripts/gds/upgrade.js +124 -2
  346. package/scripts/status-mirror-sync.js +114 -84
  347. package/src/bongos/api-errors.js +1 -0
  348. package/src/bongos/auth.js +447 -102
  349. package/src/bongos/db-kernel.js +27 -0
  350. package/src/bongos/db.js +39 -0
  351. package/src/bongos/module-scope-map.js +27 -1
  352. package/src/bongos/permission-path-check.js +136 -102
  353. package/src/bongos/repo-info.js +47 -11
  354. package/src/bongos/route-rank-check.js +429 -46
  355. package/src/bongos/routes/_helpers.js +12 -1
  356. package/src/bongos/routes/auth.js +219 -2
  357. package/src/bongos/routes/backup.js +58 -2
  358. package/src/bongos/routes/me.js +35 -1
  359. package/src/bongos/serve-internal.js +84 -15
  360. package/src/branding.js +32 -0
  361. package/src/instance-config.js +30 -0
  362. package/src/module-api.js +112 -1
  363. package/src/platform-server.js +86 -9
  364. package/tests/abandon_task.mjs +59 -3
  365. package/tests/api_cli_flags.mjs +53 -0
  366. package/tests/auth_page_gate.mjs +13 -0
  367. package/tests/backfill_sessions.mjs +127 -1
  368. package/tests/backup_cli.mjs +54 -0
  369. package/tests/bongos_box_connect.mjs +77 -0
  370. package/tests/box_access.mjs +130 -1
  371. package/tests/box_credential.mjs +125 -0
  372. package/tests/box_host_keys.mjs +24 -0
  373. package/tests/box_infra_resolve.mjs +149 -0
  374. package/tests/box_scope_session.mjs +6 -2
  375. package/tests/box_sync.mjs +55 -0
  376. package/tests/box_task_scope.mjs +99 -0
  377. package/tests/branding.mjs +12 -0
  378. package/tests/cf_ruleset_preflight.mjs +142 -0
  379. package/tests/cf_tunnel.mjs +37 -0
  380. package/tests/claim_gate_rebase.mjs +98 -0
  381. package/tests/clawback_confirmed_backlog.mjs +103 -0
  382. package/tests/cli_lib_formatters.mjs +20 -0
  383. package/tests/confirm_attribution.mjs +30 -46
  384. package/tests/credit_grant.mjs +198 -10
  385. package/tests/criterion_authoring_gate.mjs +272 -0
  386. package/tests/criterion_progress.mjs +33 -1
  387. package/tests/dev_lib.mjs +253 -0
  388. package/tests/devbox_artifact_gate.mjs +75 -0
  389. package/tests/devbox_dmg_backdrop.mjs +137 -0
  390. package/tests/devbox_icons.mjs +304 -0
  391. package/tests/devbox_installer_brand.mjs +232 -0
  392. package/tests/devbox_key_register.mjs +234 -0
  393. package/tests/docs_openapi_route.mjs +110 -0
  394. package/tests/doctor_hooks.mjs +141 -0
  395. package/tests/extract_json.mjs +140 -0
  396. package/tests/fitness.mjs +89 -0
  397. package/tests/gen_freshness.mjs +96 -0
  398. package/tests/goal_advisory.mjs +251 -0
  399. package/tests/goal_criterion_confirm_ui.mjs +269 -0
  400. package/tests/goal_routes.mjs +59 -4
  401. package/tests/goal_tier.mjs +12 -3
  402. package/tests/governance_catalog.mjs +129 -0
  403. package/tests/governance_claim_eligibility.mjs +262 -0
  404. package/tests/governance_config.mjs +187 -0
  405. package/tests/governance_ownership_scope.mjs +410 -0
  406. package/tests/governance_page_gates.mjs +324 -0
  407. package/tests/governance_parity.mjs +336 -0
  408. package/tests/governance_principals.mjs +232 -0
  409. package/tests/governance_protected_surfaces.mjs +208 -0
  410. package/tests/governance_require_permission.mjs +223 -0
  411. package/tests/governance_resolver.mjs +84 -0
  412. package/tests/governance_role_sync.mjs +231 -0
  413. package/tests/governance_seed.mjs +127 -0
  414. package/tests/governance_session_scope.mjs +434 -0
  415. package/tests/governance_vanilla_boot.mjs +203 -0
  416. package/tests/grade_attribution.mjs +7 -7
  417. package/tests/hall_audit.mjs +223 -0
  418. package/tests/hall_nav.mjs +191 -0
  419. package/tests/hall_palette.mjs +206 -0
  420. package/tests/hall_signout.mjs +124 -0
  421. package/tests/hook_user_agents.mjs +10 -0
  422. package/tests/idea_triage.mjs +162 -0
  423. package/tests/idp_device_exchange.mjs +93 -0
  424. package/tests/init.mjs +51 -0
  425. package/tests/instance_config.mjs +65 -0
  426. package/tests/instance_mismatch_guard.mjs +23 -6
  427. package/tests/instance_release_config.mjs +104 -0
  428. package/tests/linkify_refs.mjs +15 -2
  429. package/tests/module_contributions.mjs +55 -0
  430. package/tests/module_loader.mjs +10 -5
  431. package/tests/module_scope_active_claims.mjs +94 -0
  432. package/tests/needs_rebase.mjs +92 -0
  433. package/tests/one_active_claim_per_session.mjs +76 -0
  434. package/tests/override_attribution.mjs +10 -8
  435. package/tests/override_request_ownership.mjs +8 -4
  436. package/tests/parse_cookie_decode.mjs +42 -0
  437. package/tests/pay_on_land.mjs +129 -0
  438. package/tests/platform_boot.mjs +100 -1
  439. package/tests/post_login_landing.mjs +42 -0
  440. package/tests/preflight_checks.mjs +72 -0
  441. package/tests/provision.mjs +132 -7
  442. package/tests/publish_branch_route.mjs +53 -0
  443. package/tests/publish_reconciler.mjs +187 -2
  444. package/tests/repo_info_binding.mjs +248 -0
  445. package/tests/reward_gate.mjs +11 -2
  446. package/tests/reward_mode_cost_plus_only.mjs +116 -0
  447. package/tests/reward_mode_surfacing.mjs +168 -0
  448. package/tests/route_permission_check.mjs +301 -0
  449. package/tests/route_rank_check.mjs +254 -12
  450. package/tests/route_validation.mjs +30 -0
  451. package/tests/sequence.mjs +267 -0
  452. package/tests/session_digest.mjs +44 -0
  453. package/tests/session_index.mjs +18 -6
  454. package/tests/session_records.mjs +39 -1
  455. package/tests/session_reward_on_land.mjs +381 -0
  456. package/tests/session_reward_reconcile.mjs +229 -0
  457. package/tests/ship_api_client_pathspec.mjs +100 -0
  458. package/tests/ship_base_flag.mjs +93 -0
  459. package/tests/ship_ci_deploy.mjs +80 -3
  460. package/tests/ship_upstream_deletion.mjs +57 -0
  461. package/tests/smoke_target.mjs +233 -0
  462. package/tests/start_env_guard.mjs +87 -0
  463. package/tests/start_rebase_warning.mjs +103 -0
  464. package/tests/status_mirror_sync.mjs +122 -0
  465. package/tests/stop_refcheck.mjs +19 -1
  466. package/tests/task_goal_link.mjs +15 -6
  467. package/tests/upgrade.mjs +139 -0
  468. package/tests/web_admission_status.mjs +162 -0
  469. package/tests/write_validation_gaps.mjs +110 -0
@@ -0,0 +1,67 @@
1
+ # 0152 — Metic gains task abandonment, paired with an un-abandon at the same rank
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-07-27
5
+ - **Deciders:** Lars (Archon), Claude
6
+
7
+ **Builds on / amends:** [ADR 0090](<redacted>.md) (Metic task authoring — this ADR reverses one line of its "what stays Archon-only" list), [ADR 0016](<redacted>.md) (server-enforced rank boundary — unchanged), [ADR 0151](<redacted>.md) (permissions-as-atom — the `task.abandon` floor moves with the gate).
8
+
9
+ **Track:** `internal` (GDS / methodology — a Cloud Bongos platform capability). Task [1002476](https://cloudbongos.com/builders#/task/1002476).
10
+
11
+ ---
12
+
13
+ ## Context
14
+
15
+ [ADR 0090](<redacted>.md) moved task **authoring** down to the trusted working rank: a Metic can create a task, edit it, promote it backlog→ready, and wire its dependencies and criterion links. Its "what stays Archon-only" list kept one lifecycle transition behind: `POST /tasks/:id/abandon`, described there as "terminal scope removal — the inverse of promote; a destructive lifecycle kill."
16
+
17
+ A year of using that split showed the asymmetry costs more than it protects. A Metic runs `/idea-triage`, decides which ideas become work, seeds the tasks, and shapes them — and then hits a wall on the one operation that cleans up after all of it. Duplicates they created, chores that a parallel session already shipped, and tasks their own re-scoping made moot all had to be handed back to the owner to retire. The Archon was not adjudicating anything in those hand-backs; they were doing janitorial work on a queue they had already delegated.
18
+
19
+ Lars's directive (2026-07-27): **"Metics should also be able to abandon tasks."**
20
+
21
+ ### The real risk was not the grant — it was that abandon had no undo
22
+
23
+ Abandoning is a one-way door *through the API*: `/promote` accepted only `backlog`/`blocked`, `PATCH /tasks/:id` refuses `status` outright ([#514](https://cloudbongos.com/builders#/task/514)), and nothing else writes `status='abandoned'` back to a live value. Restoring a wrongly-retired task meant hand-SQL on prod — an Archon-with-ssh operation.
24
+
25
+ That was tolerable while abandon *was* Archon-only (the person who could break it could fix it). Handing the kill switch to Metic without an undo would not have removed the owner-cleanup burden; it would have swapped "please retire this" requests for "please un-retire this" requests, with a worse failure mode — a wrongly-abandoned task is invisible in every claimable list and every version rollup ([migration 017](../../migrations/<redacted>.sql) deliberately excludes abandoned from `version_progress`), so nobody notices the mistake until the work silently doesn't happen.
26
+
27
+ ## Decision
28
+
29
+ **1. Re-rank the gate.** `POST /tasks/:id/abandon` moves from `requireRank('archon')` to `requireRank('metic', 'archon')` — the same threshold ADR 0090 gave create/edit/promote. A rank trusted to seed and shape work is trusted to retire it.
30
+
31
+ **2. Add the undo at the same rank.** `POST /tasks/:id/promote` now also accepts `abandoned`, restoring the task to **`backlog`**:
32
+
33
+ | Route | Was | Now |
34
+ |---|---|---|
35
+ | `POST /tasks/:id/abandon` | archon | **metic+** |
36
+ | `POST /tasks/:id/promote` | backlog\|blocked → ready | **+ abandoned → backlog** (metic+, unchanged rank) |
37
+
38
+ Restore lands in `backlog`, never straight to `ready`. The task was retired deliberately; bringing it back should re-enter it as un-workable and require a second, explicit promote to become claimable. The restore branch returns before the reward gate ([ADR 0096](<redacted>.md)) — that is a promote-to-*workable* concern, and `backlog` is the pre-workable state, the same reasoning `/demote` already uses.
39
+
40
+ The backing `db.restoreTask` is the mirror of `db.abandonTask`: `SELECT … FOR UPDATE`, guard, single `UPDATE`, commit, with `TASK_NOT_FOUND` / `TASK_NOT_ABANDONED` as its refusals. It clears the `value_summary = 'Abandoned: <reason>'` stamp abandon writes — and *only* that stamp (`CASE WHEN value_summary LIKE 'Abandoned: %'`) — so a restored task never carries a retire note into a later ship card or `#ship-news` broadcast, while an unrelated summary survives.
41
+
42
+ ### What is unchanged
43
+
44
+ - **The abandon guards.** A `shipped` task is still refused (immutable history — retiring it would corrupt the ledger and the version rollups); a task with an **active claim** is still refused (release it first, so an abandon can never race a ship in flight); an already-abandoned task is still an idempotent no-op.
45
+ - **The audit trail.** Both the abandon and the restore are `POST`s through the writing middleware, so `audit_log` records who retired what, with the `reason`, and who brought it back.
46
+ - **Everything else on ADR 0090's Archon-only list** — setting rank, closing versions, grader-bypass confirm, cross-tier `POST|DELETE /dependencies` and goal-scope edges, `done-when` unsatisfy, newcomer-restock, peer-vote tally, override adjudication. This ADR moves exactly one row.
47
+
48
+ ### Downgrade protection + the permission catalog
49
+
50
+ - `EXPECTED_RANKS`'s lifecycle pin (`src/bongos/route-rank-check.js`, `MODULE_EXPECTED_RANKS.lifecycle`) moves to `metic+archon` in lockstep, so a *further* silent downgrade (to `any-builder`) is still a hard CI fail, and the route's `// rank:` annotation matches the middleware.
51
+ - Under [ADR 0151](<redacted>.md) the `task.abandon` permission's `floor` moves `archon` → `metic`. Because `RANK_ROLE_SEED` is *derived* from floors, that one edit re-derives the seed; the live grant arrives as `modules/governance/migrations/<redacted>.sql`, since the compat seed (002) is run-once and editing it would leave every already-migrated database on the old grant. The floor change is purely **additive** (archon already holds the permission — the seeded roles are cumulative supersets), so nothing is revoked. `tests/governance_seed.mjs` was generalized to compare the **union** of all grant migrations against the catalog, and now fails loudly if a future migration ever *revokes*, which the union model could not represent.
52
+
53
+ ## Consequences
54
+
55
+ - **The triage→author→retire loop closes for Metic.** The owner is out of the janitorial path for the queue they already delegated; the remaining owner-only lifecycle powers are ratification and scope-closing, which is where ADR 0090 wanted the line.
56
+ - **A mis-retire is now self-service to fix, at the rank that caused it** — and, notably, this also removes an Archon-with-ssh chore that existed *before* this grant.
57
+ - **Restore is deliberately not idempotent-friendly the way abandon is.** Calling `/promote` on a non-abandoned task still 409s `cannot_promote` with the current status; the restore path refuses rather than guessing. That keeps `/promote`'s contract honest — it is one route with two explicit source states, not a "make this task alive somehow" verb.
58
+ - **Larger trusted surface, same shape as ADR 0090's.** A Metic can now retire any non-shipped, unclaimed task in any open version, with no per-version/per-module fence. That is the same accepted "tighten later" debt: if Metic authority is ever folded into the goal-scope model ([ADR 0086](<redacted>.md)), abandon travels with it.
59
+ - **Trust boundary intact** ([ADR 0016](<redacted>.md)): enforcement stays server-side, per request, uncached. Editing a doc or `CLAUDE.md` grants nothing; `requireRank('metic','archon')` on the route is the authority.
60
+
61
+ ## Alternatives considered
62
+
63
+ - **Re-rank abandon with no undo.** Rejected: it relocates the owner-cleanup burden instead of removing it, and the failure mode (a silently-invisible task) is worse than the one it fixes. The undo is ~40 lines including its guards.
64
+ - **A dedicated `POST /tasks/:id/restore` route.** Rejected as surface for its own sake: `/promote` already *is* the "move this task toward workable" verb at exactly the right rank, and the restore is a third source state on it. A separate route would need its own gate, its own pin, its own catalog permission, and its own docs row — all to express the same transition.
65
+ - **Restore straight to `ready`.** Rejected: it would make a two-keystroke mistake (abandon → restore) leave the task claimable again with no human re-read of *why* it was retired. Landing in `backlog` costs one deliberate extra call and puts the scope decision back in front of someone.
66
+ - **Fence the grant (only tasks the Metic authored, or only non-`security_sensitive` ones).** Rejected as a rule builders have to learn for a threat that isn't real here: a Metic can already `PATCH` and re-scope any task, so "abandon only your own" fences nothing they couldn't achieve by editing. ADR 0090 made the same call for authoring.
67
+ - **Promote the affected builders to Archon.** Rejected for the reason ADR 0090 gave: Archon also carries rank-setting, version-close, grader-bypass, and infra — far more than "clean up the task queue."
@@ -0,0 +1,51 @@
1
+ # ADR 0153 — Sequential chain execution: one kickoff walks a dependency chain
2
+
3
+ - **Status:** accepted
4
+ - **Date:** 2026-07-28
5
+ - **Task:** 1002484
6
+ - **Relates to:** [ADR 0015](<redacted>.md) (dependencies + auto-promotion), [ADR 0049](<redacted>.md) (the split parallel-safety contract), [ADR 0086](<redacted>.md) §5 (polymorphic dependency edges)
7
+
8
+ ## Context
9
+
10
+ ADR 0015 made `dependencies[]` load-bearing: a task cannot be claimed until its predecessors ship, and shipping a task auto-promotes its now-unblocked dependents `backlog`→`ready` (the `promote_dependents_on_ship` trigger, migrations 020/163). That machinery works. What it does not do is **pick the newly-unblocked task up**.
11
+
12
+ The result, measured on the live instance on 2026-07-28: **146 of 303 open tasks sit behind a predecessor, and only ~79 are `ready`** at any moment. A single planning seed like goal 1000045 fronts a 24-task chain worth 1,440 credits and ~62 hours. Working it meant 24 separate human kickoffs — each one a fresh session that had to re-derive what was now claimable.
13
+
14
+ The only "sequential" affordance was `<redacted>` in `modules/hall-ui/public/work.js`: a clipboard template listing manually multi-selected tasks in topological order. Two problems. It is *text*, not execution. And the tasks a chain most needs to queue — the dependency-gated successors — are not selectable on the board at all, because they are still `backlog`.
15
+
16
+ Two builders asked for this independently: idea 1000461 (jaxri — workflows that run tasks "in parallel or sequentially") and idea 1000398 (IAMMASTERQUA — chains as the claimable unit, priced by total chain value).
17
+
18
+ ## Decision
19
+
20
+ Ship a **chain runner**: one kickoff walks a dependency chain end to end.
21
+
22
+ **1. No schema change.** The ordering already exists in the polymorphic `dependencies` table, the `task_is_fully_unblocked` predicate, and the promote-on-ship trigger. `GET /tasks/:id` already returns `dependencies[]` and `dependents[]`. Adding a `queue`, `chain_id`, or `next_task` column would create a second, drift-prone source of truth for an order the graph already encodes. The runner derives the chain on demand and stays stateless.
23
+
24
+ **2. No new claim guard.** One-active-claim-per-session is keyed on the *working tree* (`shouldRefuseSessionClaim`, `modules/lifecycle/routes/claims.js`), so ship-before-next-claim is already enforced — which is exactly the sequential shape. The chain therefore claims **one task at a time**, and a task is always fully `shipped` before the next is claimed. Batch-claiming a chain is not merely discouraged, it is impossible: successors fail `DEPS_NOT_SHIPPED` by construction, because their predecessor has not shipped yet. The ship *is* the thing that unblocks the next step.
25
+
26
+ **3. Split deterministic plumbing from judgement.** `scripts/gds/sequence.js` resolves scope, orders it, and evaluates a halt matrix — all from facts on the task row, no model judgement. `.claude/skills/builder-sequence/SKILL.md` owns the loop and defers to `/builder-claim`, `/dev`, and `/builder-ship` for their existing mechanics. A halt is a named reason with a non-zero exit code, not a narrative.
27
+
28
+ **4. Halt, do not skip** (owner decision, 2026-07-28). When the next task is something the runner should not decide alone, it **stops and reports**, leaving that task claimed. It does not ship a half-finished task, and it does not skip ahead to the next unblocked sibling.
29
+
30
+ The halt matrix: `needs_migration`, `security_sensitive`, `protected_path` (touches[] ∩ `PROTECTED_GLOBS`), `rank_floor`, `thin_spec`, `judgement_kind` (`decision`/`spike`), `not_claimable`, `deps_unmet`, `in_flight`, `cycle`. The skill adds two it must detect itself: `ship_failed` and `scope_surprise`.
31
+
32
+ ## Why halt rather than skip
33
+
34
+ Skipping looks like the higher-throughput option and is not. In a dependency graph, a skipped task is a *predecessor* — skipping it strands everything behind it, so the run continues while quietly guaranteeing that the rest of the chain cannot complete. The apparent progress is the dangerous part: a run that skipped three tasks and shipped nine reads as a success until someone asks why the chain never finished.
35
+
36
+ Halting also makes unattended running safe, which is the whole point of one kickoff. The owner can start a chain and walk away knowing the failure mode is "stopped early with a reason", not "made twelve judgement calls you have not seen yet".
37
+
38
+ ## Consequences
39
+
40
+ - A chain of N mechanical tasks now costs one kickoff instead of N.
41
+ - Chains stop silently stranding: a halt names what a human must do, so the queue does not just quietly stop advancing.
42
+ - The halt matrix is deliberately conservative. Tasks with thin specs will halt runs, which is a **feature** — it turns "the runner stopped" into pressure to write acceptance criteria at planning time.
43
+ - `not_claimable` defers to the server's own claimable feed rather than re-deriving its filter client-side. If that endpoint is unreadable the runner stays quiet rather than halting on everything — an early bug where the envelope key was misread (`tasks` vs `claimable`) turned an empty result into a universal halt, and the null-vs-empty distinction is now covered by a test.
44
+ - **Not built:** chains as a priced, claimable unit on the Tasks board (idea 1000398). The runner is the capability; surfacing chains with their total value is a separate, additive change.
45
+
46
+ ## Alternatives rejected
47
+
48
+ - **A `chain_id` / queue table.** A second source of truth for an order the dependency graph already encodes, and one that drifts the moment an edge is added.
49
+ - **Batch-claim the whole chain up front.** Impossible under the dep gate, and it would defeat the isolation that makes each ship safe.
50
+ - **Let the runner use judgement and keep shipping** (the third option the owner was offered). Faster, but it moves review after the fact — the owner would be auditing decisions that had already landed.
51
+ - **Extend `overnight-builder`.** That routine walks the claimable queue by priority across the whole backlog; this walks one bounded chain the user pointed at. Different scope, and the routine is instance-only (absent from the portable core).
@@ -0,0 +1,86 @@
1
+ # 0154 — A goal's done-when criteria are authored by that goal's owner or managers
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-07-29
5
+ - **Deciders:** Lars (owner / Archon), Claude
6
+
7
+ **Builds on / amends:** [ADR 0086](<redacted>.md) (the goal tier — this ADR extends its owner/manager authority to the goal's criteria), [ADR 0090](<redacted>.md) (Metic task authoring), [ADR 0106](<redacted>.md) (owner vs manager), [ADR 0105](<redacted>.md) (why this route exists at all), [ADR 0016](<redacted>.md) (server-enforced rank boundary — unchanged), [ADR 0151](<redacted>.md) (permissions-as-atom — and see §5, which corrects one of its rows).
8
+
9
+ **Track:** `internal` (GDS / methodology). Task [1002538](https://cloudbongos.com/builders#/task/1002538), under the **Builder experience** goal [1000025](https://cloudbongos.com/builders#/goal/1000025).
10
+
11
+ ---
12
+
13
+ ## Context
14
+
15
+ The Metic planning loop is complete except in one place. A Metic may create a goal ([ADR 0086](<redacted>.md): `POST /goals` is `requireRank('metic','archon')`), author the tasks under it ([ADR 0090](<redacted>.md)), wire their dependencies and criterion links, and **confirm** a criterion when the work lands ([ADR 0086](<redacted>.md) §6 re-ranked `POST /done-when/:criterionId/satisfy` to Metic+ "so the planner — not only the Archon — closes the criterion/goal loop").
16
+
17
+ But they could not **create** the criteria those tasks are measured against: `POST /versions/:id/done-when` was `requireRank('archon')`. So a Metic could stand up a goal, fill it with work, and close its criteria out — but had to ask an Archon to write them in the first place. That is the one Archon-shaped hole in an otherwise Metic-complete loop, and it is what blocked `/goal-create` ([1002540](https://cloudbongos.com/builders#/task/1002540)) from being a skill a Metic can actually run end to end.
18
+
19
+ ### Why the obvious fix — a flat re-rank — is wrong
20
+
21
+ The tempting move is symmetry: a Metic may confirm a criterion, so let them create one. That argument does not survive contact with what a criterion *does*.
22
+
23
+ **Creating an unsatisfied criterion is a backward ratchet, and there is no undo.** Every "is this done" aggregate counts unsatisfied rows:
24
+
25
+ - `modules/lifecycle/version-close.js` gates on `criteria.satisfied === criteria.total`, counted **per version regardless of goal**. One unsatisfied row parks the version's close suggestion indefinitely — and that suggestion is served on the **unauthenticated** `GET /versions/close-suggestions`, so it is publicly visible.
26
+ - `db.achieveGoalIfComplete` refuses while `unsatisfied > 0`, so the goal never auto-achieves, and `promote_dependents_on_goal_achieve` never releases the tasks waiting on it.
27
+
28
+ That is operationally the same effect as `POST /done-when/:criterionId/unsatisfy` — which is Archon-only, and which `docs/governance/<redacted>.md` lists in the same Archon "Lifecycle disposition" line as this route. And there is **no DELETE route for a criterion**: descoping is direct SQL under a claim. A mistake is not correctable through the API by anyone.
29
+
30
+ **The create path also had no ownership check whatsoever.** Its entire authorization surface was `requireBuilder` → `requireRank` → version exists → (if `goal_id` given) the goal belongs to that version. That last one is referential integrity, not authority. Every *other* goal-touching write in the module runs `authorizeGoalManagement` (owner | manager | Archon) or `authorizeGoalTaskCreate` (membership + scope wall). A flat re-rank would therefore have let any Metic attach a permanent, un-deletable criterion to **any goal on any version**, including goals they had never joined and private goals ([ADR 0112](<redacted>.md)) — published to the anonymous status feed.
31
+
32
+ `docs/canonical-permissions.md` named "set version scope (`done-when`)" as Archon-exclusive. A flat re-rank contradicts that flatly; the decision below narrows it instead.
33
+
34
+ ## Decision
35
+
36
+ **Authority over a goal's criteria follows goal ownership, not global rank.**
37
+
38
+ `POST /versions/:id/done-when` becomes `requireRank('metic','archon')`, plus an **ordered, fail-closed in-handler wall** for any non-Archon:
39
+
40
+ | Step | Refusal |
41
+ |---|---|
42
+ | a `goal_id` must be named | `403 goal_id_required` |
43
+ | the caller must be the goal's **owner or a manager** | `400 bad_goal_id` — **masked**, see below |
44
+ | the goal must be `open` | `409 goal_not_open` |
45
+ | the version must be open (`planning` or `building`) | `409 version_closed` |
46
+
47
+ An Archon short-circuits all four: version-level (goal-less) criteria, goals in any state, and versions in any state stay theirs.
48
+
49
+ The authority predicate is `isGoalOwnerOrManager` — the **same** owner-or-manager test behind visibility changes, invitations, join-request responses, member roles and conflict resolution. It moved from `modules/lifecycle/routes/goals.js` to a module sibling, `modules/lifecycle/goal-authz.js`, so both routes share one definition of "who holds this goal"; `authorizeGoalManagement` is rebuilt on it and re-exported from `routes/goals.js`, so its existing DB-free tests are untouched. No route requires another route.
50
+
51
+ ### Authority is checked before state, and the refusal is masked
52
+
53
+ Ordering matters here in a way it does not for the sibling goal routes. Those are reached by callers who already know the goal exists (they are looking at it); this one takes an arbitrary integer `goal_id` from any Metic.
54
+
55
+ If the refusals were distinguishable — `400 bad_goal_id` (no such goal) vs `403 not_owner_or_manager` (exists, not yours) vs `409 goal_not_open` (exists, settled) — the route would be an **existence oracle**: a Metic could enumerate `goal_id`s and learn which exist on a version, including **private goals** ([ADR 0112](<redacted>.md)). That surface did not exist while the route was Archon-only, because an Archon can already read every goal. Re-ranking it creates the surface, so the re-rank has to close it.
56
+
57
+ So the ownership test runs **ahead of** both state checks, and a caller who fails it gets byte-identical `400 bad_goal_id` to one naming a goal that does not exist — same code, same message, same envelope, rendered by the same `badGoalId()` helper the genuine-absence branch uses. Only someone who already holds the goal learns anything further about it. This mirrors the O1 no-leak ordering the goal abuse matrix already pins (`not_a_member` is returned before any scope hint).
58
+
59
+ **What does not move.** `POST /done-when/:criterionId/unsatisfy` stays Archon — un-flipping a *confirmed* criterion rewrites shipped scope-truth, and keeping the backward ratchet at the top rank is the safety this grant is paired with. It was previously **unpinned** in `route-rank-check`; this ADR pins it at `archon` so a silent downgrade reds the build.
60
+
61
+ ### Why this is the right boundary
62
+
63
+ Creating a criterion inside a goal you own is not scope-*truth* authorship in the sense the Archon gate was protecting — it is describing the finish line of a workspace you are already accountable for. The blast radius is bounded by something the actor already holds. Creating a criterion at the *version* level, or inside someone else's goal, still shapes the project's scope for other people, and that stays Archon.
64
+
65
+ ## Consequences
66
+
67
+ - `/goal-create` becomes genuinely Metic-runnable: create the goal → write its criteria → seed its tasks, in one session, without an Archon in the loop.
68
+ - A Metic can still park **their own** goal by adding a criterion they never satisfy. That is a self-inflicted wound inside their own workspace, visible in `/goal-review`, not an attack on anyone else's.
69
+ - The proposal → ratify queue (`kind='criterion-proposal'` → `POST /inbox/:id/ratify`, Archon-only) remains the path for **version-level** criteria and for anyone who is not the goal's owner. It is no longer the only path for a goal owner describing their own goal. Note the direct path writes one row and awards no `proposal_ratified` karma and no `criterion_proposal_audit` provenance — proposals remain the richer, attributable route, and the one an Archon adjudicates.
70
+ - Five generated artifacts regenerate (`docs/api/openapi.json`, `docs/api-reference.md`, `docs/routes-permissions.md`, `clients/bongos-client/*`), because the rank flows into the published contract via the `// rank:` annotation.
71
+
72
+ ## §5 — A correction to ADR 0151's catalog
73
+
74
+ [ADR 0151](<redacted>.md)'s "floor `metic` today" table bundles three routes into one row:
75
+
76
+ > `criterion.satisfy` / `criterion.review` | `POST /done-when/:id/satisfy`, `GET /done-when/pending-review`, `POST /versions/:id/done-when`
77
+
78
+ Two of those were Metic; the third (`POST /versions/:id/done-when`) was **Archon** at the time that table was written. 0151's own cited source — the exhaustive `docs/governance/<redacted>.md` — lists it correctly under Archon. The bundled row was a transcription slip, and it was latent-dangerous: 0151's R94 compat seed grants "exactly today's powers," so seeding from that row would have handed the `metic` role an Archon route, and R103's parity test would either have gone red or, worse, ratified the escalation.
79
+
80
+ This ADR makes the row's *outcome* correct but for a different reason, so the row is split anyway: `criterion.create` is its own atom, floor `metic`, **qualified by the goal-ownership wall above** — a floor alone does not describe this permission.
81
+
82
+ ## Alternatives considered
83
+
84
+ - **Leave it Archon; use the proposal queue.** Rejected: it keeps the Archon in the loop for a Metic describing a goal they own, which is the janitorial-delegation problem [ADR 0152](<redacted>.md) already named. The queue survives for the cases that genuinely need adjudication.
85
+ - **Flat re-rank to Metic+.** Rejected — §Context.
86
+ - **Add a DELETE for criteria so the ratchet has an undo** (the [ADR 0152](<redacted>.md) "pair the grant with its inverse" pattern). Deferred, not rejected: it is the honest completion of "manage," and it would neutralise the no-undo objection directly. It is a new capability nobody has today — including Archons — and deleting a criterion with linked `task_criteria` rows needs its own cascade decision. Worth its own task.
@@ -214,5 +214,15 @@ This keeps the decision history honest and traceable.
214
214
  | 0140 | [cloudbongos.com re-provisioned to the ADR 0108 standalone model, DB-PRESERVING — closing ADR 0139's skipped WS-0 (executed 2026-07-12). The site was a lift-and-shift clone of the `example` repo frozen on stale code — every `/builders/*` asset 404'd — because its pull-deploy silently no-op'd against the unchanged game repo (`origin` was `example-owner/example`, not a cloudbongos instance); it had never run the restored code. WS-0's original fresh-DB re-provision would now WIPE the migrated history/balances, so a build-alongside-then-flip variant was used: a dedicated private `cloudbongos/cloudbongos-instance` repo (lean config over the neutral brand + a pinned `@bongos/core@1.17.3`), materialized beside the untouched old checkout, proven on a spare port against the live DB (migrate = clean no-op; leaderboard read migrated balances), then `cloudbongos.service` flipped to the instance + engine and the ADR 0129 pull-timer repointed at the instance repo. Engine source stays authoritative at `example-owner/cloud-bongos` (not relocated); verified DB backup + retained old checkout/unit/deploy-script give instant rollback. Enabled org-wide deploy keys on the `cloudbongos` org so the box's read-only deploy key could attach.](<redacted>.md) | infra / Cloud Bongos |
215
215
  | 0141 | [Cross-site identity — cloudbongos.com becomes a federated identity provider (the "hub") so one platform account reaches any hosted project's builders hall, browser AND CLI, with no second GitHub sign-in (Goal 41 design of record; task [#1002082](https://cloudbongos.com/builders#/task/1002082); implemented across [#1002106](https://cloudbongos.com/builders#/task/1002106)–[#1002115](https://cloudbongos.com/builders#/task/1002115)). Core principle: **identity is federated, authority is not** — the hub carries identity ONLY (github_id, login, display name, avatar), never rank/credits/admission, so [ADR 0016](<redacted>.md)'s self-owned-authority boundary is preserved by construction. Federation is opt-in + reversible via a config switch (`auth.idp`): a hosted instance provisioned with `auth.idp` + `<PREFIX>_HUB_*` routes web sign-in through the hub's `/sso/authorize`→`/sso/token` (OAuth authorization-code, single-use 60s codes, server-to-server client-secret redemption) and CLI sign-in through the hub's device flow + an audience-scoped Ed25519-signed assertion verified offline at a new `/auth/device/exchange`; a self-hosted instance leaves `auth.idp` unset and keeps owning its own GitHub app (the direct-GitHub path, unchanged fallback). Adds a hub `platform-identity` module (accounts / sso_clients / sso_authorization_codes / projects / project_memberships / builder_project_activity), a project→hub membership check-in inside upsertBuilder (coarse role, never rank), a My Projects view with project-executed join/leave, a curated public Explore showcase, per-builder cross-project activity rollups, and an Archon-only cross-project scouting view (upgrading the coarse [ADR 0127](<redacted>.md) fleet gauges to a github_id-keyed store). Two credential types by design: per-client hashed secret (instance→hub server-to-server auth) + a hub Ed25519 keypair (CLI assertions, asymmetric so one instance can't forge for another). Was scoped as "ADR 0135"; that number was reassigned, so it lands here as 0141.](<redacted>.md) | auth / Cloud Bongos identity |
216
216
  | 0142 | [Project context is prose-only; the structured host-identity contract is branding (audit H11 / finding F7; task [#1002224](https://example.com/builders#/task/1002224), goal 1000042). Branding flows as a structured neutral⊕instance→validated-resolver contract because CODE reads those strings; project context (vision/world/visual-style in `docs/project-context.md`, scaffolded from the shipped `docs/project-context.template.md`) is deliberately narrative — read by AI agents, never parsed by code — so it stays prose-only with no parallel resolver/schema/env layer (YAGNI: no consumer; branding already covers code-read strings; the publish boundary + the CLAUDE.md/AGENTS.md stub already enforce the split at the file level per [ADR 0062](<redacted>.md) §7). A new host value that code must read goes in branding, not a new project-context resolver. Revisit only when code (not agents) must read project context as validated, structured data.](<redacted>.md) | core / host-split |
217
+ | 0144 | [Federated single-logout: hub-initiated back-channel session revocation (task [#1002238](https://cloudbongos.com/builders#/task/1002238), goal 41; follow-up to [ADR 0141](<redacted>.md)). ADR 0141 gave single sign-ON but not sign-OFF — a federated instance mints its own local session after the SSO handshake, so hub logout left every project still signed in. Owner chose **back-channel** (server-to-server) over front-channel (browser-driven, breaks under third-party-cookie blocking, incomplete if the tab closes) and soft-sessions (per-request hub re-check — too heavy, hard hub dependency). Core `POST /auth/logout` emits a `builder.logout` seam EVENT (no-op unless a module listens); the hub `platform-identity` module reacts by minting a **hub-signed EdDSA logout token** (`mintHubLogoutToken` — same envelope/key/~120s-TTL/aud-binding as the login assertion) per entered project and POSTing it to each `{origin}/api/gds/auth/backchannel-logout`; that core endpoint (dormant unless `idpConfigured()`, else 404) verifies via `verifyHubLogoutToken` against the hub pubkey and calls `revokeAllSessionsForBuilder`. The token carries only `{sub, purpose:"backchannel-logout"}` — **never** identity/rank (ADR 0016 boundary holds); the **`purpose` marker makes login assertions and logout tokens mutually non-interchangeable** (a login assertion lacks purpose → rejected as logout; a logout token lacks github_login → rejected as login). Entered-instance list = existing `<redacted>` (over-notify is safe — revoke is idempotent); fan-out is best-effort, per-instance 4s timeout, `Promise.allSettled`-isolated, never blocks/fails the logout (hub session revoked synchronously). No new schema, no doorway change; core `1.18.5→1.18.7`; re-vendors BOTH hub + federated instances. V1 = hub-initiated only (instance→hub propagation + a precise per-session ledger + unreachable-instance retry are follow-ups).](<redacted>.md) | auth / Cloud Bongos identity |
218
+ | 0145 | [A free "own-repo" hosted project tier can NOT be a bare `cloudbongos.com` subdomain running the owner's code — a pre-build security review found it unsafe two ways (owner design review, [task 1002249](https://cloudbongos.com/builders#/task/1002249); follow-up to [ADR 0141](<redacted>.md)/[0143](<redacted>.md)). (1) The hub `gds_session` cookie is `Domain=.cloudbongos.com`-scoped (so `builders.`/`status.` share one sign-in), so an owner-controlled `<slug>.cloudbongos.com` instance would receive every signed-in visitor's live hub token → account takeover; `httpOnly`/`sameSite:lax` don't help (same registrable domain = same site). The co-tenant instances that safely federate on cloudbongos.com subdomains (e.g. `demo.`) do so only because they run PLATFORM code, not owner code — so the precedent doesn't extend. (2) A control-plane-local `standalone` runs owner code on the hub box as `lars` with passwordless sudo in its `npm ci` deploy → root-on-hub (pre-existing, independent of federation). The federation protocol itself is sound (origin-bound, asymmetric Ed25519); the weakness is PLACEMENT + EXECUTION. **Decision:** a free own-repo tier is delivered ONLY behind (a) domain separation — free instances under a different registrable domain than the hub, so they never get the cookie — AND (b) per-project isolation (own droplet / container, distinct UID, `--ignore-scripts`, no passwordless sudo); only then is federation safe to extend. Owner chose this direction over the co-tenant free tier (safe/$0 but no own repo) and the honest-copy status quo. Build blocked on owner decisions: which separate domain, droplet-vs-container isolation, and the cost model (paid tier vs capped subsidy — [§4](../../CLAUDE.md)). Points 1&2 of the review (repo-step polish) were unrelated + already shipped ([task 1002247](https://cloudbongos.com/builders#/task/1002247)).](<redacted>.md) | auth / provisioning / Cloud Bongos identity |
219
+ | 0147 | [Carry ADR 0110's deploy-regen of generated docs to STANDALONE instances ([task 1002314](https://cloudbongos.com/builders#/task/1002314); the owner's "docs didn't transfer from primea"). The monorepo keeps its generated nav docs live because ADR 0110's regen is wired into `deploy-prod.sh`; standalone instances ([ADR 0108](<redacted>.md)) never got it — their bespoke pull-deploy regenerated nothing and `init`/`upgrade` only did the API docs, so cloudbongos.com's `docs/` was frozen with just the 3 API files. Two defects: (1) `gen-session-index.js` hardcoded the CORE root (`path.resolve(__dirname,'..','..')`) so on a standalone box it indexed `node_modules/@bongos/core`, not the instance — now `resolveInstanceRoot()`; (2) no standalone regen path. Fix: `upgrade.regenerateNavDocs()` + a `regen-instance-docs.js` CLI, wired at every refresh point (init seed, `bongos upgrade`, `provision.js` standalone deploy, and a bespoke `deploy.sh` one-liner); `INSTANCE_GITIGNORE` ignores the whole-file docs. Scope is `session-index` ONLY — the one gitignored, whole-file, instance-content nav doc; `repo-map` is core-scoped (empty for a code-less standalone; brownfield map = task 1001655), `file-map` + API docs are tracked/committed (file-map seeding at standup = follow-up), diagrams are the headless-browser exception.](<redacted>.md) | docs pipeline / Cloud Bongos |
220
+ | 0146 | [Configurable reward mode — an instance chooses whether a shipped task pays BOTH reward streams or ONLY the cost-plus one ([task 1002270](https://cloudbongos.com/builders#/task/1002270), goal 1000037; follow-up to [ADR 0120](<redacted>.md)). A shipped task pays two streams onto one balance (the [ADR 0054](<redacted>.md) "two conventions, one ledger" tension): the per-task author ESTIMATE (`credits_reward` × kind multiplier — unitless, [ADR 0023](<redacted>.md)) AND the cost-plus SESSION reward (`round(true_cost_usd × 1.20)`, 1 credit = $1 — ADR 0054). New `branding.reward.mode`: `"cost-plus-and-estimate"` (DEFAULT — pay both, exactly today's behavior; every existing instance unchanged on upgrade) or `"cost-plus-only"` (the cost-plus reward is the SOLE equity reward; the per-task estimate PAYOUT is withheld and its idea-promotion slice cascades to 0). Resolved by a pure `rewardPolicy()`; gated in the single per-task credit writer (`insertMultipliedConfirmCredit`) — entirely inside the economy module, ZERO lifecycle changes. `credits_reward` still rides the task row for display + the no-zero-reward claim gate; only the payout is suppressed. Unknown mode → default (a typo can't silently zero rewards). Cloud Bongos selects `cost-plus-only`; achievements + the net-negative +1 bonus are out of scope. Backward-compatible + reversible — no clawback, no migration.](<redacted><redacted>.md) | GDS / economy |
221
+ | 0149 | [`bongos dev` — a one-command LOCAL launcher for a standalone instance repo (owner ask, 2026-07-19: "we need a better option to stand up the local host"). A freshly-created project's `config/branding.json` points at `http://localhost:<port>` — configured to run on the owner's machine — but the platform shipped no command to START it there; `bongos onboard` provisions a CLOUD server and Docker serves a vanilla brand on `:3000`. Standing it up by hand was a six-part assembly (Postgres → createdb → migrate → PORT/PGDATABASE/PGHOST/OAuth env → server → login) with three silent mismatches: the server reads PORT from env not branding (platform-server.js:44); the pool DB name collapses to the OS user while migrate.sh defaults to `example` (pool.js:47 vs migrate.sh:52) unless PGDATABASE is set for BOTH; and node-pg defaults to the Linux peer socket `/var/run/postgresql` (pool.js:46), absent under Homebrew on a Mac. `bongos dev` does the whole thing idempotently from one source of truth (branding): ensures Postgres, createdb, migrate (`MEDUSA_GDS_ONLY=1`), starts the server on the branding port, and signs in. The background server is MANAGED (pid/log per instance) with `bongos dev stop`/`status` — the fix for the orphaned-server accident (a stale `:3005` answering a new project's login). Pure resolution + process mgmt in `scripts/gds/dev-lib.js` (unit-tested `tests/dev_lib.mjs`); orchestration in `scripts/gds/dev.js`. Alternatives rejected: cloud dev box (needs hosting + cost — kept for remote work), Docker (vanilla brand/wrong port — kept for Windows), recipe-only (leaves the six steps). Not bundled into the standalone on-ramp binary (needs a real checkout).](<redacted>.md) | Cloud Bongos / dev experience |
222
+ | 0150 | [Box first-boot bring-up on vendored instances — resolve `infra/` from the instance root + fail loud on empty user-data (spike BV1.R97, [task 1002385](https://cloudbongos.com/builders#/task/1002385); C2 of goals 1000051/1000052, "make cloudbongos boxes stop coming up bare"). Root cause: `box.js` sets `REPO_ROOT` two dirs up from itself, which on a VENDORED instance is the `@bongos/core` package — but the R89 packaging split kept `infra/` (the DO cloud-init template + inlined `box-source-fetch.sh`/cloudflared/ttyd/heartbeat scripts) as INSTANCE content, not in the core publish-manifest. So `readCloudInit` reads a missing template, its `try/catch` returns `null`, and the caller's only guard (`0 > 64KiB`) passes → the box is created with EMPTY user-data and boots bare Ubuntu (no clone, no toolchain, no terminal); `fileGzB64` is likewise fail-soft (warns, returns `''`) so a partial `infra/` half-wires. Live re-confirmed 2026-07-19: instance-root `infra/` present, vendored-core `infra/` gone (the [ADR 0144](<redacted>.md) node_modules workaround wiped by a reinstall), fleet idle so the bug is latent-but-live (next provision boots bare). Fix (R98, [task 1002386](https://cloudbongos.com/builders#/task/1002386)): a `resolveBoxInfraDir()` locating `infra/` via [ADR 0108](<redacted>.md)'s `resolveInstanceRootExplicit()` (cloudbongos `bongos.config.json` = `{"instanceRoot":"."}`) + an env override + a walk-up-out-of-`node_modules` fallback, AND fail-loud backstops (throw on missing template; abort provision on empty user-data). R99 ([task 1002387](https://cloudbongos.com/builders#/task/1002387)) then ensures the toolchain builds on the box. Reinstall-proof + portable; core-only, no migration/doorway change.](<redacted>.md) | GDS / dev boxes |
223
+ | 0151 | [Governance: **permissions are the atom, ranks become seeded roles** — the foundation of the BONGOS-V1 Governance goal (1000030), spike BV1.R90 ([task 1001838](https://cloudbongos.com/builders#/task/1001838)); settles the model in an ADR before R93–R114 build. Authority stops being an all-or-nothing rank threshold ([ADR 0016](<redacted>.md)/0018/0034) and becomes granular **permissions** (code constants that map 1:1 to real routes/surfaces — a permission no route consumes doesn't exist), bundled into assignable **roles**; the four live ranks become four seeded roles whose grants reproduce today's thresholds EXACTLY (the compat seed — cumulative nested supersets, so the resolver's UNION preserves the at-or-above threshold), and `builders.rank` stays as the identity/graduation column. A per-request **uncached** `resolveBuilderPermissions()` (kernel port, preserving ADR 0016's instant-demotion) + a `requirePermission()` gate that coexists with `requireRank` via the seed → gates migrate route-by-route, no flag day. The **complete authority model** (C3) represents four axes as first-class types, not rank special-cases: rank-seeded roles, machine **principals** (`system_role`=bfg/discord_bot, exact-identity gate, never UI-grantable), **session-scopes** (box tokens, deny-by-default allow-list intersected with the builder's roles), and **ownership-scoped** perms (`X.manage.own` ≠ `.any`; goal owner/manager per [ADR 0106](<redacted>.md)). The safety keystone (C6): every permission carries a **`system` flag** — `system:true` (deploy, protected-path, rank-set, version-close, grade-bypass, cross-tier edges) is **non-grantable** and LOCKED in the tab, so no configurable role can hand out the crown jewels; and the today-hardcoded `PROTECTED_GLOBS`/`EXPECTED_RANKS` map becomes a **governance-owned protected-surface registry** that the OUT-OF-BAND substrate (pre-push hook, grader pre-pass, `main-audit.js`, gate-review — [ADR 0043](<redacted>.md)) READS but never moves in-process: **policy owns / substrate enforces** — the tab can re-map the floor but a forced `system` grant still can't deploy (abuse-matrix + live-demo proofs, R112/R114). Capability in **core** (`modules/governance/`, default:true); role templates are per-instance **config** (`config/governance.json` over `config/governance.neutral.json` over env, mirroring `src/branding.js`), live assignments in the DB. Collapses the ~6 duplicated numeric rank-tier maps (the Builder-25-incident drift class) behind the resolver. Zero day-one behavior change (parity test R103); one deliberate fail-closed divergence (unwired divine ranks map to no role instead of latently inheriting Archon). Includes the full enumerated permission catalog (every key + `system` flag) grounded in the 2026-07-24 authority sweep. No new recurring cost.](<redacted>.md) | governance / permissions / Cloud Bongos |
224
+ | 0152 | [The landing gate is served by the **core app**, not by the front proxy — the cutover step of the landing migration ([task 1002411](https://example.com/builders#/task/1002411), L05 of goal 1000035). The reference instance's orb-gate front page was `file_server`'d by Caddy straight out of a checkout of an UNRELATED product repo (`root * <checkout>/brand/cloud-bongos`), so the landing had two sources of truth, no deploy path of its own, and a cross-product coupling the core could not express. [L01](https://example.com/builders#/task/1002407) had already ported the bundle into the core as `public-landing/` and [L02](https://example.com/builders#/task/1002408) had already taught `platform-server.js` to serve it (`/` -> 302 `/gate/`, `/gate/` serves the index raw, `express.static` covers the assets) — so by the time L05 came up, BOTH halves of its original plan ("give the landing (a) its own Caddy config and (b) its own deploy script") had dissolved. **Decision:** the app owns the landing end to end; the proxy drops `root` + `file_server` and keeps exactly three jobs on that surface — TLS, an optional pre-launch `basic_auth` lock, and the `@hidden` guard that keeps `gate/README.md` unreachable. No separate Caddy config to write (the resulting block IS the generic pure-proxy shape `provision.js`'s `caddyBlock()` already emits per instance, [ADR 0111](<redacted>.md) §4; host-specific hostnames/certs stay on the host per [ADR 0062](<redacted>.md) §7) and no separate deploy script to write (the landing ships INSIDE the pinned core package, so it rides the instance's existing pull-deploy — one artifact, one version, one path). Keeps `redir / /gate/ 302` because Caddy orders `redir` before `basic_auth`, so "/" behaves identically before and after, and the app issues the same 302 once the lock is lifted. Cost accepted: the landing now sits in the app's request path (a dead app = a dead front page — but the gate's whole purpose is to reach the app), and a landing tweak now needs a core release. Verified on the live instance: identical status codes across the apex and both vanity doors before/after, relative redirects (no origin leak), and a **severance test** — with the foreign repo's landing folder moved aside entirely the site still served the byte-identical gate, proving the dependency is cut rather than merely unused. Retiring the foreign copy is L06 ([task 1002412](https://example.com/builders#/task/1002412)).](<redacted>.md) | platform serving / instance infra |
225
+ | 0152 | [**Metic gains task abandonment, paired with an un-abandon at the same rank** (owner directive 2026-07-27: "Metics should also be able to abandon tasks"; [task 1002476](https://cloudbongos.com/builders#/task/1002476)). [ADR 0090](<redacted>.md) moved task authoring (create/edit/promote/deps/criteria) down to the trusted working rank but kept `POST /tasks/:id/abandon` Archon-only as "terminal scope removal" — so the rank that runs idea-triage, seeds the tasks, and shapes them still had to hand every duplicate/moot/superseded chore back to the owner to retire. That hand-back is janitorial, not adjudication. The real hazard was never the grant: abandon was **one-way through the API** (`/promote` took only backlog/blocked, `PATCH` refuses `status`), so a mis-retire needed hand-SQL on prod, and an abandoned task is invisible in every claimable list and every version rollup (migration 017 excludes it from `version_progress`) — nobody notices until the work silently doesn't happen. So the gate moves to `requireRank('metic','archon')` **and** `/promote` gains a third source state, `abandoned → backlog` (never straight to ready — a retired task re-enters un-workable and takes a second deliberate promote), backed by `db.restoreTask`, the mirror of `abandonTask`, which clears the `'Abandoned: <reason>'` stamp and only that. Unchanged: the shipped-task and active-claim refusals, idempotency, and the `audit_log` record of who retired what and who brought it back. The `EXPECTED_RANKS` lifecycle pin moves in lockstep (a *further* downgrade stays a hard CI fail); under [ADR 0151](<redacted>.md) the `task.abandon` floor moves archon→metic, landing as `governance_003` because the compat seed is run-once — purely additive, and the drift guard now compares the **union** of grant migrations to the catalog and fails loudly on a future revocation.](<redacted>.md) | GDS / permissions |
226
+ | 0153 | [**Sequential chain execution — one kickoff walks a dependency chain** ([task 1002484](https://cloudbongos.com/builders#/task/1002484)). [ADR 0015](<redacted>.md) made `dependencies[]` load-bearing and auto-promotes a task's dependents `backlog`→`ready` on ship — but nothing *picks the unblocked task up*, so on the live instance **146 of 303 open tasks sit behind a predecessor** and a 24-task planning seed needed 24 separate human kickoffs. The only "sequential" affordance was a clipboard prompt on the Tasks board (`<redacted>`), which is text rather than execution and cannot even select the gated successors, because they are still `backlog`. Requested independently by two builders (ideas 1000461, 1000398). **Decision:** a chain runner with **no schema change** (the polymorphic `dependencies` table + `task_is_fully_unblocked` + the promote-on-ship trigger already encode the order; a `chain_id`/queue column would be a second, drift-prone source of truth) and **no new claim guard** (one-active-claim-per-session is keyed on the *working tree*, so ship-before-next-claim is already the enforced shape — and batch-claiming a chain is impossible anyway, since successors fail `DEPS_NOT_SHIPPED` until their predecessor ships; the ship IS what unblocks the next step). Deterministic plumbing (`scripts/gds/sequence.js` — scope, topological order, halt matrix, all read off the task row) is split from judgement (`.claude/skills/builder-sequence/`, which defers to `/builder-claim` + `/dev` + `/builder-ship`). **Halt, do not skip** (owner decision): on `needs_migration`, `security_sensitive`, `protected_path`, `rank_floor`, `thin_spec`, `judgement_kind`, `not_claimable`, `deps_unmet`, `in_flight`, or `cycle`, the runner stops and reports with the task still claimed — because in a dependency graph a skipped task is a *predecessor*, so skipping strands everything behind it while the run keeps posting apparent progress. Not built: chains as a priced claimable unit on the board (idea 1000398), which is additive.](<redacted>.md) | GDS / methodology |
217
227
 
218
228
  > ⚠️ **Numbering collision (known):** two ADRs share `0008`. The numbers were assigned in parallel sessions before the lock could be enforced. The files are kept under their existing filenames to preserve git history, but the table disambiguates them as `0008-a` / `0008-b`. New ADRs should resume at `0040` to avoid further collisions. (`0024` is also a real on-disk duplicate: the cloneable-repo/local-first-memory ADR and the multi-agent-system-architecture ADR were both numbered `0024` in parallel; both keep their filenames, disambiguated as `0024-a` / `0024-b` like `0008`. Likewise `0025` ended up with two on-disk files — the offsite-backup ADR and the structured-criterion↔task-link ADR — both kept under their existing filenames and disambiguated as `0025-a` / `0025-b`; separately, the BFG ADR had also claimed `0025` on 2026-05-30 and yielded to `0026`. A third happened 2026-06-06: `0036` was claimed in parallel by the builders-subdomain ADR ([#726](https://example.com/builders#/task/726)) and this Discord-channels ADR ([#727](https://example.com/builders#/task/727)); the Discord-channels ADR yielded to `0037`. A fourth landed 2026-06-10: `0044` was claimed in parallel by the palette-replacement ADR and the per-box game-preview ADR ([#897](https://example.com/builders#/task/897)); both keep their filenames, disambiguated as `0044-a` / `0044-b` like `0008`. A fifth was caught pre-land 2026-06-13: `0050` was claimed in parallel by the device-flow-admission ADR ([#1000](https://example.com/builders#/task/1000)) and the sandbox-for-everyone ADR ([#1001](https://example.com/builders#/task/1001)); the latter yielded to `0052` before merging, so there is no `0050` duplicate. A sixth was caught pre-land 2026-06-21: the tool-agnostic-design-layer ADR ([#1375](https://example.com/builders#/task/1375)) first collided at `0079` with the value-gated-autonomous-engagement ADR, then at `0080` with the llm-cache-api-transport ADR (both part of the 0075–0080 LLM-cost-reduction series, landed on `main` in parallel); it yielded to `0081` before merging, so there is no `0079`/`0080` design-layer duplicate. A seventh landed 2026-06-23: `0084` was claimed in parallel by the auto-derived-per-task-rank-floor ADR and the remove-default-spend-cap ADR (task 1499); the rank-floor ADR landed on `main` first, so the spend-cap ADR yielded to `0085`, leaving no `0084` duplicate. An eighth landed 2026-06-27: `0095` was claimed in parallel by the borrowed-memory-and-retrieval-concepts ADR (PR #459, landed first) and the cross-agent-context-management ADR ([#1651](https://example.com/builders#/task/1651), PR #460); both keep their filenames, disambiguated as `0095-a` / `0095-b` like `0008`. These two are companions — `0095-a` adopts the memory/retrieval concepts and explicitly defers the grep-vs-semantic question to `0095-b`.)