@bongos/core 1.23.21 → 1.24.1

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 (457) hide show
  1. package/.bongos-core.json +983 -428
  2. package/.claude/skills/blocker-review/SKILL.md +9 -1
  3. package/.claude/skills/blocker-solve/SKILL.md +1 -1
  4. package/.claude/skills/builder-claim/SKILL.md +1 -1
  5. package/.claude/skills/builder-reauth/SKILL.md +1 -1
  6. package/.claude/skills/builder-redteam/SKILL.md +1 -1
  7. package/.claude/skills/builder-sequence/SKILL.md +1 -1
  8. package/.claude/skills/builder-setup/SKILL.md +1 -1
  9. package/.claude/skills/builder-ship/SKILL.md +1 -1
  10. package/.claude/skills/collab-review/SKILL.md +5 -2
  11. package/.claude/skills/design/SKILL.md +1 -1
  12. package/.claude/skills/goal-create/SKILL.md +6 -3
  13. package/.claude/skills/grade-recover/SKILL.md +1 -1
  14. package/.claude/skills/merge-mode/SKILL.md +1 -1
  15. package/.claude/skills/owner-review/SKILL.md +1 -1
  16. package/.claude/skills/planning-session/SKILL.md +11 -11
  17. package/.claude/skills/read-session-export/SKILL.md +1 -1
  18. package/.claude/skills/recall/SKILL.md +1 -1
  19. package/.claude/skills/scan-before-install/SKILL.md +1 -1
  20. package/.claude/skills/session-handoff/SKILL.md +1 -1
  21. package/.claude/skills/strand-fix/SKILL.md +13 -0
  22. package/.claude/skills/worktree-clean/SKILL.md +1 -1
  23. package/AGENTS.md +15 -0
  24. package/GEMINI.md +5 -0
  25. package/clients/bongos-client/README.md +1 -1
  26. package/clients/bongos-client/bongos-client.global.js +94 -0
  27. package/clients/bongos-client/index.cjs +94 -0
  28. package/clients/bongos-client/index.d.ts +154 -4
  29. package/clients/bongos-client/index.mjs +94 -0
  30. package/docs/adr/0054-session-token-reward-cost-plus.md +3 -1
  31. package/docs/adr/0055-server-mediated-branch-publish.md +1 -1
  32. package/docs/adr/0385-money-lives-with-a-processor-credits-stay-a-project-meter.md +87 -0
  33. package/docs/adr/0386-the-funding-wall-is-a-per-project-rule-claim-time-hold-by-default.md +37 -0
  34. package/docs/adr/0387-two-ledgers-credits-measure-work-money-stays-with-the-processor.md +39 -0
  35. package/docs/adr/0388-a-module-sale-is-split-by-the-project-the-buyer-pays-the-platform-fee.md +42 -0
  36. package/docs/adr/0392-ai-generation-runs-on-a-processor-held-balance-and-the-builders-own-keys.md +33 -0
  37. package/docs/adr/0393-a-platform-account-is-a-person-and-github-is-one-way-in.md +37 -0
  38. package/docs/adr/0394-market-watch-searches-through-the-messages-api-on-the-projects-key-and-runs-from-its-own-poller.md +102 -0
  39. package/docs/adr/0395-a-task-says-what-it-needs-and-lists-show-only-what-you-can-do.md +36 -0
  40. package/docs/adr/0396-google-sign-in-a-stand-in-login-and-a-browser-approved-cli-sign-in.md +41 -0
  41. package/docs/adr/README.md +10 -1
  42. package/docs/api/openapi.json +8619 -5885
  43. package/docs/api-reference.md +68 -9
  44. package/docs/architecture.md +37 -1
  45. package/docs/canonical-permissions.md +1 -1
  46. package/docs/copy-inventory.md +420 -337
  47. package/docs/copy-registry.json +1321 -500
  48. package/docs/file-map.md +4 -0
  49. package/docs/module-api-changelog.md +7 -0
  50. package/docs/onboarding/diagrams/03-drachmae-karma.mmd +3 -3
  51. package/docs/onboarding/diagrams/README.md +1 -1
  52. package/docs/onboarding/drachmae-and-karma.md +15 -15
  53. package/docs/onboarding/primer.md +2 -2
  54. package/docs/page-inventory.json +55 -6
  55. package/docs/page-readings.json +1185 -1011
  56. package/docs/recipes/google-sign-in.md +60 -0
  57. package/docs/recipes/ops-gotchas.md +1 -1
  58. package/migrations/core_282_builders_lower_login_idx.sql +33 -0
  59. package/migrations/core_283_claimable_ignores_page_markers.sql +53 -0
  60. package/migrations/core_283_payout_approvals.sql +183 -0
  61. package/migrations/core_284_payout_gates.sql +179 -0
  62. package/migrations/core_285_task_needs.sql +74 -0
  63. package/modules/ai-studio/CLAUDE.md +15 -0
  64. package/modules/ai-studio/billing.js +67 -0
  65. package/modules/ai-studio/context.js +29 -0
  66. package/modules/ai-studio/db.js +172 -0
  67. package/modules/ai-studio/gemini.js +75 -0
  68. package/modules/ai-studio/generate.js +119 -0
  69. package/modules/ai-studio/keys.js +73 -0
  70. package/modules/ai-studio/migrations/ai_studio_001_keys.sql +22 -0
  71. package/modules/ai-studio/migrations/ai_studio_002_ledger.sql +48 -0
  72. package/modules/ai-studio/migrations/ai_studio_003_requests.sql +55 -0
  73. package/modules/ai-studio/module.json +26 -0
  74. package/modules/ai-studio/openrouter.js +90 -0
  75. package/modules/ai-studio/payments.js +105 -0
  76. package/modules/ai-studio/pricing.js +92 -0
  77. package/modules/ai-studio/routes/ai-studio.js +252 -0
  78. package/modules/ai-studio/studio.js +121 -0
  79. package/modules/autonomy/fence.js +64 -1
  80. package/modules/builder-settings/builder-needs.js +27 -1
  81. package/modules/copy-desk/routes/copy-desk.js +5 -0
  82. package/modules/economy/achievements.js +22 -10
  83. package/modules/economy/cost-classify.js +9 -0
  84. package/modules/economy/cost-write-guard.js +52 -0
  85. package/modules/economy/cost.js +2 -1
  86. package/modules/economy/credits.js +18 -15
  87. package/modules/economy/goal-reward.js +161 -0
  88. package/modules/economy/reward.js +4 -0
  89. package/modules/economy/routes/cost.js +33 -7
  90. package/modules/government/catalog.js +6 -0
  91. package/modules/government/db.js +108 -0
  92. package/modules/government/docket.js +36 -0
  93. package/modules/government/migrations/government_035_payout_approve.sql +29 -0
  94. package/modules/government/pollers/board-expiry.js +108 -20
  95. package/modules/government/protected-surfaces.json +1 -0
  96. package/modules/government/routes/government.js +50 -18
  97. package/modules/government/routing.js +39 -5
  98. package/modules/government/waiting.js +74 -0
  99. package/modules/grading/grader-subagent.js +30 -11
  100. package/modules/hall-ui/public/ai-studio-lib.js +268 -0
  101. package/modules/hall-ui/public/ai-studio.css +193 -0
  102. package/modules/hall-ui/public/ai-studio.html +98 -0
  103. package/modules/hall-ui/public/ai-studio.js +556 -0
  104. package/modules/hall-ui/public/ai-studio.states.json +94 -0
  105. package/modules/hall-ui/public/blockers-by-person.js +156 -0
  106. package/modules/hall-ui/public/blockers-lib.js +15 -1
  107. package/modules/hall-ui/public/blockers.html +8 -0
  108. package/modules/hall-ui/public/blockers.js +1 -1
  109. package/modules/hall-ui/public/city.html +15 -15
  110. package/modules/hall-ui/public/claim-action.js +3 -0
  111. package/modules/hall-ui/public/collab.html +12 -0
  112. package/modules/hall-ui/public/craft-mode.js +4 -1
  113. package/modules/hall-ui/public/criterion-uat-ui.js +4 -1
  114. package/modules/hall-ui/public/drachmae.html +1 -1
  115. package/modules/hall-ui/public/genesis/r5-life-1-primordial-sea.jpg +0 -0
  116. package/modules/hall-ui/public/genesis/r5-life-2-first-cells-cut.webp +0 -0
  117. package/modules/hall-ui/public/genesis/r5-life-2-first-cells.jpg +0 -0
  118. package/modules/hall-ui/public/genesis/r5-life-3-genome-cut.webp +0 -0
  119. package/modules/hall-ui/public/genesis/r5-life-3-genome.jpg +0 -0
  120. package/modules/hall-ui/public/genesis/r5-life-4-membrane-cut.webp +0 -0
  121. package/modules/hall-ui/public/genesis/r5-life-4-membrane.jpg +0 -0
  122. package/modules/hall-ui/public/genesis/r5-life-5-nervous-system-cut.webp +0 -0
  123. package/modules/hall-ui/public/genesis/r5-life-5-nervous-system.jpg +0 -0
  124. package/modules/hall-ui/public/goal-inbox.js +1 -1
  125. package/modules/hall-ui/public/goals-render.js +4 -0
  126. package/modules/hall-ui/public/goals.html +8 -0
  127. package/modules/hall-ui/public/gov-living.js +6 -2
  128. package/modules/hall-ui/public/hall-ask.css +5 -0
  129. package/modules/hall-ui/public/hall-ask.js +39 -3
  130. package/modules/hall-ui/public/notes-panel.css +20 -0
  131. package/modules/hall-ui/public/notes-panel.js +146 -0
  132. package/modules/hall-ui/public/project-settings.html +1 -1
  133. package/modules/hall-ui/public/project-settings.js +7 -5
  134. package/modules/hall-ui/public/settings-ai-keys.js +124 -0
  135. package/modules/hall-ui/public/settings.html +15 -0
  136. package/modules/hall-ui/public/street-deck.js +12 -3
  137. package/modules/hall-ui/public/studio.css +90 -8
  138. package/modules/hall-ui/public/studio.html +39 -10
  139. package/modules/hall-ui/public/studio.js +148 -18
  140. package/modules/hall-ui/public/task.html +2 -0
  141. package/modules/hall-ui/public/task.js +4 -0
  142. package/modules/hall-ui/public/tweak-editor.js +12 -3
  143. package/modules/hall-ui/public/welcome-boot.css +42 -6
  144. package/modules/hall-ui/public/welcome-boot.js +20 -6
  145. package/modules/hall-ui/public/work.js +20 -0
  146. package/modules/hall-ui/records/ai-studio.md +36 -0
  147. package/modules/hall-ui/records/blockers-people.md +24 -0
  148. package/modules/lifecycle/db-analytics.js +10 -3
  149. package/modules/lifecycle/db-claims.js +32 -4
  150. package/modules/lifecycle/db-goals.js +48 -18
  151. package/modules/lifecycle/db-shared.js +9 -0
  152. package/modules/lifecycle/db-tasks.js +15 -6
  153. package/modules/lifecycle/db-versions.js +21 -8
  154. package/modules/lifecycle/done-when.js +23 -1
  155. package/modules/lifecycle/lifecycle.js +5 -0
  156. package/modules/lifecycle/migrations/lifecycle_017_notes.sql +46 -0
  157. package/modules/lifecycle/module.json +2 -1
  158. package/modules/lifecycle/notes.js +219 -0
  159. package/modules/lifecycle/page-notice.js +124 -0
  160. package/modules/lifecycle/page-tweak-artist.js +18 -1
  161. package/modules/lifecycle/publish-reconciler.js +67 -20
  162. package/modules/lifecycle/routes/claims.js +5 -2
  163. package/modules/lifecycle/routes/goals.js +3 -1
  164. package/modules/lifecycle/routes/notes.js +91 -0
  165. package/modules/lifecycle/routes/project-settings.js +43 -35
  166. package/modules/lifecycle/routes/task-route-helpers.js +21 -0
  167. package/modules/lifecycle/routes/task-write-routes.js +21 -3
  168. package/modules/lifecycle/routes/tasks.js +12 -12
  169. package/modules/lifecycle/ship-assurance.js +4 -1
  170. package/modules/lifecycle/ship-card.js +8 -2
  171. package/modules/lifecycle/task-needs-gate.js +70 -0
  172. package/modules/market-watch/CLAUDE.md +15 -0
  173. package/modules/market-watch/db.js +340 -0
  174. package/modules/market-watch/key.js +58 -0
  175. package/modules/market-watch/migrations/market_watch_001_tables.sql +249 -0
  176. package/modules/market-watch/migrations/market_watch_002_run.sql +46 -0
  177. package/modules/market-watch/module.json +26 -0
  178. package/modules/market-watch/pollers/hub-seed.js +54 -0
  179. package/modules/market-watch/pollers/weekly-run.js +100 -0
  180. package/modules/market-watch/research.js +350 -0
  181. package/modules/market-watch/run.js +245 -0
  182. package/modules/market-watch/seed/market-data.json +38 -0
  183. package/modules/market-watch/seed/partners-data.json +497 -0
  184. package/modules/market-watch/seed.js +252 -0
  185. package/modules/market-watch/store.js +230 -0
  186. package/modules/memory/routes/memory.js +14 -2
  187. package/modules/memory/routes/search.js +8 -2
  188. package/modules/onboarding/howto-moments.js +8 -1
  189. package/modules/platform-identity/account-logins.js +192 -0
  190. package/modules/platform-identity/migrations/platform_identity_034_account_logins.sql +96 -0
  191. package/modules/platform-identity/platform-identity.js +5 -4
  192. package/modules/platform-identity/routes/my-projects.js +6 -0
  193. package/modules/platform-identity/routes/profile.js +18 -0
  194. package/modules/platform-identity/routes/public-profile.js +10 -1
  195. package/modules/platform-identity/routes/sso.js +26 -1
  196. package/modules/platform-identity/tests/platform-identity.mjs +2 -1
  197. package/modules/provisioning/credential-preflight.js +13 -3
  198. package/modules/provisioning/pollers/app-liveness.js +25 -5
  199. package/modules/provisioning/routes/provisioning.js +1 -1
  200. package/modules/provisioning/starter-bundles.js +8 -2
  201. package/modules/public-landing/public/account.html +10 -2
  202. package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg +0 -0
  203. package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg.json +1 -1
  204. package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp +0 -0
  205. package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp.json +1 -1
  206. package/modules/public-landing/public/assets/world/r2-chrome-blob-one-cut.webp +0 -0
  207. package/modules/public-landing/public/assets/world/r2-chrome-blob-one-cut.webp.json +1 -1
  208. package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg +0 -0
  209. package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg.json +1 -1
  210. package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp +0 -0
  211. package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp.json +1 -1
  212. package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg +0 -0
  213. package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg.json +1 -1
  214. package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp +0 -0
  215. package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp.json +1 -1
  216. package/modules/public-landing/public/assets/world/r3-frost-bead-cut.webp +0 -0
  217. package/modules/public-landing/public/assets/world/r3-frost-bead-cut.webp.json +1 -1
  218. package/modules/public-landing/public/assets/world/r3-frost-pair-cut.webp +0 -0
  219. package/modules/public-landing/public/assets/world/r3-frost-pair-cut.webp.json +1 -1
  220. package/modules/public-landing/public/assets/world/r3-gold-blob-cut.webp +0 -0
  221. package/modules/public-landing/public/assets/world/r3-gold-blob-cut.webp.json +1 -1
  222. package/modules/public-landing/public/assets/world/r3-gold-pair-cut.webp +0 -0
  223. package/modules/public-landing/public/assets/world/r3-gold-pair-cut.webp.json +1 -1
  224. package/modules/public-landing/public/assets/world/r3-obsidian-blob-cut.webp +0 -0
  225. package/modules/public-landing/public/assets/world/r3-obsidian-blob-cut.webp.json +1 -1
  226. package/modules/public-landing/public/assets/world/r3-obsidian-pair-cut.webp +0 -0
  227. package/modules/public-landing/public/assets/world/r3-obsidian-pair-cut.webp.json +1 -1
  228. package/modules/public-landing/public/assets/world/r3-space-nebula.jpg +0 -0
  229. package/modules/public-landing/public/assets/world/r3-space-nebula.jpg.json +1 -1
  230. package/modules/public-landing/public/assets/world/r4-card-chrome.jpg +0 -0
  231. package/modules/public-landing/public/assets/world/r4-card-chrome.jpg.json +1 -1
  232. package/modules/public-landing/public/assets/world/r4-card-nebula.jpg +0 -0
  233. package/modules/public-landing/public/assets/world/r4-card-nebula.jpg.json +1 -1
  234. package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg +0 -0
  235. package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg.json +1 -1
  236. package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp +0 -0
  237. package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp.json +1 -1
  238. package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg +0 -0
  239. package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg.json +1 -1
  240. package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp +0 -0
  241. package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp.json +1 -1
  242. package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg +0 -0
  243. package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg.json +1 -1
  244. package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp +0 -0
  245. package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp.json +1 -1
  246. package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg +0 -0
  247. package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg.json +1 -1
  248. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp +0 -0
  249. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp.json +1 -1
  250. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg +0 -0
  251. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg.json +1 -1
  252. package/modules/public-landing/public/community.html +1 -1
  253. package/modules/public-landing/public/connections.html +2 -2
  254. package/modules/public-landing/public/guild.html +1 -1
  255. package/modules/public-landing/public/invitations.html +2 -2
  256. package/modules/public-landing/public/projects.html +5 -5
  257. package/modules/raise/views/nda.html +2 -0
  258. package/modules/specialities/db.js +32 -1
  259. package/modules/specialities/routes/specialities.js +10 -6
  260. package/modules/status-ui/public/index.html +1 -1
  261. package/package-lock.json +2 -2
  262. package/package.json +1 -1
  263. package/release-notes.json +286 -0
  264. package/scripts/bongos/autobongos-tweak-lane.js +46 -1
  265. package/scripts/bongos/backfill-subagent-rewards.js +2 -2
  266. package/scripts/bongos/claim.js +22 -1
  267. package/scripts/bongos/db-backup-nightly.sh +29 -0
  268. package/scripts/bongos/deploy-divergence.js +7 -0
  269. package/scripts/bongos/doc-cli-guard.js +6 -0
  270. package/scripts/bongos/fitness.js +3 -2
  271. package/scripts/bongos/gen-diagrams.js +3 -3
  272. package/scripts/bongos/lib.sh +4 -5
  273. package/scripts/bongos/migration-namespace.js +13 -1
  274. package/scripts/bongos/plain-cards.js +4 -0
  275. package/scripts/bongos/provision-github-setup.js +3 -1
  276. package/scripts/bongos/provision-units.js +6 -1
  277. package/scripts/bongos/publish-manifest.js +4 -0
  278. package/scripts/bongos/release.js +2 -0
  279. package/scripts/bongos/run-unit-tests.js +268 -82
  280. package/scripts/bongos/seed-tasks.example.json +56 -0
  281. package/scripts/bongos/seed-tasks.js +222 -0
  282. package/scripts/bongos/seed-verify-criteria.js +1 -2
  283. package/scripts/bongos/ship-finish.js +2 -0
  284. package/scripts/bongos/ship-resume.js +2 -0
  285. package/scripts/bongos/strand-lease.js +250 -0
  286. package/scripts/bongos/strand-watch.js +129 -12
  287. package/scripts/bongos/version-literal-guard.js +2 -2
  288. package/scripts/discord/post.js +4 -3
  289. package/scripts/gds/seed-tasks.js +7 -0
  290. package/scripts/gds/{build-bongos.js → strand-lease.js} +3 -3
  291. package/scripts/hall-preview/server.js +21 -0
  292. package/src/bongos/auth-config.js +29 -1
  293. package/src/bongos/auth-discord.js +3 -3
  294. package/src/bongos/auth-github.js +3 -6
  295. package/src/bongos/auth-google.js +217 -0
  296. package/src/bongos/device-approval.js +140 -0
  297. package/src/bongos/llm-pricing.js +17 -1
  298. package/src/bongos/metrics.js +48 -2
  299. package/src/bongos/middleware/rate-limit.js +30 -11
  300. package/src/bongos/module-scope-map.js +16 -1
  301. package/src/bongos/optimizer.js +15 -1
  302. package/src/bongos/payout-approvals.js +342 -0
  303. package/src/bongos/payout-gates.js +364 -0
  304. package/src/bongos/prelaunch-gate.js +7 -9
  305. package/src/bongos/prelaunch-switch.js +20 -0
  306. package/src/bongos/route-rank-check.js +3 -0
  307. package/src/bongos/routes/auth-google.js +246 -0
  308. package/src/bongos/routes/auth.js +38 -1
  309. package/src/bongos/routes/backup.js +33 -9
  310. package/src/bongos/routes/builders.js +57 -30
  311. package/src/bongos/routes/github.js +9 -0
  312. package/src/bongos/routes/me.js +18 -0
  313. package/src/bongos/routes/my-payouts.js +133 -0
  314. package/src/bongos/routes/payout-approvals.js +312 -0
  315. package/src/bongos/routes/payout-gates.js +179 -0
  316. package/src/bongos/routes.js +15 -1
  317. package/src/bongos/secret-box.js +18 -3
  318. package/src/bongos/serve-internal.js +47 -19
  319. package/src/bongos/sign-in-pages.js +138 -0
  320. package/src/bongos/task-needs.js +146 -0
  321. package/src/bongos-downloads.js +18 -1
  322. package/src/instance-config.js +28 -0
  323. package/src/module-api.js +14 -1
  324. package/src/platform-server.js +27 -1
  325. package/tests/account_logins.mjs +326 -0
  326. package/tests/account_logins_db.mjs +161 -0
  327. package/tests/ai_studio_billing.mjs +253 -0
  328. package/tests/ai_studio_generate.mjs +282 -0
  329. package/tests/ai_studio_keys.mjs +159 -0
  330. package/tests/ai_studio_requests.mjs +319 -0
  331. package/tests/auth_backchannel_logout_route.mjs +201 -0
  332. package/tests/auto_satisfy_criteria.mjs +10 -1
  333. package/tests/autobongos_tweak_lane.mjs +144 -0
  334. package/tests/autobongos_tweak_pin_identity.mjs +137 -0
  335. package/tests/backfill_subagent_rewards.mjs +17 -1
  336. package/tests/backup_trigger_lock.mjs +119 -0
  337. package/tests/batched_writes_parity.mjs +340 -0
  338. package/tests/blockers_people.mjs +198 -0
  339. package/tests/bounded_rate_maps.mjs +184 -0
  340. package/tests/claimable_blocker_status.mjs +1 -1
  341. package/tests/claims_batch_routes.mjs +421 -0
  342. package/tests/conductor_main_e2e.mjs +4 -0
  343. package/tests/config_read_cache.mjs +171 -0
  344. package/tests/copy_desk_page_editor.mjs +12 -0
  345. package/tests/cost_write_guard.mjs +200 -0
  346. package/tests/craft_achievements.mjs +33 -10
  347. package/tests/criterion_goal_attach.mjs +1 -1
  348. package/tests/db_backup_nightly.mjs +73 -1
  349. package/tests/deploy_divergence_line.mjs +7 -0
  350. package/tests/effective_visibility_predicate.mjs +12 -0
  351. package/tests/fitness.mjs +19 -0
  352. package/tests/fixtures/per-row-loops-legacy.cjs +245 -0
  353. package/tests/fixtures/seed-tasks/bongos-v1-idea-routing.json +256 -0
  354. package/tests/fixtures/seed-tasks/bv1-profiles.json +592 -0
  355. package/tests/fixtures/seed-tasks/bv2-ideator-surface.json +373 -0
  356. package/tests/fixtures/seed-tasks/fake-api.mjs +80 -0
  357. package/tests/github_one_trip.mjs +34 -0
  358. package/tests/github_repo_visibility.mjs +20 -0
  359. package/tests/goal_achievement.mjs +30 -1
  360. package/tests/goal_achievement_reward.mjs +205 -0
  361. package/tests/goal_archive_disposition.mjs +5 -5
  362. package/tests/goal_sort_staleness.mjs +2 -1
  363. package/tests/goal_tier.mjs +11 -1
  364. package/tests/google_fonts_nonblocking.mjs +84 -0
  365. package/tests/google_sign_in.mjs +401 -0
  366. package/tests/government_board_expiry.mjs +94 -0
  367. package/tests/government_protected_surfaces.mjs +1 -1
  368. package/tests/government_routes.mjs +146 -23
  369. package/tests/grader.mjs +31 -0
  370. package/tests/hall_ai_studio.mjs +347 -0
  371. package/tests/hall_city.mjs +8 -0
  372. package/tests/hall_page_gate_map.mjs +3 -0
  373. package/tests/hall_studio_home.mjs +61 -2
  374. package/tests/home_attention_cards.mjs +1 -1
  375. package/tests/hook_shell_tools.mjs +8 -0
  376. package/tests/hooks_drain.mjs +1 -1
  377. package/tests/host_topology_skips.mjs +2 -0
  378. package/tests/howto_moments.mjs +17 -0
  379. package/tests/human_signal.mjs +141 -0
  380. package/tests/install_script_reader.mjs +51 -0
  381. package/tests/integration_fixture_ids.mjs +82 -0
  382. package/tests/invite_expiry_db.mjs +13 -1
  383. package/tests/lib_sh_resolution.mjs +0 -4
  384. package/tests/market_watch_data.mjs +244 -0
  385. package/tests/market_watch_data_db.mjs +308 -0
  386. package/tests/market_watch_module.mjs +114 -0
  387. package/tests/market_watch_run.mjs +503 -0
  388. package/tests/metrics.mjs +124 -0
  389. package/tests/migration_namespace.mjs +14 -0
  390. package/tests/module_api.mjs +2 -0
  391. package/tests/module_loader.mjs +1 -1
  392. package/tests/module_skills_hook.mjs +69 -0
  393. package/tests/notes.mjs +182 -0
  394. package/tests/own_scope_guard.mjs +5 -0
  395. package/tests/page_tweak_marker_sql.mjs +76 -0
  396. package/tests/page_tweak_no_block.mjs +135 -0
  397. package/tests/page_tweak_notice.mjs +103 -0
  398. package/tests/payout_approvals.mjs +253 -0
  399. package/tests/payout_approvals_db.mjs +106 -0
  400. package/tests/payout_gates.mjs +322 -0
  401. package/tests/payout_gates_db.mjs +113 -0
  402. package/tests/planning_session_skill.mjs +13 -3
  403. package/tests/project_settings_page.mjs +29 -5
  404. package/tests/project_settings_spend_cap_route.mjs +235 -0
  405. package/tests/promotion_sweep.mjs +29 -1
  406. package/tests/provision.mjs +3 -2
  407. package/tests/provisioning_app_status.mjs +18 -0
  408. package/tests/provisioning_credential_preflight.mjs +53 -0
  409. package/tests/provisioning_env_manifest_edit_delete.mjs +223 -0
  410. package/tests/public_landing_projects.mjs +37 -0
  411. package/tests/publish_reconciler_overlap.mjs +60 -0
  412. package/tests/rank_change_broadcast_port.mjs +151 -0
  413. package/tests/seed_tasks.mjs +176 -0
  414. package/tests/seed_verify_criteria.mjs +2 -6
  415. package/tests/session_start_freshness.mjs +66 -1
  416. package/tests/shell_hooks.mjs +209 -0
  417. package/tests/ship_assurance.mjs +28 -0
  418. package/tests/ship_card.mjs +20 -5
  419. package/tests/site_classified_once.mjs +142 -0
  420. package/tests/speciality_db_shape.mjs +23 -0
  421. package/tests/speciality_session_skills.mjs +48 -2
  422. package/tests/strand_lease.mjs +249 -0
  423. package/tests/strand_watch.mjs +58 -0
  424. package/tests/task_criteria_routes.mjs +275 -0
  425. package/tests/task_criteria_unlink_db.mjs +123 -0
  426. package/tests/task_needs.mjs +233 -0
  427. package/tests/task_needs_db.mjs +110 -0
  428. package/tests/unit_runner_serial.mjs +266 -43
  429. package/tests/version_close_route.mjs +1 -1
  430. package/tests/version_literals.mjs +5 -7
  431. package/tests/welcome_boot.mjs +16 -5
  432. package/tests/welcome_boot_browser.mjs +36 -4
  433. package/docs/recipes/bongos-cli-release.md +0 -66
  434. package/scripts/bongos/backfill-disciplines.js +0 -144
  435. package/scripts/bongos/backfill-session-earnings.js +0 -123
  436. package/scripts/bongos/backfill-session-types.js +0 -87
  437. package/scripts/bongos/build-bongos.js +0 -94
  438. package/scripts/bongos/cleanup-orphans.js +0 -74
  439. package/scripts/bongos/seed-bongos-v1-idea-routing-tasks.js +0 -152
  440. package/scripts/bongos/seed-bv1-profiles-tasks.js +0 -135
  441. package/scripts/bongos/seed-bv2-ideator-surface-tasks.js +0 -390
  442. package/scripts/bongos/smoke-batch-claim.sh +0 -238
  443. package/scripts/bongos/smoke-batch-release.sh +0 -316
  444. package/scripts/bongos/smoke-claim-consistency.sh +0 -222
  445. package/scripts/bongos/smoke-dependencies.sh +0 -143
  446. package/scripts/gds/backfill-disciplines.js +0 -7
  447. package/scripts/gds/backfill-session-earnings.js +0 -7
  448. package/scripts/gds/backfill-session-types.js +0 -7
  449. package/scripts/gds/cleanup-orphans.js +0 -7
  450. package/scripts/gds/seed-bongos-v1-idea-routing-tasks.js +0 -6
  451. package/scripts/gds/seed-bv1-profiles-tasks.js +0 -6
  452. package/scripts/gds/seed-bv2-ideator-surface-tasks.js +0 -6
  453. package/scripts/gds/smoke-batch-claim.sh +0 -8
  454. package/scripts/gds/smoke-batch-release.sh +0 -8
  455. package/scripts/gds/smoke-claim-consistency.sh +0 -8
  456. package/scripts/gds/smoke-dependencies.sh +0 -8
  457. package/tests/smoke_dependencies_witness.mjs +0 -46
@@ -0,0 +1,245 @@
1
+ 'use strict';
2
+
3
+ // tests/fixtures/per-row-loops-legacy.cjs — the FOUR one-query-per-row loops as
4
+ // they stood before task 1004929 batched them, kept verbatim (comments trimmed)
5
+ // so tests/batched_writes_parity.mjs and tests/government_routes.mjs can run the
6
+ // old and the new code over the same fake database and compare what each one
7
+ // returns and leaves behind. A frozen reference, not live code: nothing in src/
8
+ // or modules/ may require it.
9
+
10
+ const { nonTerminalSql, isTerminalStatus } = require('../../modules/lifecycle/task-status');
11
+ const { cancelPendingMembershipRequests } = require('../../modules/lifecycle/goal-close-sweep.js');
12
+
13
+ // modules/government/routes/government.js — POST /government/rank-sets/apply,
14
+ // the try-block body. `db` is the government db slice, `rankSet` the
15
+ // recommendedRankSet output. Returns { created, skipped } as the reply carried.
16
+ async function legacyApplyRankSet({ db, catalog, rankSet, builderId }) {
17
+ const set = rankSet;
18
+ const created = [];
19
+ const skipped = [];
20
+ for (const r of set.ranks) {
21
+ if (await db.getRankByKey(r.rank_key)) { skipped.push({ rank_key: r.rank_key, label: r.label }); continue; }
22
+ let rank;
23
+ try {
24
+ rank = await db.createRank({ rankKey: r.rank_key, label: r.label, description: r.description, createdBy: builderId });
25
+ } catch (err) {
26
+ if (err && err.code === '23505') { skipped.push({ rank_key: r.rank_key, label: r.label }); continue; }
27
+ throw err;
28
+ }
29
+ const granted = [];
30
+ for (const key of r.permissions) {
31
+ if (!catalog.byKey(key) || !catalog.isGrantableTo(key, rank)) continue;
32
+ if (await db.grantRankPermission({ rankKey: r.rank_key, permissionKey: key })) granted.push(key);
33
+ }
34
+ created.push({ rank_key: r.rank_key, label: r.label, permissions: granted });
35
+ }
36
+ return { created, skipped };
37
+ }
38
+
39
+ // modules/lifecycle/db-goals.js — archiveGoalWithDispositions, the plan loop.
40
+ // Runs on an open client (the caller has already locked the goal). Returns the
41
+ // applied list, or throws what the loop threw.
42
+ async function legacyArchiveLoop(client, { goalId, plan = [], reason }) {
43
+ const applied = [];
44
+ for (const step of plan) {
45
+ const { rows: tRows } = await client.query(
46
+ `SELECT id, title, status FROM tasks WHERE id = $1 AND goal_id = $2 FOR UPDATE`,
47
+ [step.taskId, goalId]
48
+ );
49
+ if (!tRows[0]) { applied.push({ task_id: String(step.taskId), verb: step.verb, result: 'skipped_not_in_goal' }); continue; }
50
+ if (isTerminalStatus(tRows[0].status)) {
51
+ applied.push({ task_id: String(step.taskId), verb: step.verb, result: 'skipped_already_terminal', status: tRows[0].status });
52
+ continue;
53
+ }
54
+
55
+ const { rows: claimed } = await client.query(
56
+ `SELECT id FROM claims WHERE task_id = $1 AND released_at IS NULL LIMIT 1`, [step.taskId]
57
+ );
58
+ if (claimed[0]) {
59
+ throw Object.assign(new Error('task has an active claim'), {
60
+ code: 'TASK_HAS_ACTIVE_CLAIM', taskId: String(step.taskId), taskTitle: tRows[0].title,
61
+ });
62
+ }
63
+
64
+ if (step.verb === 'abandon') {
65
+ await client.query(
66
+ `UPDATE tasks SET status = 'abandoned',
67
+ value_summary = COALESCE(NULLIF($2, ''), value_summary),
68
+ updated_at = now()
69
+ WHERE id = $1`,
70
+ [step.taskId, `Abandoned: ${reason}`]
71
+ );
72
+ applied.push({ task_id: String(step.taskId), verb: 'abandon', result: 'abandoned' });
73
+ } else {
74
+ const { rows: dest } = await client.query(
75
+ `SELECT id, status FROM goals WHERE id = $1 FOR SHARE`, [step.toGoalId]
76
+ );
77
+ if (!dest[0]) {
78
+ throw Object.assign(new Error('move target not found'), { code: 'MOVE_TARGET_NOT_FOUND', toGoalId: String(step.toGoalId) });
79
+ }
80
+ if (dest[0].status !== 'open') {
81
+ throw Object.assign(new Error('move target is not open'), {
82
+ code: 'MOVE_TARGET_NOT_OPEN', toGoalId: String(step.toGoalId), targetStatus: dest[0].status,
83
+ });
84
+ }
85
+ await client.query(`UPDATE tasks SET goal_id = $2, updated_at = now() WHERE id = $1`, [step.taskId, step.toGoalId]);
86
+ applied.push({ task_id: String(step.taskId), verb: 'move', result: 'moved', to_goal_id: String(step.toGoalId) });
87
+ }
88
+ }
89
+ return applied;
90
+ }
91
+
92
+ // modules/lifecycle/db-versions.js — closeVersion, the plan loop. Same contract.
93
+ async function legacyCloseLoop(client, { versionId, plan = [], reason, planningVersionId = null }) {
94
+ const applied = [];
95
+ for (const step of plan) {
96
+ const { rows: gRows } = await client.query(
97
+ `SELECT id, title, status FROM goals WHERE id = $1 AND version_id = $2 FOR UPDATE`,
98
+ [step.goalId, versionId]
99
+ );
100
+ if (!gRows[0]) { applied.push({ goal_id: String(step.goalId), verb: step.verb, result: 'skipped_not_on_version' }); continue; }
101
+ if (gRows[0].status !== 'open') {
102
+ applied.push({ goal_id: String(step.goalId), verb: step.verb, result: 'skipped_already_closed', status: gRows[0].status });
103
+ continue;
104
+ }
105
+
106
+ if (step.verb === 'abandon') {
107
+ const { rows: killed } = await client.query(
108
+ `UPDATE tasks SET status = 'abandoned',
109
+ value_summary = COALESCE(NULLIF($2, ''), value_summary),
110
+ updated_at = now()
111
+ WHERE goal_id = $1 AND ${nonTerminalSql('status')}
112
+ AND title NOT LIKE '\\_\\_smoke\\_\\_%' ESCAPE '\\'
113
+ RETURNING id`,
114
+ [step.goalId, `Abandoned: ${reason}`]
115
+ );
116
+ await client.query(`UPDATE goals SET status = 'archived', updated_at = now() WHERE id = $1`, [step.goalId]);
117
+ await cancelPendingMembershipRequests(client, step.goalId);
118
+ applied.push({ goal_id: String(step.goalId), verb: 'abandon', result: 'archived', tasks_abandoned: killed.length });
119
+ } else {
120
+ if (!planningVersionId) {
121
+ throw Object.assign(new Error('roll_forward with no planning version'), {
122
+ code: 'NO_PLANNING_VERSION', goalId: String(step.goalId),
123
+ });
124
+ }
125
+ const { rows: made } = await client.query(
126
+ `WITH next_order AS (
127
+ SELECT COALESCE(MAX(sort_order) + 1, 0) AS n FROM goals WHERE version_id = $2
128
+ )
129
+ INSERT INTO goals (version_id, title, subtitle, description, scope_modules,
130
+ status, visibility, accepting_requests, created_by, sort_order, category_id, working_area)
131
+ SELECT $2, g.title, g.subtitle, g.description, g.scope_modules,
132
+ 'open', g.visibility, g.accepting_requests, g.created_by, next_order.n, g.category_id, g.working_area
133
+ FROM goals g, next_order
134
+ WHERE g.id = $1
135
+ RETURNING id`,
136
+ [step.goalId, planningVersionId]
137
+ );
138
+ const successorId = made[0].id;
139
+
140
+ await client.query(
141
+ `INSERT INTO goal_members (goal_id, builder_id, membership_kind)
142
+ SELECT $2, builder_id, membership_kind FROM goal_members WHERE goal_id = $1
143
+ ON CONFLICT DO NOTHING`,
144
+ [step.goalId, successorId]
145
+ );
146
+
147
+ const { rows: moved } = await client.query(
148
+ `UPDATE tasks SET goal_id = $2, version_id = $3, updated_at = now()
149
+ WHERE goal_id = $1 AND ${nonTerminalSql('status')}
150
+ AND title NOT LIKE '\\_\\_smoke\\_\\_%' ESCAPE '\\'
151
+ RETURNING id`,
152
+ [step.goalId, successorId, planningVersionId]
153
+ );
154
+
155
+ await client.query(
156
+ `UPDATE goals SET succeeded_by_goal_id = $2, updated_at = now() WHERE id = $1`,
157
+ [step.goalId, successorId]
158
+ );
159
+ await client.query(`UPDATE goals SET status = 'archived', updated_at = now() WHERE id = $1`, [step.goalId]);
160
+ await cancelPendingMembershipRequests(client, step.goalId);
161
+
162
+ applied.push({
163
+ goal_id: String(step.goalId), verb: 'roll_forward', result: 'succeeded',
164
+ successor_goal_id: String(successorId), tasks_moved: moved.length,
165
+ });
166
+ }
167
+ }
168
+ return applied;
169
+ }
170
+
171
+ // modules/lifecycle/db-analytics.js — tallyPeerVotes, the whole function, with
172
+ // the transaction primitive passed in (it was api.withTx).
173
+ const PEER_VOTE_KARMA_LAG_DAYS = 7;
174
+ async function legacyTallyPeerVotes(withTx) {
175
+ return withTx(async (client) => {
176
+ const { rows: matured } = await client.query(
177
+ `SELECT pv.id, t.shipped_by AS builder_id, pv.vote
178
+ FROM peer_votes pv
179
+ JOIN tasks t ON t.id = pv.target_task_id
180
+ WHERE pv.tallied_at IS NULL
181
+ AND pv.recorded_at <= now() - ($1::int * interval '1 day')
182
+ AND t.shipped_by IS NOT NULL
183
+ FOR UPDATE OF pv`,
184
+ [PEER_VOTE_KARMA_LAG_DAYS]
185
+ );
186
+ const netByBuilder = new Map();
187
+ const voteIds = [];
188
+ for (const r of matured) {
189
+ voteIds.push(r.id);
190
+ netByBuilder.set(r.builder_id, (netByBuilder.get(r.builder_id) || 0) + r.vote);
191
+ }
192
+ const ideaVoteIds = [];
193
+ const { rows: [{ has: hasIdeaVotes }] } = await client.query(
194
+ `SELECT to_regclass('ideas_votes') IS NOT NULL AS has`
195
+ );
196
+ if (hasIdeaVotes) {
197
+ const { rows: maturedIdeas } = await client.query(
198
+ `SELECT iv.id, i.captured_by AS builder_id, iv.vote
199
+ FROM ideas_votes iv
200
+ JOIN idea_inbox i ON i.id = iv.target_idea_id
201
+ WHERE iv.tallied_at IS NULL
202
+ AND iv.recorded_at <= now() - ($1::int * interval '1 day')
203
+ AND i.captured_by IS NOT NULL
204
+ FOR UPDATE OF iv`,
205
+ [PEER_VOTE_KARMA_LAG_DAYS]
206
+ );
207
+ for (const r of maturedIdeas) {
208
+ ideaVoteIds.push(r.id);
209
+ netByBuilder.set(r.builder_id, (netByBuilder.get(r.builder_id) || 0) + r.vote);
210
+ }
211
+ }
212
+ const karmaRows = [];
213
+ for (const [builderId, net] of netByBuilder) {
214
+ if (net === 0) continue;
215
+ await client.query(
216
+ `INSERT INTO karma_log (builder_id, delta, reason, granted_by)
217
+ VALUES ($1, $2, 'peer_vote_weekly', NULL)`,
218
+ [builderId, net]
219
+ );
220
+ karmaRows.push({ builder_id: builderId, delta: net });
221
+ }
222
+ if (voteIds.length) {
223
+ await client.query(
224
+ `UPDATE peer_votes SET tallied_at = now() WHERE id = ANY($1::bigint[])`,
225
+ [voteIds]
226
+ );
227
+ }
228
+ if (ideaVoteIds.length) {
229
+ await client.query(
230
+ `UPDATE ideas_votes SET tallied_at = now() WHERE id = ANY($1::bigint[])`,
231
+ [ideaVoteIds]
232
+ );
233
+ }
234
+ return {
235
+ ok: true,
236
+ votes_tallied: voteIds.length + ideaVoteIds.length,
237
+ idea_votes_tallied: ideaVoteIds.length,
238
+ builders_credited: karmaRows.length,
239
+ karma: karmaRows,
240
+ lag_days: PEER_VOTE_KARMA_LAG_DAYS,
241
+ };
242
+ });
243
+ }
244
+
245
+ module.exports = { legacyApplyRankSet, legacyArchiveLoop, legacyCloseLoop, legacyTallyPeerVotes };
@@ -0,0 +1,256 @@
1
+ {
2
+ "_comment": "Historical seed, already run: goal 1000071 (BONGOS-V1), from seed-bongos-v1-idea-routing-tasks.js; spec docs/specs/bongos-v1-idea-routing.md. Kept as the equivalence fixture for tests/seed_tasks.mjs (task 1004979).",
3
+ "version_id": "BONGOS-V1",
4
+ "goal_id": 1000071,
5
+ "source": "planning-session",
6
+ "source_ref_prefix": "bongos-v1-idea-routing",
7
+ "tasks": [
8
+ {
9
+ "ref": "R02",
10
+ "title": "R02 · Schema: idea_inbox.suggested_goal_id + lineage origin 'route'",
11
+ "description": "Core migration: nullable suggested_goal_id FK on idea_inbox (routing hint for soft-landed/Discord/homeless ideas). Module migration (ideas_*): extend ideas_task_lineage.origin CHECK with 'route'. See docs/specs/bongos-v1-idea-routing.md.",
12
+ "kind": "infra",
13
+ "discipline": "engineer",
14
+ "priority": 5,
15
+ "est_minutes": 90,
16
+ "criterion_ids": [
17
+ "bug-lands-in-its-goal",
18
+ "no-route-loses-the-author"
19
+ ]
20
+ },
21
+ {
22
+ "ref": "R03",
23
+ "title": "R03 · Catch-all fallback never resolves to a non-building version",
24
+ "description": "generalGoalIdForVersion (modules/lifecycle/db.js:1343) + the null-goal fallback (:1414): a task create must never land in a shipped/frozen version's catch-all goal (the 1000771 failure shape). Routed creates with no goal go to the inbox instead. Done-when: test proves a create against a shipped version cannot resolve a catch-all.",
25
+ "kind": "bug",
26
+ "discipline": "engineer",
27
+ "priority": 5,
28
+ "est_minutes": 90,
29
+ "criterion_ids": [
30
+ "bug-lands-in-its-goal"
31
+ ]
32
+ },
33
+ {
34
+ "ref": "R07",
35
+ "title": "R07 · Capture-time goal suggester (advisory consulted BEFORE creation)",
36
+ "description": "Expose candidate goals for a draft capture — reuse buildGoalAdvisory's candidate logic (modules/lifecycle/goal-advisory.js:89) as a pre-creation read (e.g. GET /inbox/goal-suggestions?title=&kind=&version=). Feeds the hall picker (R08), CLI (R09), and Discord suggestion (R11). Done-when: candidates returned for a draft title/kind without creating anything.",
37
+ "kind": "feature",
38
+ "discipline": "engineer",
39
+ "priority": 5,
40
+ "est_minutes": 120,
41
+ "criterion_ids": [
42
+ "system-offers-the-home"
43
+ ]
44
+ },
45
+ {
46
+ "ref": "R10",
47
+ "title": "R10 · Hall move-a-task-to-goal affordance",
48
+ "description": "PATCH /tasks/:id {goal_id} (task 1763) exists but has no hall UI. Add move-to-goal on the task overlay (Metic+, task.edit) so a wrong routing pick is one reassignment, never a re-file. Done-when: a Metic+ moves a task between same-version goals from the hall.",
49
+ "kind": "feature",
50
+ "discipline": "ui",
51
+ "priority": 5,
52
+ "est_minutes": 120,
53
+ "criterion_ids": [
54
+ "system-offers-the-home"
55
+ ]
56
+ },
57
+ {
58
+ "ref": "R14",
59
+ "title": "R14 · ADR: idea routing — capture-time promotion, the landing matrix, the homeless-only inbox",
60
+ "description": "Write the ADR recording: routing mechanism is capture-time promotion (never inbox bypass — the Discord #bugs lesson), the landing matrix (bugs hard-land by rank; other kinds soft-land; Full Ideas/criterion-proposals/decisions never auto-route), and the inbox's new identity (holds only work without a home). Source: docs/specs/bongos-v1-idea-routing.md interview decisions.",
61
+ "kind": "decision",
62
+ "discipline": "engineer",
63
+ "priority": 5,
64
+ "est_minutes": 60,
65
+ "criterion_ids": [
66
+ "inbox-holds-only-homeless"
67
+ ]
68
+ },
69
+ {
70
+ "ref": "R01",
71
+ "title": "R01 · Capture-time routing core: POST /inbox accepts goal_id and converts in one transaction",
72
+ "description": "Extend inbox.captureIdea/route: when goal_id present and the landing matrix says route, one transaction writes the idea row (status=promoted, promoted_to_task_id), the task (in the goal, matrix-decided status), and ideas_task_lineage (origin=route). Validations: goal open + belongs to a building version; Full-grade and criterion-proposal/decision kinds never auto-route; goal-less capture behaves exactly as today. Deps R02.",
73
+ "kind": "feature",
74
+ "discipline": "engineer",
75
+ "priority": 5,
76
+ "est_minutes": 240,
77
+ "criterion_ids": [
78
+ "bug-lands-in-its-goal",
79
+ "no-route-loses-the-author"
80
+ ],
81
+ "deps": [
82
+ "R02"
83
+ ]
84
+ },
85
+ {
86
+ "ref": "R05",
87
+ "title": "R05 · Rank-aware landing: matrix enforcement server-side",
88
+ "description": "Metic+ filer's bug → status=ready; sub-Metic filer / any non-bug kind → backlog (awaiting any Metic+ promote via the existing task.promote, surfaced in the Needs-your-attention slice). Auto-derived requires_rank floors (ADR 0084) and the goal scope wall run on routed tasks exactly as on authored ones. Deps R01.",
89
+ "kind": "feature",
90
+ "discipline": "engineer",
91
+ "priority": 4,
92
+ "est_minutes": 150,
93
+ "criterion_ids": [
94
+ "filing-cannot-outrun-rank"
95
+ ],
96
+ "deps": [
97
+ "R01"
98
+ ]
99
+ },
100
+ {
101
+ "ref": "R11",
102
+ "title": "R11 · Discord #bugs retired onto the routing path",
103
+ "description": "Replace discord-bugs.js's direct createTask bypass (hardcoded GDS-V3 → goal 1000002 catch-all; destroys attribution) with capture-time routing: idea row + classifier-suggested goal (suggested_goal_id), soft-landing into the pending queue for a one-click Metic+ route. Unlinked authors still yield an attributable idea row (captured_by null). Closes inbox idea 1000771. Deps R01.",
104
+ "kind": "feature",
105
+ "discipline": "engineer",
106
+ "priority": 4,
107
+ "est_minutes": 180,
108
+ "criterion_ids": [
109
+ "bug-lands-in-its-goal",
110
+ "no-route-loses-the-author"
111
+ ],
112
+ "deps": [
113
+ "R01"
114
+ ]
115
+ },
116
+ {
117
+ "ref": "R12",
118
+ "title": "R12 · Discord #ideas carries routing hints",
119
+ "description": "The #ideas inbound (discord-inbound.js) files with no suggested_version at all today. Add: default suggested_version (most-recent building), classifier-suggested suggested_goal_id. Deps R02.",
120
+ "kind": "feature",
121
+ "discipline": "engineer",
122
+ "priority": 4,
123
+ "est_minutes": 120,
124
+ "criterion_ids": [
125
+ "system-offers-the-home"
126
+ ],
127
+ "deps": [
128
+ "R02"
129
+ ]
130
+ },
131
+ {
132
+ "ref": "R08",
133
+ "title": "R08 · Hall filing surface: file a bug/idea with a goal picker",
134
+ "description": "The hall has NO capture surface today (triage only). Add file-a-bug/idea: title, body, kind, goal picker fed by the suggester (R07); submits to POST /inbox with goal_id so goal-bound work routes at filing time. Grade capture untouched. Deps R01,R07.",
135
+ "kind": "feature",
136
+ "discipline": "ui",
137
+ "priority": 4,
138
+ "est_minutes": 240,
139
+ "criterion_ids": [
140
+ "bug-lands-in-its-goal",
141
+ "system-offers-the-home"
142
+ ],
143
+ "deps": [
144
+ "R01",
145
+ "R07"
146
+ ]
147
+ },
148
+ {
149
+ "ref": "R09",
150
+ "title": "R09 · CLI capture routing: --goal, suggester prompt, active-claim default",
151
+ "description": "capture.js gains --goal; when the builder holds a claim the suggested goal defaults to the claim's goal (interview decision 4); suggester candidates offered otherwise. Deps R01,R07.",
152
+ "kind": "feature",
153
+ "discipline": "engineer",
154
+ "priority": 4,
155
+ "est_minutes": 120,
156
+ "criterion_ids": [
157
+ "bug-lands-in-its-goal",
158
+ "system-offers-the-home"
159
+ ],
160
+ "deps": [
161
+ "R01",
162
+ "R07"
163
+ ]
164
+ },
165
+ {
166
+ "ref": "R13",
167
+ "title": "R13 · Promote path passes goal_id; /idea-triage goes homeless-only",
168
+ "description": "task-detail.js (:320) and triage.js (:160) finally send goal_id (suggester-fed default) so even triaged promotions land in a real goal; the /idea-triage skill doc is updated to the homeless-only contract. Deps R07.",
169
+ "kind": "feature",
170
+ "discipline": "engineer",
171
+ "priority": 4,
172
+ "est_minutes": 120,
173
+ "criterion_ids": [
174
+ "inbox-holds-only-homeless",
175
+ "triage-shrinks-to-judgment"
176
+ ],
177
+ "deps": [
178
+ "R07"
179
+ ]
180
+ },
181
+ {
182
+ "ref": "R04",
183
+ "title": "R04 · PROOF: attribution survives the route end-to-end",
184
+ "description": "A routed bug's task ships; sourceIdeaForTask returns the filer; ideas_task_lineage (origin=route) + captured_by intact — the trail goal 1000067's ideator credit will pay against (test). Deps R01.",
185
+ "kind": "verify",
186
+ "discipline": "engineer",
187
+ "priority": 3,
188
+ "est_minutes": 90,
189
+ "criterion_ids": [
190
+ "no-route-loses-the-author"
191
+ ],
192
+ "deps": [
193
+ "R01"
194
+ ]
195
+ },
196
+ {
197
+ "ref": "R06",
198
+ "title": "R06 · PROOF: abuse matrix — filing cannot escalate",
199
+ "description": "A Xenos mass-filing bugs never lands claimable work, never reaches protected scope, rate limits hold; a sub-Metic cannot reach ready via any capture vector (test suite). Deps R05.",
200
+ "kind": "verify",
201
+ "discipline": "engineer",
202
+ "priority": 3,
203
+ "est_minutes": 120,
204
+ "criterion_ids": [
205
+ "filing-cannot-outrun-rank"
206
+ ],
207
+ "deps": [
208
+ "R05"
209
+ ]
210
+ },
211
+ {
212
+ "ref": "R15",
213
+ "title": "R15 · PROOF: reclassification sweep of the open inbox",
214
+ "description": "Walk the open idea_inbox (~50 rows at planning time; 14 bugs) under the new rules: every goal-bound bug homed in a real goal; residual inbox is homeless-only; before/after counts recorded in this task's notes as the goal's measured evidence. Deps R01,R05,R13.",
215
+ "kind": "verify",
216
+ "discipline": "engineer",
217
+ "priority": 2,
218
+ "est_minutes": 120,
219
+ "criterion_ids": [
220
+ "inbox-holds-only-homeless",
221
+ "triage-shrinks-to-judgment"
222
+ ],
223
+ "deps": [
224
+ "R01",
225
+ "R05",
226
+ "R13"
227
+ ]
228
+ }
229
+ ],
230
+ "promote": [
231
+ "R02",
232
+ "R03",
233
+ "R07",
234
+ "R10",
235
+ "R14"
236
+ ],
237
+ "links": [
238
+ {
239
+ "method": "PATCH",
240
+ "path": "/inbox/1000771",
241
+ "body": {
242
+ "status": "promoted",
243
+ "promoted_to_task_id": {
244
+ "$task": "R11"
245
+ }
246
+ }
247
+ },
248
+ {
249
+ "method": "PATCH",
250
+ "path": "/tasks/1003059",
251
+ "body": {
252
+ "goal_id": 1000071
253
+ }
254
+ }
255
+ ]
256
+ }