@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
@@ -10515,5 +10515,291 @@
10515
10515
  "id": "1004904",
10516
10516
  "text": "In Tweak Mode, the shared top bar, navigation, footer and skip link no longer clutter every page; each is now its own page to review once."
10517
10517
  }
10518
+ ],
10519
+ "1.23.22": [
10520
+ {
10521
+ "id": "1004916",
10522
+ "text": "Groundwork for letting artists generate images with AI: the design for pricing and key storage is written down and the new module has its place in the project."
10523
+ },
10524
+ {
10525
+ "id": "1004742",
10526
+ "text": "Asking the session guide for the next tutorial step a second time now shows just that step, instead of repeating every earlier reminder."
10527
+ },
10528
+ {
10529
+ "id": "1004929",
10530
+ "text": "Four database jobs that used to ask the database one question per item now ask once for the whole batch: adding a recommended set of ranks, archiving a goal, closing a version, and the weekly karma count. Results are the same,"
10531
+ },
10532
+ {
10533
+ "id": "1004944",
10534
+ "text": "The goal-planning guide no longer tells people to send a setting the server rejects, and now reminds them to name each task's role."
10535
+ },
10536
+ {
10537
+ "id": "1004931",
10538
+ "text": "AI sessions run faster. The safety checks that run around every command the AI types used to start four or five separate small programs each time; they now share two, cutting that overhead about in half with the same checks an"
10539
+ },
10540
+ {
10541
+ "id": "1004909",
10542
+ "text": "How module sales are shared is now decided. The buyer pays a small platform fee on top of the price (processing cost plus 1 percent), so builders lose nothing to it. Each project sets how its module money is split between the"
10543
+ },
10544
+ {
10545
+ "id": "1004741",
10546
+ "text": "The finished-task card no longer says a change is \"live\" just because it was merged. It now says \"merged\" until the live site is actually confirmed to be running it, so nobody acts on work that has not reached the site yet."
10547
+ },
10548
+ {
10549
+ "id": "1004928",
10550
+ "text": "AI sessions now start several seconds faster: the stuck-work check reuses its recent answer instead of asking the server every time, and the check for newer code no longer waits on the network while still saying how up to date"
10551
+ },
10552
+ {
10553
+ "id": "1004925",
10554
+ "text": "Pages load a little faster: the city page no longer waits on each of its scripts in turn, and returning visitors to the landing keep its styles and scripts for a year instead of fetching them again every few hours."
10555
+ },
10556
+ {
10557
+ "id": "1004932",
10558
+ "text": "Quality reviews now cost less. Each reviewer used to be handed up to twice as much file text as it needs; it now gets half, starting with the files the change is mostly about, and can still open the rest itself."
10559
+ },
10560
+ {
10561
+ "id": "1003180",
10562
+ "text": "Credits and money are now two separate records. Credits keep measuring who did how much work, and money lives in its own record tied to the payment processor, so a payout can always be explained from one place. Old credit reco"
10563
+ },
10564
+ {
10565
+ "id": "1004744",
10566
+ "text": "Pasting a long message that happens to mention \"skip the tutorial\" no longer switches off your how-to guide. Only a short request that actually asks for it does."
10567
+ },
10568
+ {
10569
+ "id": "1003181",
10570
+ "text": "Each project now decides what happens when it runs short of money: refuse new work, refuse completion, or carry on and record what it owes. A project that picks nothing gets the safest default, which stops a builder from start"
10571
+ },
10572
+ {
10573
+ "id": "1004915",
10574
+ "text": "Artists now get /tweak (and any other enabled module skill) automatically when a session starts, instead of needing a manual copy step. Other builders see no change."
10575
+ },
10576
+ {
10577
+ "id": "1004879",
10578
+ "text": "Every ship now keeps its record of why it was allowed to land. Before, that record was silently thrown away on every ship because the database refused how it was labelled."
10579
+ },
10580
+ {
10581
+ "id": "1004905",
10582
+ "text": "The money plan is now decided on paper: a licensed payment processor holds all user and project money, never Bongos. Credits stay a per-project measure of contribution and are never cash. Each project sets what its work pays a"
10583
+ }
10584
+ ],
10585
+ "1.24.0": [
10586
+ {
10587
+ "id": "1004986",
10588
+ "text": "Tests now prove a project's environment-variable list can be edited and pruned only by its owner or an Archon, never stores a secret value, and never reveals or touches another project's variables."
10589
+ },
10590
+ {
10591
+ "id": "1004967",
10592
+ "text": "Credits are now described everywhere in the core as a meter of work done, never as money, cash-out value or ownership, and the settings page says equity is something a project grants separately."
10593
+ },
10594
+ {
10595
+ "id": "1004983",
10596
+ "text": "New tests prove a builder cannot hand back someone else's claimed task, even in bulk, and that new builders cannot grab several tasks at once; two small gaps they found are filed as bugs."
10597
+ },
10598
+ {
10599
+ "id": "1004985",
10600
+ "text": "The project's monthly spend cap now has tests proving it can be set and turned off, and that anyone without permission, or blocked by the Treasury's rules, is refused with nothing changed."
10601
+ },
10602
+ {
10603
+ "id": "1003640",
10604
+ "text": "The report that decides which past sessions are owed extra pay no longer says a session's cost tracking broke just because it used no helper agents. It now says most of those sessions simply owe nothing, so whoever reads it is"
10605
+ },
10606
+ {
10607
+ "id": "1004956",
10608
+ "text": "When several AI sessions run at once and a finished piece of work gets stuck on its way in, only one session now goes to fix it. The first one to start takes it; every other session is told which session already has it and car"
10609
+ },
10610
+ {
10611
+ "id": "1004914",
10612
+ "text": "Before anyone is paid, the platform now checks their age and a sanctions screening, on every route that moves money and again at the moment of payment. Under-13s and 13-15s are paid no money; 16-17s only into an account their"
10613
+ },
10614
+ {
10615
+ "id": "1004935",
10616
+ "text": "Market Watch now exists as a switched-off, empty module that every project knows about, ready for the weekly market-watch features to be built into it."
10617
+ },
10618
+ {
10619
+ "id": "1004992",
10620
+ "text": "Bongos accounts no longer have to be GitHub accounts. A person can now have GitHub, Google, or both as ways to sign in, and every existing GitHub account keeps working exactly as before. This is the groundwork: the next steps"
10621
+ },
10622
+ {
10623
+ "id": "1004943",
10624
+ "text": "The owner's chosen Market Watch designs are now saved in the project, one per screen, and each screen's build task points at the design it must match."
10625
+ },
10626
+ {
10627
+ "id": "1004998",
10628
+ "text": "While the site is in pre-launch (stealth), no project can have a public GitHub repo through Bongos. Adopting a repo that is public is refused with a plain sentence telling the founder to make it private first or start a fresh"
10629
+ },
10630
+ {
10631
+ "id": "1004918",
10632
+ "text": "Builders who have linked their own OpenRouter or Gemini key now generate on it and pay us nothing: no money is held or taken, and the request still shows what it cost them (or that Google did not say). Their key is unlocked on"
10633
+ },
10634
+ {
10635
+ "id": "1004966",
10636
+ "text": "The nightly backup and a \"Back up now\" press can no longer run at the same time: if one is already going, the second politely steps aside instead of starting a duplicate copy."
10637
+ },
10638
+ {
10639
+ "id": "1004945",
10640
+ "text": "The Studio now shows three clear action buttons with the review summaries tucked below, and live-site reviews open as a full page you can read instead of a pop-up."
10641
+ },
10642
+ {
10643
+ "id": "1004965",
10644
+ "text": "Two background clean-up jobs, moving waiting tasks forward and catching up missed badges, now run once an hour instead of every five minutes, and the badge job only looks at what changed since its last run."
10645
+ },
10646
+ {
10647
+ "id": "1004917",
10648
+ "text": "Artists can now send a sketch or reference images (or a PDF) with a request, and get back a Gemini reply plus generated artwork, with the project's own look and context added automatically. Each one is paid for from the builde"
10649
+ },
10650
+ {
10651
+ "id": "1004963",
10652
+ "text": "The server now works out which site a visitor is asking for once per request instead of re-checking it in a dozen places, with every page still going to exactly the same place as before."
10653
+ },
10654
+ {
10655
+ "id": "1004964",
10656
+ "text": "The server's short-term memory of who is knocking on the door now has a fixed size, so a flood of visitors from many addresses can no longer make it swell and slow the site down."
10657
+ },
10658
+ {
10659
+ "id": "1004960",
10660
+ "text": "Two background checks now ask the database far less often: board votes still close right on time because the checker wakes up just after the next deadline, and the \"is your app live yet\" check runs every five minutes instead o"
10661
+ },
10662
+ {
10663
+ "id": "1004962",
10664
+ "text": "Announcing a builder's rank change on Discord no longer starts a separate program each time, so the server uses far less memory and a failed announcement can never block the rank change."
10665
+ },
10666
+ {
10667
+ "id": "1004961",
10668
+ "text": "The server now checks its own responsiveness every ten seconds instead of every second, so an idle project can rest and stop burning hosting credits."
10669
+ },
10670
+ {
10671
+ "id": "1004959",
10672
+ "text": "Closes a hole in how AI spending is recorded: a builder can no longer log fake spending that pauses every automatic agent, or charge spending to someone else's task."
10673
+ },
10674
+ {
10675
+ "id": "1004745",
10676
+ "text": "Tasks and goals now have a Notes section where anyone can add scoping info, decisions, questions, risks and links, grouped by purpose so a long history stays easy to scan."
10677
+ },
10678
+ {
10679
+ "id": "1004948",
10680
+ "text": "Engineers and AI sessions now get a one-line heads-up when the page they are working on is being updated by an artist, and artists see when engineering work is open on their page. Nothing is blocked."
10681
+ },
10682
+ {
10683
+ "id": "1004946",
10684
+ "text": "Blockers that ask the owner to choose between options now show HUMAN DECISION; ones that only need something done still show HUMAN TASK."
10685
+ },
10686
+ {
10687
+ "id": "1004933",
10688
+ "text": "Every AI session starts a little lighter. The project guide every session reads was trimmed of repeated lines, and the list of commands the AI sees was tightened, with no rule or command removed."
10689
+ },
10690
+ {
10691
+ "id": "1004919",
10692
+ "text": "Builders can now link their own OpenRouter or Gemini key in Settings, so AI generations can run on their own account; the key is stored encrypted and never shown again."
10693
+ },
10694
+ {
10695
+ "id": "1004926",
10696
+ "text": "The unit test check now runs several test files at the same time instead of one by one. Tests that touch shared things (a browser, a fixed port, a fixed temp folder, or real project files) still run alone. On a 12-core machine"
10697
+ },
10698
+ {
10699
+ "id": "1004954",
10700
+ "text": "Every pending change can land again: two database updates that had both been given the same number no longer trip the safety check that was holding everyone's work back."
10701
+ },
10702
+ {
10703
+ "id": "1004953",
10704
+ "text": "The welcome screen now opens with the animated Cloud Bongos logo instead of a line of text, and shows a small \"Built on cloudbongos.com\" credit in the corner."
10705
+ },
10706
+ {
10707
+ "id": "1004913",
10708
+ "text": "Every payout now needs a personal approval from the project's authorised person, kept in a log nobody can edit. A person can sign a standing payout rule (limits, how pay is worked out, a schedule, and a pause before each run),"
10709
+ },
10710
+ {
10711
+ "id": "1004922",
10712
+ "text": "Anything waiting on a person is now marked HUMAN TASK or HUMAN DECISION, with a person icon in the hall and the same words in Claude, ChatGPT and Gemini chats."
10713
+ },
10714
+ {
10715
+ "id": "1004924",
10716
+ "text": "The server stays lighter under load: bot traffic no longer grows its memory, background checks no longer pile up, and settings files are no longer re-read on every request."
10717
+ },
10718
+ {
10719
+ "id": "1004947",
10720
+ "text": "When an artist is updating a page, engineers can still see and claim coding tasks on that page. Artist claims are now just a marker and never hide or block engineering work."
10721
+ },
10722
+ {
10723
+ "id": "1004952",
10724
+ "text": "AI helpers stop repeating the routine \"not live yet\" and \"stranded ship\" notes to you every session; they only mention them when you ask or when something is actually stuck."
10725
+ },
10726
+ {
10727
+ "id": "1004930",
10728
+ "text": "The world art on the landing page and the hall's genesis pictures now download about 0.7 MB less, with no visible change: the same pictures at the same size, just packed more tightly (1.27 MB off the stored art in all). Pages"
10729
+ },
10730
+ {
10731
+ "id": "1004923",
10732
+ "text": "Every AI session prompt now loads a builder's specialities with one database read instead of two per speciality, and looking up a builder by GitHub name no longer scans the whole builders table."
10733
+ }
10734
+ ],
10735
+ "1.24.1": [
10736
+ {
10737
+ "id": "1005010",
10738
+ "text": "The automatic core release can run again: two test failures that had blocked every release since this morning are fixed, with no safety check loosened."
10739
+ },
10740
+ {
10741
+ "id": "1005009",
10742
+ "text": "Hosted projects can talk to GitHub again: new projects get their GitHub key in a form the server can read, so finished work is recognised as landed."
10743
+ },
10744
+ {
10745
+ "id": "1004921",
10746
+ "text": "Artists can now make things with AI from the Studio: pick sketches, pictures or a PDF, say what you want, choose your own key (free) or your balance, and get an answer and pictures back on a full results page. The request can"
10747
+ },
10748
+ {
10749
+ "id": "1005008",
10750
+ "text": "Fixed two database checks that were failing on every change and blocking new releases, and added a guard so two such checks can never collide on the same test records again."
10751
+ },
10752
+ {
10753
+ "id": "1004993",
10754
+ "text": "People without a GitHub account can now sign up and sign in to Cloud Bongos with Google. Every Sign in button opens one short page with Continue with GitHub and Continue with Google. A first Google sign-in creates the account"
10755
+ },
10756
+ {
10757
+ "id": "1004999",
10758
+ "text": "Every open blocker now shows who it is actually waiting on, by name. The Blockers page lists them under each person (your own first), the Collab page has a \"Blockers waiting on you\" list, and the City's cards say \"waits on Ana"
10759
+ },
10760
+ {
10761
+ "id": "1004937",
10762
+ "text": "Market Watch now runs itself once a week on the project's own Anthropic key, writing sourced findings and fresh scores for every tracked item, stopping at the monthly cap, and recording every skipped run and every item's cost."
10763
+ },
10764
+ {
10765
+ "id": "1004979",
10766
+ "text": "Planning sessions now fill in a simple task list file instead of copying a whole script; one tool files it, and a test proves it creates exactly the same tasks the three old scripts did."
10767
+ },
10768
+ {
10769
+ "id": "1004950",
10770
+ "text": "A task can now say it needs the project owner or server access, and each person's list of work to pick up shows only what they can actually do; server tasks refuse anyone not given server access, and the board can filter by wh"
10771
+ },
10772
+ {
10773
+ "id": "1005002",
10774
+ "text": "As you decided, removed the four old test scripts that nothing ran, plus the check that only read their text, so there is less unused code to maintain."
10775
+ },
10776
+ {
10777
+ "id": "1004984",
10778
+ "text": "New tests check that linking work to a goal's finish line, or removing that link, only works for people allowed to do it and cannot close a goal by mistake; bad ids are now refused cleanly."
10779
+ },
10780
+ {
10781
+ "id": "1001693",
10782
+ "text": "Finishing a goal now earns the people who built it some karma. Each achieved goal shares 10 karma by how much of its work each person shipped, with a small share to whoever set the goal up. Joining a goal without shipping earn"
10783
+ },
10784
+ {
10785
+ "id": "1004977",
10786
+ "text": "Removed five old scripts nothing used, plus the outdated guide for one; checked first that no release step still builds the CLI binaries with bun build, so nothing depended on them."
10787
+ },
10788
+ {
10789
+ "id": "1004987",
10790
+ "text": "Tests now prove the hub's sign-out-everywhere message only works when genuinely signed by the hub, works once per message, and never reveals whether a person has an account here."
10791
+ },
10792
+ {
10793
+ "id": "1004936",
10794
+ "text": "Market Watch now has somewhere to keep its findings: the tables for tracked companies, their sourced log, scores over time, the watch rules, proposals and weekly questions, plus the 2026-10-28 research loaded into the hub."
10795
+ },
10796
+ {
10797
+ "id": "1004974",
10798
+ "text": "The automatic page-tweak runner now refuses to hand a worker any instruction to take a project live. Tests also show a gap: a worker running as the owner could still do it. That gap is filed privately as security report 100003"
10799
+ },
10800
+ {
10801
+ "id": "1004934",
10802
+ "text": "We decided that Market Watch will search the web each week on the project's own AI key and under the owner's monthly cap, at about $4 to $5 per weekly check of 40 competitors."
10803
+ }
10518
10804
  ]
10519
10805
  }
@@ -95,6 +95,41 @@ function decideRound({ task, claims, files, failedAt, matchProtected }) {
95
95
  });
96
96
  }
97
97
 
98
+ // THE HAND-OFF FENCE (task 1004974): land only, going live waits for a person.
99
+ // What a worker is handed is its prompt, the scripts this lane runs around it,
100
+ // and the command lines of the skill it is told to follow. fence.refusePinMove
101
+ // reads all three and refuses before the claim if any names a door that would
102
+ // take a project live. The skill is read fresh each round, so an edit to it is
103
+ // checked by the next round rather than trusted from the last one.
104
+ const SKILL_PATH = path.join('modules', 'copy-desk', 'skills', 'tweak', 'SKILL.md');
105
+ const LANE_SCRIPTS = Object.freeze([
106
+ 'node scripts/bongos/worktree.js add|remove',
107
+ 'node scripts/bongos/claim.js <id> --auto-pickup',
108
+ 'node scripts/bongos/tweak-halt.js <id> --code <code>',
109
+ ]);
110
+
111
+ function readSkill(root = REPO_ROOT) {
112
+ return fs.readFileSync(path.join(root, SKILL_PATH), 'utf8');
113
+ }
114
+
115
+ // The command lines inside the skill's fenced blocks: the scripts it tells a
116
+ // worker to run.
117
+ function skillCommands(text) {
118
+ const out = [];
119
+ let inFence = false;
120
+ for (const line of String(text || '').split(/\r?\n/)) {
121
+ if (/^\s*```/.test(line)) { inFence = !inFence; continue; }
122
+ if (inFence && line.trim()) out.push(line.trim());
123
+ }
124
+ return out;
125
+ }
126
+
127
+ // Unreadable refuses: a skill the fence cannot read is one it cannot vouch for.
128
+ function guardHandOff({ prompt, skillText }) {
129
+ if (typeof skillText !== 'string') return fence.refusePinMove({ prompt, scripts: null });
130
+ return fence.refusePinMove({ prompt, scripts: [...LANE_SCRIPTS, ...skillCommands(skillText)] });
131
+ }
132
+
98
133
  function buildTweakPrompt({ task, pageId }) {
99
134
  return [
100
135
  `You are an unattended Autobongos worker applying ONE page tweak: task ${task.id}, page ${pageId}.`,
@@ -193,6 +228,11 @@ async function workRound({ round, opts, state }, d) {
193
228
  state.tweakFailed.set(id, after && after.task ? after.task.updated_at : task.updated_at);
194
229
  return after;
195
230
  };
231
+ const prompt = buildTweakPrompt({ task, pageId });
232
+ let skillText = null;
233
+ try { skillText = (d.readSkill || readSkill)(); } catch (_) { skillText = null; }
234
+ const handOff = guardHandOff({ prompt, skillText });
235
+ if (!handOff.go) return d.log({ event: 'fenced', lane: 'tweak', task_id: task.id, page_id: pageId, code: handOff.code, reason: handOff.reason });
196
236
  const name = d.worktreeName(task.id);
197
237
  const made = await d.runCmd(node, [path.join('scripts', 'bongos', 'worktree.js'), 'add', name, '--base', 'origin/main']);
198
238
  if (!made.ok) return d.log({ event: 'worktree_failed', lane: 'tweak', task_id: task.id, page_id: pageId, worktree: name });
@@ -208,7 +248,7 @@ async function workRound({ round, opts, state }, d) {
208
248
  // grade ledger, read after the worker, never the worker's word.
209
249
  const gradeLedger = d.gradeLedgerPath ? d.gradeLedgerPath(task.id) : null;
210
250
  const capCheck = d.gradeCapReader ? await d.gradeCapReader({ api: d.api, taskId: task.id, ledger: gradeLedger }) : null;
211
- const res = await d.spawnWork({ task, model: opts.model, timeoutMs: opts.timeoutMs, cwd: treePath, gradeLedger, capCheck, prompt: buildTweakPrompt({ task, pageId }), deps: d.spawnDeps || {} });
251
+ const res = await d.spawnWork({ task, model: opts.model, timeoutMs: opts.timeoutMs, cwd: treePath, gradeLedger, capCheck, prompt, deps: d.spawnDeps || {} });
212
252
  const grades = capCheck ? await capCheck() : null;
213
253
  if (gradeLedger) { try { fs.rmSync(gradeLedger, { force: true }); } catch (_) { /* its counts travel in the row */ } }
214
254
  let after = await (d.readRound || readRound)(d.api, id).catch(() => null);
@@ -256,6 +296,11 @@ module.exports = {
256
296
  pageFiles,
257
297
  decideRound,
258
298
  buildTweakPrompt,
299
+ SKILL_PATH,
300
+ LANE_SCRIPTS,
301
+ readSkill,
302
+ skillCommands,
303
+ guardHandOff,
259
304
  tweakOutcome,
260
305
  readRound,
261
306
  pickRound,
@@ -99,7 +99,7 @@ const CLASS_NOTE = Object.freeze({
99
99
  [CLASSES.UP_TO_DATE]: 'already paid at or above this basis — nothing owed',
100
100
  [CLASSES.OVERPAID]: 'paid ABOVE its worth; ADR 0054 is append-only, so never clawed back',
101
101
  [CLASSES.PRE_DEDUP_FIX]: 'subagent cost row predates the task-1003217 de-dup fix (~2.22x inflated) — paying it would OVERPAY',
102
- [CLASSES.NO_SUBAGENT_RECORD]: 'no subagent cost row: the cost hook never ran (no token / hard kill / 401). Unrecoverable — never guessed at',
102
+ [CLASSES.NO_SUBAGENT_RECORD]: 'no subagent cost row. Usually the session ran no subagent, so nothing is owed. Rarely the cost hook never ran (no token / hard kill / 401); the data cannot tell the two apart, so neither is guessed at',
103
103
  [CLASSES.PRE_SPLIT]: 'only a pre-split single `session` cost row, which lumps interactive + subagent and cannot be separated',
104
104
  [CLASSES.LEGACY]: 'reward_basis=0 legacy row — a DIFFERENT gap that ADR 0173 already declined to backfill; not folded in here',
105
105
  });
@@ -255,7 +255,7 @@ function renderReport(plan, { loginFor = () => null } = {}) {
255
255
  out.push('WHAT THIS RUN DOES NOT SETTLE (state it before deciding — ADR 0173 §Rationale 2/3)');
256
256
  out.push(` $${plan.unpaidSubagentUsd.toFixed(2)} of subagent spend sits in pre-dedup-fix rows that are ~2.22x inflated and cannot be paid from.`);
257
257
  out.push(` ${plan.overpaidExcess} credits of existing OVERpayment are not recovered; this corrects in one direction only.`);
258
- out.push(' Sessions whose cost hook never ran left no trace at all and are unrecoverable.');
258
+ out.push(` The ${plan.counts[CLASSES.NO_SUBAGENT_RECORD]} no_subagent_record session(s) are mostly sessions that ran no subagent; any among them whose cost hook failed left no trace and cannot be recovered.`);
259
259
  out.push('');
260
260
  out.push(`GRAND TOTAL to mint: ${plan.grandTotal} credits`);
261
261
  return { text: out.join('\n'), total: plan.grandTotal };
@@ -301,6 +301,17 @@ function clip(s, max = 160) {
301
301
  // ({ worktree, isMainCheckout }) so the session-claim branch can tell a real
302
302
  // conflict apart from a failed worktree detection. Optional and defaulted — the
303
303
  // function stays pure and every existing caller/test keeps working.
304
+ // task 1004956: who is fixing which strand on this machine, as { task_id, by, mine }.
305
+ // Fail-open — a refusal must print even when the lease folder cannot be read.
306
+ function liveStrandLeases() {
307
+ try {
308
+ const lease = require('./strand-lease.js');
309
+ const me = lease.currentHolder();
310
+ return lease.liveLeases({ dir: lease.leaseDir() })
311
+ .map((l) => ({ task_id: l.task_id, by: lease.describe(l), mine: !!me && l.holder === me.holder }));
312
+ } catch { return []; }
313
+ }
314
+
304
315
  function formatClaimFailure(r, taskId, ctx) {
305
316
  const info = claimErrorInfo(r);
306
317
  const { lines, exit } = claimFailureGuidance(info, r, taskId, ctx);
@@ -376,9 +387,14 @@ function claimFailureGuidance({ code, message, d }, r, taskId, ctx) {
376
387
  lines.push(owed.length
377
388
  ? `Your queue is gated — ${owed.length} confirmed task(s) are waiting on your rebase:`
378
389
  : 'Your queue is gated — a confirmed task is waiting on your rebase.');
390
+ // task 1004956: a strand another session is already fixing is named with that session,
391
+ // so this one leaves it alone instead of piling onto the same /strand-fix.
392
+ const leases = (ctx && ctx.strandLeases) || [];
379
393
  for (const t of owed) {
380
394
  lines.push(` - task ${t.id}${t.title ? ` ${clip(t.title)}` : ''}`);
381
395
  if (t.reason) lines.push(` flagged: ${clip(t.reason)}`);
396
+ const held = leases.find((l) => String(l.task_id) === String(t.id) && !l.mine);
397
+ if (held) lines.push(` already being fixed by ${held.by} — leave it to that session`);
382
398
  }
383
399
  if (waiting.length) lines.push(' Waiting, not on you (set aside; they land on their own):');
384
400
  for (const t of waiting) lines.push(` - task ${t.id}${t.title ? ` ${clip(t.title)}` : ''} — ${clip(t.waits_on || t.reason || 'outside you')}`);
@@ -661,6 +677,10 @@ async function main() {
661
677
  console.log(`\n⚠ touches[] overlaps an active claim: ${r.data.overlap_warning.join(', ')}`);
662
678
  console.log(' Advisory only — your claim stands; git merge is the authoritative collision check.');
663
679
  }
680
+ // task 1004948: a page an artist is updating — one gentle line each, no refusal.
681
+ for (const n of (Array.isArray(r.data.page_notice) ? r.data.page_notice : [])) {
682
+ if (n && typeof n.line === 'string') console.log(`\nℹ ${n.line}`);
683
+ }
664
684
  // task 1001741: goal-aware — an unclaimed OPEN sibling in the same goal
665
685
  // declares overlapping files. Same advisory posture as above.
666
686
  if (Array.isArray(r.data.sibling_overlap_warning) && r.data.sibling_overlap_warning.length > 0) {
@@ -768,6 +788,7 @@ async function main() {
768
788
  const failure = formatClaimFailure(r, taskId, {
769
789
  worktree,
770
790
  isMainCheckout: tree ? tree.isMainCheckout : null,
791
+ strandLeases: liveStrandLeases(),
771
792
  });
772
793
  for (const line of failure.lines) console.error(line);
773
794
  // task 1002592: this exit runs immediately after the claim request, which is
@@ -786,4 +807,4 @@ if (require.main === module) {
786
807
  });
787
808
  }
788
809
 
789
- module.exports = { formatClaimFailure, claimErrorInfo, goalMembershipOfferPlan, materializeModeSkills, printRoleLine, worktreeDetectionNote, QUEUE_GATED_EXIT };
810
+ module.exports = { formatClaimFailure, claimErrorInfo, goalMembershipOfferPlan, materializeModeSkills, printRoleLine, worktreeDetectionNote, liveStrandLeases, QUEUE_GATED_EXIT };
@@ -85,6 +85,35 @@ if ! command -v pg_dump >/dev/null 2>&1; then
85
85
  exit 2
86
86
  fi
87
87
 
88
+ # ---- one backup at a time (task 1004966) ------------------------------------
89
+
90
+ # The nightly timer (gds-db-backup.service) and the hall's "Back up now"
91
+ # (POST /backup/trigger in src/bongos/routes/backup.js) BOTH run this script, so
92
+ # the lock lives here and both paths take the same one. The server's in-memory
93
+ # flag only stops two manual runs; this stops a manual run overlapping the timer.
94
+ #
95
+ # One lock file per DATABASE under the backup dir, not one for the whole dir:
96
+ # every project's unit shares BACKUP_DIR and the timer fires them all at 04:00,
97
+ # so a dir-wide lock would make every project but the first skip its night.
98
+ #
99
+ # A second run does NOT wait — it exits 0 with the SKIPPED line below, because
100
+ # the run already in progress IS the backup it asked for. The lock is held on
101
+ # fd 9 for the life of this process and released by the kernel on exit, so a
102
+ # killed run can never leave it stuck.
103
+ #
104
+ # flock is util-linux (every Linux host). Where it is missing (macOS, Git Bash)
105
+ # the script warns and runs unlocked rather than refusing to back up at all.
106
+ LOCK_FILE="${BACKUP_DIR}/.${DB_NAME}.backup.lock"
107
+ if command -v flock >/dev/null 2>&1; then
108
+ exec 9>>"$LOCK_FILE"
109
+ if ! flock -n 9; then
110
+ echo "[$(date -u +%FT%TZ)] gds-db-backup: SKIPPED another backup of ${DB_NAME} is already running (lock ${LOCK_FILE} is held) — this run exits without dumping"
111
+ exit 0
112
+ fi
113
+ else
114
+ echo "[$(date -u +%FT%TZ)] gds-db-backup: WARN flock not found — running without the one-backup-at-a-time lock" >&2
115
+ fi
116
+
88
117
  # ---- the dump ---------------------------------------------------------------
89
118
 
90
119
  echo "[$(date -u +%FT%TZ)] gds-db-backup: dumping ${DB_NAME} → ${OUT}"
@@ -364,6 +364,11 @@ function report(r, { quiet = false } = {}) {
364
364
  // Returns null when there is nothing an agent must know: aligned, or the check
365
365
  // could not be determined at all. A SessionStart hook prints only a string, so
366
366
  // null IS the silence — an unreachable instance must never editorialize.
367
+ // The line is context for the AGENT: queued work lands on its own, so repeating it to the
368
+ // builder every session is noise. Fixed text only — it is injected every session.
369
+ const RELAY_RULE = 'This note is for you, not the builder: do not repeat it to them unless they ask '
370
+ + 'whether work is live, or something here stays stuck and needs a person.';
371
+
367
372
  function line(r) {
368
373
  if (!r || r.undetermined || r.ok) return null;
369
374
 
@@ -400,6 +405,8 @@ function line(r) {
400
405
  // Fixed vocabulary + the bounded host only (this line is injected context).
401
406
  if (versionParts) advice.push(`${hubRule(host)}; ${HOSTED_LINE.charAt(0).toLowerCase()}${HOSTED_LINE.slice(1)} Never call work live until that instance's /version shows it.`);
402
407
  if (pending) advice.push('Run migrate against that instance DB (PGDATABASE set) before anything else.');
408
+ // Agents kept relaying this to the owner on every session (owner, 2026-10-28; task 1004952).
409
+ advice.push(RELAY_RULE);
403
410
  return `Deploy state (read from ${host}/version): ${parts.join('; ')}. ${advice.join(' ')}`;
404
411
  }
405
412
 
@@ -50,6 +50,12 @@ const DOC_CLI_ALLOW = new Map([
50
50
  'a dated session log — the historical record, never rewritten — of the spike whose harness task 1004857 deleted; it describes what was run then, not what to run now.'],
51
51
  ['docs/session-logs/2026-09-16-task-1003752-stranded-detector-never-ran.md::scripts/bongos/gate-review.js',
52
52
  'a dated session log — the historical record, never rewritten — naming the gate-review classifier task 1004859 deleted (its CI check never ran); it describes what was run then, not what to run now.'],
53
+ ['docs/audits/goal-1000090-relevance-audit-2026-09-09.md::scripts/bongos/build-bongos.js',
54
+ 'a dated audit — docs/audits is frozen record exactly like docs/adr and docs/session-logs (rename-history-check.js property 2 pins its literal floors, and the rename codemod never rewrites it) — naming the CLI binary builder task 1004977 deleted once ADR 0324 retired the npm CLI channel; it describes what was true then.'],
55
+ ['docs/session-logs/2026-09-17-task-1002988-discipline-required.md::scripts/bongos/backfill-disciplines.js',
56
+ 'a dated session log — the historical record, never rewritten — of the one-shot discipline backfill task 1004977 deleted after it had run; it describes what was run then, not what to run now.'],
57
+ ['docs/session-logs/2026-09-17-task-1003975-session-type.md::scripts/bongos/backfill-session-types.js',
58
+ 'a dated session log — the historical record, never rewritten — of the one-shot session-type backfill task 1004977 deleted after it had run; it describes what was run then, not what to run now.'],
53
59
  ]);
54
60
 
55
61
  // `node scripts/bongos/foo.js` … up to the end of the line/segment, so the flags that
@@ -565,7 +565,8 @@ const KERNEL_FILES = [
565
565
  // task 1002969 (ADR 0356): when an invite's door closes is part of the admission
566
566
  // gate db-kernel.js runs, and the doorway hands the same definition to the
567
567
  // onboarding and hub modules. It requires NOTHING, so joining costs nothing.
568
- 'src/bongos/invite-expiry.js', // the read-time invite expiry (INVITE_EXPIRY_DAYS + its SQL)
568
+ 'src/bongos/invite-expiry.js', 'src/bongos/task-needs.js', // the read-time invite expiry + what a builder can do (task 1004950: capabilities read like rank, ADR 0016/0395; requires only pool/seams/db-kernel)
569
+ 'src/bongos/prelaunch-switch.js', // is pre-launch (stealth) on? — the doorway's prelaunchActive (task 1004998); imports nothing
569
570
  'src/bongos/routes/_helpers.js', // shared request validators the doorway re-exports
570
571
  // config / identity (host context a module reads, never hardcodes)
571
572
  'src/instance-config.js',
@@ -651,7 +652,7 @@ const KERNEL_NAMES_MODULES_OK = new Set([
651
652
  'src/bongos/auth-github.js', // ticks the 'onboarding' port on first sign-in
652
653
  'src/bongos/auth-idp.js', // same, on the federated path
653
654
  'src/bongos/auth-discord.js', // the Discord account-link flow itself
654
- 'src/bongos/act-alone.js', // resolves the 'government' port by key for checkActAlone (task 1004657)
655
+ 'src/bongos/act-alone.js', 'src/bongos/task-needs.js', // resolve the 'government' port by key: checkActAlone (task 1004657), the owner seat (task 1004950)
655
656
  'src/bongos/permission-path-check.js', // names module path subtrees to PROTECT them
656
657
  'src/bongos/route-rank-check.js', // names module route dirs to rank-gate them
657
658
  'src/role-responsibilities.js', // the ideator's VERBATIM statement reads "Make good ideas" — the English word, colliding with the `ideas` key. Owner text; unparaphrasable.
@@ -294,9 +294,9 @@ function drachmaeKarmaMmd(f) {
294
294
  `title: ${cap(currency)} & karma — where each comes from, where each goes (AUTO-GENERATED from live facts — do not hand-edit; see scripts/bongos/gen-diagrams.js)`,
295
295
  '---',
296
296
  'flowchart LR',
297
- ` subgraph CURRENCY["${CUR} (the builder reward — equity / reputation, a stake, not a spendable currency)"]`,
297
+ ` subgraph CURRENCY["${CUR} (the builder reward — a contribution meter: not money, not equity, not a spendable currency)"]`,
298
298
  ' direction TB',
299
- ' D_TOKEN["Ship a SESSION — the main reward\\nround(true_cost_usd × 1.20) cents — cost-plus on real token spend\\n(LIVE: priced server-side from token counts; ADR 0054, idempotent per session)"]',
299
+ ' D_TOKEN["Ship a SESSION — the main reward\\nround(true_cost_usd × 1.20) — sized by real token spend in USD\\n(LIVE: priced server-side from token counts; ADR 0054, idempotent per session)"]',
300
300
  ' D_SHIP["SHIP a TASK — books when the code lands on main, not at confirm\\ncredits_reward × kind multiplier (0.8–1.5×)\\n+ 25% idea-promotion bonus to the capturer\\n(LIVE: ADR 0023; pay-on-land ADR 0120; withheld entirely on a cost-plus-only instance, ADR 0146)"]',
301
301
  ` D_ACH["Unlock an achievement\\nachievements.credit_bonus (LIVE: ${nAch} seeded)\\n+ small net-negative-ship bonus"]`,
302
302
  ` D_BOUNTY["Red-team / security bounty\\n${currency} payout by severity\\n(rate card ${rateCard ? 'LIVE' : 'PLANNED'}: R65 #284 — src/bongos/security.js + shown on /builders;\\nawarding still ${awarding ? 'LIVE' : 'PLANNED'}: R67 #286 + vulnerability_reports R63 #282)"]`,
@@ -308,7 +308,7 @@ function drachmaeKarmaMmd(f) {
308
308
  ' D_ACH --> D_BAL',
309
309
  ` D_BOUNTY ${awarding ? '-->' : '-.PLANNED.->'} D_BAL`,
310
310
  '',
311
- ` D_SPEND["Spent on: nothing yet.\\n${cap(currency)} are EQUITY / reputation — a stake, not a\\nspendable currency. They persist losslessly across exit (migration 025)."]`,
311
+ ` D_SPEND["Spent on: nothing yet.\\n${cap(currency)} measure contribution — never money, never\\nredeemable, never equity (ADR 0387). They persist losslessly across exit (migration 025)."]`,
312
312
  ' D_BAL --> D_SPEND',
313
313
  ' end',
314
314
  '',
@@ -18,11 +18,10 @@
18
18
  # not run on a Windows builder's machine.
19
19
  #
20
20
  # Deliberately NOT here:
21
- # - the per-script curl `call()` wrapper — the two contracts differ in
22
- # load-bearing ways (smoke-batch-claim echoes "PAYLOAD\nCODE" and never exits
23
- # non-zero so its caller can read CODE/PAYLOAD; smoke-write/-consistency echo
24
- # the payload and `return 1` on a non-2xx so `set -e` aborts). Merging them
25
- # would break one or the other, so each script keeps its own.
21
+ # - the per-script curl `call()` wrapper — what a non-2xx means is the
22
+ # caller's contract (smoke-write echoes the payload and `return 1`s so
23
+ # `set -e` aborts; another smoke may want to read the code and carry on),
24
+ # so each script keeps its own.
26
25
 
27
26
  # lib.sh's own directory, captured at SOURCE time (BASH_SOURCE, not $0 — $0 is
28
27
  # the sourcing script). Captured eagerly because a caller may cd afterwards:
@@ -187,7 +187,7 @@ function stemCollisions(files) {
187
187
  // series/number exemption: that would also wave through a FOURTH file landing at 012,
188
188
  // which is the very bug this detects. Only the already-applied files are forgiven, so
189
189
  // the number stays closed to anything new.
190
- // Seven races that already landed. Every one of these files is applied on prod, and
190
+ // Eight races that already landed. Every one of these files is applied on prod, and
191
191
  // renaming an applied migration changes its schema_migrations stem, so migrate.sh would
192
192
  // see it as unapplied and RE-RUN it — the same constraint that produced
193
193
  // LEGACY_BARE_MODULES and RENAMED_MODULE_PREFIXES above. They are recorded rather than
@@ -204,6 +204,18 @@ const GRANDFATHERED_NUMBER_COLLISIONS = new Map([
204
204
  ['core/core/189', new Set(['core_189_core_upgrades_ledger', 'core_189_credit_log_task_id_index'])],
205
205
  ['core/core/223', new Set(['core_223_blocker_resolve_respects_other_open_blockers', 'core_223_task_visual'])],
206
206
  ['core/core/240', new Set(['core_240_artist_gate_indexes', 'core_240_goal_members_drop_legacy_role'])],
207
+ // 2026-10-28: tasks 1004923 and 1004947 each allocated 282 and both landed on main
208
+ // the same day, turning this check red for every branch. Order between them
209
+ // does not matter: one indexes builders by lower(github_login), the other replaces
210
+ // the claimable-list function; neither reads what the other writes.
211
+ ['core/core/282', new Set(['core_282_builders_lower_login_idx', 'core_282_claimable_ignores_page_markers'])],
212
+ // 2026-10-28, the same race one number up: tasks 1004913 and 1004953 each renamed
213
+ // their file off 282 into 283 and both merged within eight minutes, so the rename
214
+ // that was meant to clear 282 re-opened the check at 283. Order between them does
215
+ // not matter: one creates the payout_approvals table, the other re-creates the
216
+ // claimable_tasks view (DROP VIEW IF EXISTS + CREATE), and neither reads the other.
217
+ // Grandfathered, not renamed again: both stems are applied on the hub.
218
+ ['core/core/283', new Set(['core_283_claimable_ignores_page_markers', 'core_283_payout_approvals'])],
207
219
  ['module:platform-identity/platform_identity/012', new Set([
208
220
  // 2026-08-28, the collision task 1003646 was filed for: three parallel sessions each
209
221
  // allocated 012 from its own branch point. Order between them does not matter — they
@@ -82,6 +82,10 @@ const CARDS = Object.freeze({
82
82
  'This task belongs to a goal you have not joined.',
83
83
  'I will offer to join it for you — or apply, if it is private. I will not do either without your yes.',
84
84
  'For a private goal, its owner approves the request.', RULE),
85
+ NEEDS_SERVER_ACCESS: card(
86
+ 'This task needs server access, which this account does not have yet.',
87
+ 'I will pick a task that can be done from here instead.',
88
+ 'The project owner, if you should have server access.', RULE),
85
89
  archon_approval_required: card(
86
90
  'This goal covers a protected part of the project, so joining it needs an Archon to say yes.',
87
91
  'I will file the request for you and we pick other work while it waits.',