@bongos/core 1.23.22 → 1.24.2
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.
- package/.bongos-core.json +933 -413
- package/.claude/skills/blocker-review/SKILL.md +9 -1
- package/.claude/skills/blocker-solve/SKILL.md +1 -1
- package/.claude/skills/builder-claim/SKILL.md +1 -1
- package/.claude/skills/builder-reauth/SKILL.md +1 -1
- package/.claude/skills/builder-redteam/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/builder-setup/SKILL.md +1 -1
- package/.claude/skills/builder-ship/SKILL.md +1 -1
- package/.claude/skills/collab-review/SKILL.md +5 -2
- package/.claude/skills/design/SKILL.md +1 -1
- package/.claude/skills/goal-create/SKILL.md +2 -2
- package/.claude/skills/grade-recover/SKILL.md +1 -1
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/.claude/skills/owner-review/SKILL.md +1 -1
- package/.claude/skills/planning-session/SKILL.md +11 -11
- package/.claude/skills/read-session-export/SKILL.md +1 -1
- package/.claude/skills/recall/SKILL.md +1 -1
- package/.claude/skills/scan-before-install/SKILL.md +1 -1
- package/.claude/skills/session-handoff/SKILL.md +1 -1
- package/.claude/skills/strand-fix/SKILL.md +13 -0
- package/.claude/skills/worktree-clean/SKILL.md +1 -1
- package/AGENTS.md +15 -0
- package/GEMINI.md +5 -0
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +96 -0
- package/clients/bongos-client/index.cjs +96 -0
- package/clients/bongos-client/index.d.ts +155 -4
- package/clients/bongos-client/index.mjs +96 -0
- package/docs/adr/0054-session-token-reward-cost-plus.md +3 -1
- package/docs/adr/0055-server-mediated-branch-publish.md +1 -1
- package/docs/adr/0385-money-lives-with-a-processor-credits-stay-a-project-meter.md +12 -1
- package/docs/adr/0393-a-platform-account-is-a-person-and-github-is-one-way-in.md +37 -0
- package/docs/adr/0394-market-watch-searches-through-the-messages-api-on-the-projects-key-and-runs-from-its-own-poller.md +102 -0
- package/docs/adr/0395-a-task-says-what-it-needs-and-lists-show-only-what-you-can-do.md +36 -0
- package/docs/adr/0396-google-sign-in-a-stand-in-login-and-a-browser-approved-cli-sign-in.md +41 -0
- package/docs/adr/README.md +6 -2
- package/docs/api/openapi.json +8651 -5833
- package/docs/api-reference.md +66 -10
- package/docs/architecture.md +37 -1
- package/docs/canonical-permissions.md +1 -1
- package/docs/copy-inventory.md +427 -339
- package/docs/copy-registry.json +1376 -500
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +7 -0
- package/docs/onboarding/diagrams/03-drachmae-karma.mmd +3 -3
- package/docs/onboarding/diagrams/README.md +1 -1
- package/docs/onboarding/drachmae-and-karma.md +15 -15
- package/docs/onboarding/primer.md +2 -2
- package/docs/page-inventory.json +57 -7
- package/docs/page-readings.json +1210 -1027
- package/docs/recipes/google-sign-in.md +60 -0
- package/docs/recipes/ops-gotchas.md +1 -1
- package/migrations/core_282_builders_lower_login_idx.sql +33 -0
- package/migrations/core_283_claimable_ignores_page_markers.sql +53 -0
- package/migrations/core_283_payout_approvals.sql +183 -0
- package/migrations/core_284_payout_gates.sql +179 -0
- package/migrations/core_285_task_needs.sql +74 -0
- package/modules/ai-studio/billing.js +67 -0
- package/modules/ai-studio/context.js +29 -0
- package/modules/ai-studio/db.js +172 -0
- package/modules/ai-studio/gemini.js +75 -0
- package/modules/ai-studio/generate.js +119 -0
- package/modules/ai-studio/keys.js +73 -0
- package/modules/ai-studio/migrations/ai_studio_001_keys.sql +22 -0
- package/modules/ai-studio/migrations/ai_studio_002_ledger.sql +48 -0
- package/modules/ai-studio/migrations/ai_studio_003_requests.sql +55 -0
- package/modules/ai-studio/openrouter.js +90 -0
- package/modules/ai-studio/payments.js +105 -0
- package/modules/ai-studio/pricing.js +92 -0
- package/modules/ai-studio/routes/ai-studio.js +235 -5
- package/modules/ai-studio/studio.js +121 -0
- package/modules/autonomy/fence.js +64 -1
- package/modules/builder-settings/builder-needs.js +27 -1
- package/modules/copy-desk/routes/copy-desk.js +5 -0
- package/modules/economy/achievements.js +22 -10
- package/modules/economy/cost-classify.js +9 -0
- package/modules/economy/cost-write-guard.js +52 -0
- package/modules/economy/cost.js +2 -1
- package/modules/economy/credits.js +18 -15
- package/modules/economy/goal-reward.js +161 -0
- package/modules/economy/reward.js +4 -0
- package/modules/economy/routes/cost.js +33 -7
- package/modules/government/access-routes.js +70 -0
- package/modules/government/access.js +88 -0
- package/modules/government/asks.js +5 -2
- package/modules/government/catalog.js +6 -0
- package/modules/government/db.js +72 -0
- package/modules/government/docket.js +48 -2
- package/modules/government/migrations/government_035_payout_approve.sql +29 -0
- package/modules/government/migrations/government_036_access_holders.sql +33 -0
- package/modules/government/pollers/board-expiry.js +108 -20
- package/modules/government/protected-surfaces.json +1 -0
- package/modules/government/routes/government.js +31 -0
- package/modules/government/routing.js +49 -7
- package/modules/government/waiting.js +78 -0
- package/modules/hall-ui/public/ai-studio-lib.js +268 -0
- package/modules/hall-ui/public/ai-studio.css +193 -0
- package/modules/hall-ui/public/ai-studio.html +98 -0
- package/modules/hall-ui/public/ai-studio.js +556 -0
- package/modules/hall-ui/public/ai-studio.states.json +94 -0
- package/modules/hall-ui/public/blockers-by-person.js +158 -0
- package/modules/hall-ui/public/blockers-lib.js +15 -1
- package/modules/hall-ui/public/blockers.html +8 -0
- package/modules/hall-ui/public/blockers.js +1 -1
- package/modules/hall-ui/public/claim-action.js +3 -0
- package/modules/hall-ui/public/collab.html +12 -0
- package/modules/hall-ui/public/craft-mode.js +4 -1
- package/modules/hall-ui/public/criterion-uat-ui.js +4 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/genesis/r5-life-1-primordial-sea.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-2-first-cells-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-2-first-cells.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-3-genome-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-3-genome.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-4-membrane-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-4-membrane.jpg +0 -0
- package/modules/hall-ui/public/genesis/r5-life-5-nervous-system-cut.webp +0 -0
- package/modules/hall-ui/public/genesis/r5-life-5-nervous-system.jpg +0 -0
- package/modules/hall-ui/public/goal-inbox.js +1 -1
- package/modules/hall-ui/public/goals-render.js +4 -0
- package/modules/hall-ui/public/goals.html +8 -0
- package/modules/hall-ui/public/gov-living.js +6 -2
- package/modules/hall-ui/public/government-access.js +108 -0
- package/modules/hall-ui/public/government.html +21 -0
- package/modules/hall-ui/public/hall-ask.css +5 -0
- package/modules/hall-ui/public/hall-ask.js +39 -3
- package/modules/hall-ui/public/notes-panel.css +20 -0
- package/modules/hall-ui/public/notes-panel.js +146 -0
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/project-settings.js +7 -5
- package/modules/hall-ui/public/settings-ai-keys.js +124 -0
- package/modules/hall-ui/public/settings.html +15 -0
- package/modules/hall-ui/public/street-deck.js +12 -3
- package/modules/hall-ui/public/studio.css +90 -8
- package/modules/hall-ui/public/studio.html +39 -10
- package/modules/hall-ui/public/studio.js +148 -18
- package/modules/hall-ui/public/task.html +2 -0
- package/modules/hall-ui/public/task.js +4 -0
- package/modules/hall-ui/public/tweak-editor.js +12 -3
- package/modules/hall-ui/public/welcome-boot.css +42 -6
- package/modules/hall-ui/public/welcome-boot.js +20 -6
- package/modules/hall-ui/public/work.js +20 -0
- package/modules/hall-ui/records/ai-studio.md +36 -0
- package/modules/hall-ui/records/blockers-people.md +38 -0
- package/modules/lifecycle/db-claims.js +32 -4
- package/modules/lifecycle/db-goals.js +18 -5
- package/modules/lifecycle/db-tasks.js +15 -6
- package/modules/lifecycle/done-when.js +23 -1
- package/modules/lifecycle/lifecycle.js +5 -0
- package/modules/lifecycle/migrations/lifecycle_017_notes.sql +46 -0
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/notes.js +219 -0
- package/modules/lifecycle/page-notice.js +124 -0
- package/modules/lifecycle/page-tweak-artist.js +18 -1
- package/modules/lifecycle/publish-reconciler.js +67 -20
- package/modules/lifecycle/routes/claims.js +5 -2
- package/modules/lifecycle/routes/goals.js +3 -1
- package/modules/lifecycle/routes/notes.js +91 -0
- package/modules/lifecycle/routes/project-settings.js +43 -35
- package/modules/lifecycle/routes/task-route-helpers.js +21 -0
- package/modules/lifecycle/routes/task-write-routes.js +21 -3
- package/modules/lifecycle/routes/tasks.js +12 -12
- package/modules/lifecycle/task-needs-gate.js +70 -0
- package/modules/market-watch/CLAUDE.md +15 -0
- package/modules/market-watch/db.js +340 -0
- package/modules/market-watch/key.js +58 -0
- package/modules/market-watch/migrations/market_watch_001_tables.sql +249 -0
- package/modules/market-watch/migrations/market_watch_002_run.sql +46 -0
- package/modules/market-watch/module.json +26 -0
- package/modules/market-watch/pollers/hub-seed.js +54 -0
- package/modules/market-watch/pollers/weekly-run.js +100 -0
- package/modules/market-watch/research.js +350 -0
- package/modules/market-watch/run.js +245 -0
- package/modules/market-watch/seed/market-data.json +38 -0
- package/modules/market-watch/seed/partners-data.json +497 -0
- package/modules/market-watch/seed.js +252 -0
- package/modules/market-watch/store.js +230 -0
- package/modules/memory/routes/memory.js +14 -2
- package/modules/memory/routes/search.js +8 -2
- package/modules/platform-identity/account-logins.js +192 -0
- package/modules/platform-identity/migrations/platform_identity_034_account_logins.sql +96 -0
- package/modules/platform-identity/platform-identity.js +5 -4
- package/modules/platform-identity/routes/my-projects.js +6 -0
- package/modules/platform-identity/routes/profile.js +18 -0
- package/modules/platform-identity/routes/public-profile.js +10 -1
- package/modules/platform-identity/routes/sso.js +26 -1
- package/modules/platform-identity/tests/platform-identity.mjs +2 -1
- package/modules/provisioning/credential-preflight.js +13 -3
- package/modules/provisioning/pollers/app-liveness.js +25 -5
- package/modules/provisioning/routes/provisioning.js +1 -1
- package/modules/provisioning/starter-bundles.js +6 -2
- package/modules/public-landing/public/account.html +10 -2
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg +0 -0
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-frost-bead-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-frost-bead-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-frost-pair-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-frost-pair-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-gold-blob-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-gold-blob-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-gold-pair-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-gold-pair-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-obsidian-blob-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-obsidian-blob-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-obsidian-pair-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r3-obsidian-pair-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r3-space-nebula.jpg +0 -0
- package/modules/public-landing/public/assets/world/r3-space-nebula.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r4-card-chrome.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-chrome.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r4-card-nebula.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-nebula.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp.json +1 -1
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg.json +1 -1
- package/modules/public-landing/public/community.html +1 -1
- package/modules/public-landing/public/connections.html +2 -2
- package/modules/public-landing/public/guild.html +1 -1
- package/modules/public-landing/public/invitations.html +2 -2
- package/modules/public-landing/public/projects.html +5 -5
- package/modules/raise/views/nda.html +2 -0
- package/modules/specialities/db.js +32 -1
- package/modules/specialities/routes/specialities.js +10 -6
- package/modules/status-ui/public/index.html +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +230 -0
- package/scripts/bongos/autobongos-tweak-lane.js +46 -1
- package/scripts/bongos/backfill-subagent-rewards.js +2 -2
- package/scripts/bongos/claim.js +22 -1
- package/scripts/bongos/db-backup-nightly.sh +29 -0
- package/scripts/bongos/deploy-divergence.js +7 -0
- package/scripts/bongos/doc-cli-guard.js +6 -0
- package/scripts/bongos/fitness.js +3 -2
- package/scripts/bongos/gen-diagrams.js +3 -3
- package/scripts/bongos/government-walk.js +3 -0
- package/scripts/bongos/lib.sh +4 -5
- package/scripts/bongos/migration-namespace.js +13 -1
- package/scripts/bongos/plain-cards.js +4 -0
- package/scripts/bongos/provision-github-setup.js +3 -1
- package/scripts/bongos/provision-units.js +6 -1
- package/scripts/bongos/publish-manifest.js +3 -0
- package/scripts/bongos/release.js +2 -0
- package/scripts/bongos/run-unit-tests.js +268 -82
- package/scripts/bongos/seed-tasks.example.json +56 -0
- package/scripts/bongos/seed-tasks.js +222 -0
- package/scripts/bongos/seed-verify-criteria.js +1 -2
- package/scripts/bongos/ship-finish.js +2 -0
- package/scripts/bongos/ship-resume.js +2 -0
- package/scripts/bongos/strand-lease.js +250 -0
- package/scripts/bongos/strand-watch.js +26 -5
- package/scripts/bongos/version-literal-guard.js +2 -2
- package/scripts/discord/post.js +4 -3
- package/scripts/gds/seed-tasks.js +7 -0
- package/scripts/gds/{build-bongos.js → strand-lease.js} +3 -3
- package/scripts/hall-preview/server.js +21 -0
- package/src/bongos/auth-config.js +29 -1
- package/src/bongos/auth-discord.js +3 -3
- package/src/bongos/auth-github.js +3 -6
- package/src/bongos/auth-google.js +217 -0
- package/src/bongos/device-approval.js +140 -0
- package/src/bongos/llm-pricing.js +17 -1
- package/src/bongos/metrics.js +48 -2
- package/src/bongos/middleware/rate-limit.js +30 -11
- package/src/bongos/module-scope-map.js +12 -1
- package/src/bongos/optimizer.js +15 -1
- package/src/bongos/payout-approvals.js +342 -0
- package/src/bongos/payout-gates.js +364 -0
- package/src/bongos/prelaunch-gate.js +7 -9
- package/src/bongos/prelaunch-switch.js +20 -0
- package/src/bongos/route-rank-check.js +7 -0
- package/src/bongos/routes/auth-google.js +246 -0
- package/src/bongos/routes/auth.js +38 -1
- package/src/bongos/routes/backup.js +33 -9
- package/src/bongos/routes/builders.js +57 -30
- package/src/bongos/routes/github.js +9 -0
- package/src/bongos/routes/me.js +18 -0
- package/src/bongos/routes/my-payouts.js +133 -0
- package/src/bongos/routes/payout-approvals.js +312 -0
- package/src/bongos/routes/payout-gates.js +179 -0
- package/src/bongos/routes.js +15 -1
- package/src/bongos/secret-box.js +18 -3
- package/src/bongos/serve-internal.js +47 -19
- package/src/bongos/sign-in-pages.js +138 -0
- package/src/bongos/task-needs.js +146 -0
- package/src/bongos-downloads.js +18 -1
- package/src/instance-config.js +28 -0
- package/src/module-api.js +14 -1
- package/tests/access_holders.mjs +214 -0
- package/tests/account_logins.mjs +326 -0
- package/tests/account_logins_db.mjs +161 -0
- package/tests/ai_studio_billing.mjs +253 -0
- package/tests/ai_studio_generate.mjs +282 -0
- package/tests/ai_studio_keys.mjs +159 -0
- package/tests/ai_studio_requests.mjs +319 -0
- package/tests/auth_backchannel_logout_route.mjs +201 -0
- package/tests/auto_satisfy_criteria.mjs +10 -1
- package/tests/autobongos_tweak_lane.mjs +144 -0
- package/tests/autobongos_tweak_pin_identity.mjs +137 -0
- package/tests/backfill_subagent_rewards.mjs +17 -1
- package/tests/backup_trigger_lock.mjs +119 -0
- package/tests/blockers_people.mjs +198 -0
- package/tests/bounded_rate_maps.mjs +184 -0
- package/tests/claimable_blocker_status.mjs +1 -1
- package/tests/claims_batch_routes.mjs +421 -0
- package/tests/config_read_cache.mjs +171 -0
- package/tests/copy_desk_page_editor.mjs +12 -0
- package/tests/cost_write_guard.mjs +200 -0
- package/tests/craft_achievements.mjs +33 -10
- package/tests/criterion_goal_attach.mjs +1 -1
- package/tests/db_backup_nightly.mjs +73 -1
- package/tests/deploy_divergence_line.mjs +7 -0
- package/tests/effective_visibility_predicate.mjs +12 -0
- package/tests/fitness.mjs +19 -0
- package/tests/fixtures/seed-tasks/bongos-v1-idea-routing.json +256 -0
- package/tests/fixtures/seed-tasks/bv1-profiles.json +592 -0
- package/tests/fixtures/seed-tasks/bv2-ideator-surface.json +373 -0
- package/tests/fixtures/seed-tasks/fake-api.mjs +80 -0
- package/tests/github_one_trip.mjs +34 -0
- package/tests/github_repo_visibility.mjs +20 -0
- package/tests/goal_achievement.mjs +30 -1
- package/tests/goal_achievement_reward.mjs +205 -0
- package/tests/goal_sort_staleness.mjs +2 -1
- package/tests/goal_tier.mjs +11 -1
- package/tests/google_fonts_nonblocking.mjs +84 -0
- package/tests/google_sign_in.mjs +401 -0
- package/tests/government_board_expiry.mjs +94 -0
- package/tests/government_board_membership.mjs +4 -1
- package/tests/government_protected_surfaces.mjs +1 -1
- package/tests/government_routes.mjs +1 -1
- package/tests/government_seed.mjs +1 -0
- package/tests/hall_ai_studio.mjs +347 -0
- package/tests/hall_page_gate_map.mjs +3 -0
- package/tests/hall_studio_home.mjs +61 -2
- package/tests/home_attention_cards.mjs +1 -1
- package/tests/human_signal.mjs +141 -0
- package/tests/install_script_reader.mjs +51 -0
- package/tests/integration_fixture_ids.mjs +82 -0
- package/tests/invite_expiry_db.mjs +13 -1
- package/tests/lib_sh_resolution.mjs +0 -4
- package/tests/market_watch_data.mjs +244 -0
- package/tests/market_watch_data_db.mjs +308 -0
- package/tests/market_watch_module.mjs +114 -0
- package/tests/market_watch_run.mjs +503 -0
- package/tests/metrics.mjs +124 -0
- package/tests/migration_namespace.mjs +14 -0
- package/tests/module_api.mjs +2 -0
- package/tests/module_loader.mjs +1 -1
- package/tests/notes.mjs +182 -0
- package/tests/own_scope_guard.mjs +5 -0
- package/tests/page_tweak_marker_sql.mjs +76 -0
- package/tests/page_tweak_no_block.mjs +135 -0
- package/tests/page_tweak_notice.mjs +103 -0
- package/tests/payout_approvals.mjs +253 -0
- package/tests/payout_approvals_db.mjs +106 -0
- package/tests/payout_gates.mjs +322 -0
- package/tests/payout_gates_db.mjs +113 -0
- package/tests/planning_session_skill.mjs +13 -3
- package/tests/project_settings_page.mjs +29 -5
- package/tests/project_settings_spend_cap_route.mjs +235 -0
- package/tests/promotion_sweep.mjs +29 -1
- package/tests/provision.mjs +3 -2
- package/tests/provisioning_app_status.mjs +18 -0
- package/tests/provisioning_credential_preflight.mjs +53 -0
- package/tests/provisioning_env_manifest_edit_delete.mjs +223 -0
- package/tests/publish_reconciler_overlap.mjs +60 -0
- package/tests/rank_change_broadcast_port.mjs +151 -0
- package/tests/seed_tasks.mjs +176 -0
- package/tests/seed_verify_criteria.mjs +2 -6
- package/tests/site_classified_once.mjs +142 -0
- package/tests/speciality_db_shape.mjs +23 -0
- package/tests/speciality_session_skills.mjs +48 -2
- package/tests/strand_lease.mjs +249 -0
- package/tests/strand_watch.mjs +10 -0
- package/tests/task_criteria_routes.mjs +275 -0
- package/tests/task_criteria_unlink_db.mjs +123 -0
- package/tests/task_needs.mjs +233 -0
- package/tests/task_needs_db.mjs +110 -0
- package/tests/unit_runner_serial.mjs +266 -43
- package/tests/version_literals.mjs +5 -7
- package/tests/welcome_boot.mjs +16 -5
- package/tests/welcome_boot_browser.mjs +36 -4
- package/docs/recipes/bongos-cli-release.md +0 -66
- package/scripts/bongos/backfill-disciplines.js +0 -144
- package/scripts/bongos/backfill-session-earnings.js +0 -123
- package/scripts/bongos/backfill-session-types.js +0 -87
- package/scripts/bongos/build-bongos.js +0 -94
- package/scripts/bongos/cleanup-orphans.js +0 -74
- package/scripts/bongos/seed-bongos-v1-idea-routing-tasks.js +0 -152
- package/scripts/bongos/seed-bv1-profiles-tasks.js +0 -135
- package/scripts/bongos/seed-bv2-ideator-surface-tasks.js +0 -390
- package/scripts/bongos/smoke-batch-claim.sh +0 -238
- package/scripts/bongos/smoke-batch-release.sh +0 -316
- package/scripts/bongos/smoke-claim-consistency.sh +0 -222
- package/scripts/bongos/smoke-dependencies.sh +0 -143
- package/scripts/gds/backfill-disciplines.js +0 -7
- package/scripts/gds/backfill-session-earnings.js +0 -7
- package/scripts/gds/backfill-session-types.js +0 -7
- package/scripts/gds/cleanup-orphans.js +0 -7
- package/scripts/gds/seed-bongos-v1-idea-routing-tasks.js +0 -6
- package/scripts/gds/seed-bv1-profiles-tasks.js +0 -6
- package/scripts/gds/seed-bv2-ideator-surface-tasks.js +0 -6
- package/scripts/gds/smoke-batch-claim.sh +0 -8
- package/scripts/gds/smoke-batch-release.sh +0 -8
- package/scripts/gds/smoke-claim-consistency.sh +0 -8
- package/scripts/gds/smoke-dependencies.sh +0 -8
- package/tests/smoke_dependencies_witness.mjs +0 -46
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Turning on Google sign-in (the hub)
|
|
2
|
+
|
|
3
|
+
Google sign-in ([task 1004993](https://cloudbongos.com/builders#/task/1004993), [ADR 0396](../adr/0396-google-sign-in-a-stand-in-login-and-a-browser-approved-cli-sign-in.md)) ships **switched off**. Until the two keys below are on the server, the site behaves exactly as before: GitHub only, no chooser page, and the command line still uses GitHub's code page. Adding the keys is what turns it on, so it is also the "go live" step.
|
|
4
|
+
|
|
5
|
+
It needs a Google sign-in app. The app is **free** and takes about ten minutes to create. Only the owner should create it, signed in with the Google account that should own it.
|
|
6
|
+
|
|
7
|
+
> **Never paste the client secret into a chat, a task, a commit or Discord.** It goes straight from Google's page into the server's settings file.
|
|
8
|
+
|
|
9
|
+
## 1. Create the Google sign-in app
|
|
10
|
+
|
|
11
|
+
1. Open <https://console.cloud.google.com/> and sign in.
|
|
12
|
+
2. In the top bar, click the project picker, then **New project**. Name it `Cloud Bongos sign-in` and click **Create**. When it is ready, make sure it is the selected project in the top bar.
|
|
13
|
+
3. In the left menu open **APIs & Services → OAuth consent screen**. Google may call this **Google Auth Platform**. Click **Get started**.
|
|
14
|
+
- **App name:** `Cloud Bongos`. **User support email:** your address.
|
|
15
|
+
- **Audience:** **External**.
|
|
16
|
+
- **Contact information:** your address. Tick the agreement and click **Create**.
|
|
17
|
+
4. Open **Branding** and fill in:
|
|
18
|
+
- **Application home page:** `https://cloudbongos.com`
|
|
19
|
+
- **Privacy policy:** `https://cloudbongos.com/privacy`
|
|
20
|
+
- **Terms of service:** `https://cloudbongos.com/terms`
|
|
21
|
+
- **Authorised domains:** `cloudbongos.com`
|
|
22
|
+
|
|
23
|
+
Click **Save**. Leave the logo empty: adding one sends the app to Google for a review.
|
|
24
|
+
5. Open **Data access**. Do not add anything. The three basic scopes (name, email address, profile picture) are all Bongos asks for, and they need no review.
|
|
25
|
+
6. Open **Audience** and click **Publish app**, then **Confirm**. Until you do this, only email addresses you list as test users can sign in.
|
|
26
|
+
7. Open **Clients**, click **Create client**, and fill in:
|
|
27
|
+
- **Application type:** **Web application**
|
|
28
|
+
- **Name:** `Cloud Bongos web`
|
|
29
|
+
- **Authorised redirect URIs:** click **Add URI** and enter exactly `https://cloudbongos.com/api/bongos/auth/google/callback`
|
|
30
|
+
- Leave **Authorised JavaScript origins** empty.
|
|
31
|
+
|
|
32
|
+
Click **Create**.
|
|
33
|
+
8. Google shows a **Client ID** and a **Client secret**. Click **Download JSON** and keep the file somewhere private. You need both values in the next step.
|
|
34
|
+
|
|
35
|
+
## 2. Put the two values on the server
|
|
36
|
+
|
|
37
|
+
The server keeps its sign-in keys in its settings file, the same file that already holds `…_GITHUB_CLIENT_ID` (on cloudbongos.com: `/etc/cloudbongos/web.env`). Add two lines next to the GitHub ones. Use **the same first word** those lines start with, so that if they read `CLOUDBONGOS_GITHUB_CLIENT_ID`, yours read:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
CLOUDBONGOS_GOOGLE_CLIENT_ID=<the Client ID>
|
|
41
|
+
CLOUDBONGOS_GOOGLE_CLIENT_SECRET=<the Client secret>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then restart the web service, the same way as after any other change to that file. If you would rather not edit the file yourself, ask in a session for help doing it on the server, but still type the secret there yourself.
|
|
45
|
+
|
|
46
|
+
## 3. Check it
|
|
47
|
+
|
|
48
|
+
1. Open <https://cloudbongos.com> in a private window and click **Sign in**. You should see **Continue with GitHub** and **Continue with Google**.
|
|
49
|
+
2. Click **Continue with Google** and pick an account. A first sign-in creates an account and lands on the account page so the person can choose their handle.
|
|
50
|
+
3. In a terminal, run the Bongos sign-in. The browser opens a cloudbongos.com page showing the same code as the terminal. Press **Approve**, and the terminal finishes by itself.
|
|
51
|
+
|
|
52
|
+
## What a Google-only person can and cannot do yet
|
|
53
|
+
|
|
54
|
+
- **Can:** sign up and sign in on cloudbongos.com and its hall, use the command line, and edit their account.
|
|
55
|
+
- **Cannot yet:** join another project's hall ([task 1004994](https://cloudbongos.com/builders#/task/1004994)) or link GitHub and Google together ([task 1004995](https://cloudbongos.com/builders#/task/1004995)). They are told so in plain words.
|
|
56
|
+
- Until they link GitHub, the hall shows them as `~<their handle>`. The `~` means "no GitHub account".
|
|
57
|
+
|
|
58
|
+
## Turning it off again
|
|
59
|
+
|
|
60
|
+
Remove the two lines and restart. Accounts created with Google stay. Their owners cannot sign in again until the lines are back, and their data is not lost.
|
|
@@ -171,7 +171,7 @@ Inside the namespace the file lists as `---------- 1 root root 0`. The runner un
|
|
|
171
171
|
|
|
172
172
|
- **Symptom:** on a fresh instance every `/builder-ship` parks at `confirmed`; `GET /tasks/:id/publish-status` answers 503 `push_unconfigured`; the archon has to merge PRs and `POST /tasks/:id/ship` by hand. Silent — work just piles up (gap-list W7, proven live on Spike Harbor task 1).
|
|
173
173
|
- **Root cause:** the ADR 0133 manifest used to request **zero** repo permissions and the conversion's App private key (`pem`) was discarded, so no publish credential (ADR 0055) ever existed on a wizard-born instance. Fixed for newly minted Apps (task 1003049): the manifest now requests `contents: write` + `pull_requests: write` + `actions: read`, `placeManifestCreds` writes `GITHUB_APP_ID` + `GITHUB_APP_PRIVATE_KEY` into web.env, and the server discovers the installation id at runtime — the one human step left is **installing the App on the repo** (the onboarding `app-install` step).
|
|
174
|
-
- **Fix for pre-existing instances:** in the App's GitHub settings — (1) **Permissions & events**: grant Contents read/write, Pull requests read/write, Actions read-only; (2) **General → Private keys**: generate a key; (3) put `GITHUB_APP_ID=<id>` and `GITHUB_APP_PRIVATE_KEY
|
|
174
|
+
- **Fix for pre-existing instances:** in the App's GitHub settings — (1) **Permissions & events**: grant Contents read/write, Pull requests read/write, Actions read-only; (2) **General → Private keys**: generate a key; (3) put `GITHUB_APP_ID=<id>` and `GITHUB_APP_PRIVATE_KEY="<pem as one line with literal \n>"` in `/etc/<slug>/web.env` — **double-quoted**: systemd drops an unquoted backslash, so the server gets `n` for every line break and fails with `ERR_OSSL_UNSUPPORTED` (task 1005009) — and restart the unit; (4) **Install App** → pick the project repo (owners must re-approve the new permissions on an already-installed App). Alternative: a fine-grained `GITHUB_PUSH_TOKEN` PAT with the same three scopes (ADR 0055).
|
|
175
175
|
- **How you know it worked:** `publish-status` stops 503ing, and the publish-reconciler log line `WARN … not installed` (if the App wasn't installed yet) goes quiet after the install.
|
|
176
176
|
|
|
177
177
|
---
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- =========================================================================
|
|
2
|
+
-- core_282 — index builders by lower(github_login) (task 1004923, from the DB
|
|
3
|
+
-- performance audit)
|
|
4
|
+
--
|
|
5
|
+
-- builders has NO index on github_login at all: 003_pms declares github_id
|
|
6
|
+
-- UNIQUE and leaves github_login bare, and no later migration indexes it. Yet
|
|
7
|
+
-- the admission paths look a builder up by login, case-insensitively:
|
|
8
|
+
--
|
|
9
|
+
-- the onboarding routes "is this login already a member?" before an
|
|
10
|
+
-- invite, an approval and the public POST
|
|
11
|
+
-- (SELECT 1 FROM builders WHERE lower(github_login) = lower($1))
|
|
12
|
+
-- the access-request a correlated is-member probe PER access_requests row
|
|
13
|
+
-- lists (EXISTS ... WHERE lower(b.github_login) = lower(ar.github_login))
|
|
14
|
+
--
|
|
15
|
+
-- A plain index on github_login could not serve any of them — they compare
|
|
16
|
+
-- lower(), so only an index on the expression can. Each was a sequential scan
|
|
17
|
+
-- of builders, and the list probes paid it once per row.
|
|
18
|
+
--
|
|
19
|
+
-- Not UNIQUE on purpose: logins can be renamed on GitHub and github_id is the
|
|
20
|
+
-- stable key, so two rows may legitimately have held the same login over time.
|
|
21
|
+
--
|
|
22
|
+
-- Additive and idempotent: an index only, no column, constraint or data. Safe to
|
|
23
|
+
-- run forward against a live database; an older release just has an unused
|
|
24
|
+
-- index. Rollback is DROP INDEX.
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_builders_lower_login
|
|
27
|
+
ON builders (lower(github_login));
|
|
28
|
+
|
|
29
|
+
COMMENT ON INDEX idx_builders_lower_login IS
|
|
30
|
+
'Case-insensitive builder-by-login lookups on the admission paths (modules/onboarding/routes/access-requests.js). github_login had no index of any kind before this (task 1004923).';
|
|
31
|
+
|
|
32
|
+
INSERT INTO schema_migrations (version) VALUES ('core_282_builders_lower_login_idx')
|
|
33
|
+
ON CONFLICT DO NOTHING;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
-- core_282 — claimable_tasks ignores an artist's page claim (task 1004947).
|
|
2
|
+
--
|
|
3
|
+
-- An artist claiming a page holds a web claim (no worktree, no session) on a
|
|
4
|
+
-- page-tweak task whose touches list the page's files. It is a MARKER: it says
|
|
5
|
+
-- "this page is being updated" and nothing more. The touches-overlap guard below
|
|
6
|
+
-- hid every ready engineering task on those files while the marker stood. The
|
|
7
|
+
-- two inlined copies (CLAIMABLE_SQL in modules/lifecycle/db-tasks.js and
|
|
8
|
+
-- src/bongos/optimizer.js) carry the same exclusion, and
|
|
9
|
+
-- tests/claimable_blocker_status.mjs pins all three. COALESCE keeps a claim on a
|
|
10
|
+
-- source-less task counted (a NULL source must not make the NOT evaluate to NULL).
|
|
11
|
+
--
|
|
12
|
+
-- Everything else is core_264's definition, byte for byte: DROP VIEW IF EXISTS +
|
|
13
|
+
-- CREATE, the t.* wildcard, the blocker and dependency clauses unchanged.
|
|
14
|
+
|
|
15
|
+
BEGIN;
|
|
16
|
+
|
|
17
|
+
DROP VIEW IF EXISTS claimable_tasks;
|
|
18
|
+
CREATE VIEW claimable_tasks AS
|
|
19
|
+
SELECT t.*,
|
|
20
|
+
pms_calibrated_minutes(t.est_minutes, t.kind) AS est_minutes_calibrated
|
|
21
|
+
FROM tasks t
|
|
22
|
+
WHERE t.status = 'ready'
|
|
23
|
+
AND t.title NOT LIKE '\_\_smoke\_\_%' ESCAPE '\'
|
|
24
|
+
AND NOT EXISTS (
|
|
25
|
+
SELECT 1
|
|
26
|
+
FROM task_blockers tb
|
|
27
|
+
LEFT JOIN blockers b ON b.id = tb.blocker_id
|
|
28
|
+
WHERE tb.task_id = t.id
|
|
29
|
+
AND (b.id IS NULL OR b.status = 'open')
|
|
30
|
+
)
|
|
31
|
+
AND NOT EXISTS (
|
|
32
|
+
SELECT 1
|
|
33
|
+
FROM dependencies d
|
|
34
|
+
WHERE ((d.from_kind = 'task' AND d.from_id = t.id)
|
|
35
|
+
OR (d.from_kind = 'goal' AND d.from_id = t.goal_id)
|
|
36
|
+
OR (d.from_kind = 'criterion' AND d.from_id IN (
|
|
37
|
+
SELECT tc.criterion_id FROM task_criteria tc WHERE tc.task_id = t.id)))
|
|
38
|
+
AND ((d.to_kind = 'task' AND EXISTS (SELECT 1 FROM tasks x WHERE x.id = d.to_id AND x.status <> 'shipped'))
|
|
39
|
+
OR (d.to_kind = 'criterion' AND EXISTS (SELECT 1 FROM done_when_criteria x WHERE x.id = d.to_id AND x.satisfied = false))
|
|
40
|
+
OR (d.to_kind = 'goal' AND EXISTS (SELECT 1 FROM goals x WHERE x.id = d.to_id AND x.status <> 'achieved')))
|
|
41
|
+
)
|
|
42
|
+
AND NOT EXISTS (
|
|
43
|
+
SELECT 1 FROM claims c
|
|
44
|
+
JOIN tasks t2 ON t2.id = c.task_id
|
|
45
|
+
WHERE c.released_at IS NULL
|
|
46
|
+
AND NOT (COALESCE(t2.source, '') = 'page-tweak' AND c.worktree_name IS NULL AND c.creator_session_id IS NULL)
|
|
47
|
+
AND t2.touches && t.touches
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
COMMIT;
|
|
51
|
+
|
|
52
|
+
INSERT INTO schema_migrations (version) VALUES ('core_283_claimable_ignores_page_markers')
|
|
53
|
+
ON CONFLICT (version) DO NOTHING;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
-- core_283_payout_approvals.sql — every payout needs a logged, personal approval,
|
|
2
|
+
-- and the approvals are an unchangeable log (task 1004913, goal 1000092 N9;
|
|
3
|
+
-- ADR 0385 decisions 4 and 8, ADR 0387 decision 3).
|
|
4
|
+
--
|
|
5
|
+
-- WHY. The owner's legal framework (Art. I, cited by Article only) says a payout,
|
|
6
|
+
-- equity grant or contract is never executed by the platform or an AI on its own:
|
|
7
|
+
-- each needs a distinct step by the project's authorised person, kept in an
|
|
8
|
+
-- append-only log of who approved what and when. If we cannot prove the person
|
|
9
|
+
-- pressed the button, we were the actor. ADR 0385 decision 4 lets a project sign a
|
|
10
|
+
-- STANDING payout rule once (caps, formula, schedule, a pause window before each
|
|
11
|
+
-- run) instead of approving every payout; equity grants and contracts keep one
|
|
12
|
+
-- signature each; an AI may never create or widen a rule.
|
|
13
|
+
--
|
|
14
|
+
-- SHAPE. One table, one row per event, never edited:
|
|
15
|
+
--
|
|
16
|
+
-- rule_signed a person signs a standing payout rule. Its row id IS the
|
|
17
|
+
-- rule id; terms holds caps, formula, schedule, pause_hours.
|
|
18
|
+
-- rule_narrowed a person tightens a signed rule. The trigger refuses any
|
|
19
|
+
-- term that would widen it (a higher cap, a shorter pause, a
|
|
20
|
+
-- different formula, schedule or currency). Widening is
|
|
21
|
+
-- therefore only possible by signing a NEW rule.
|
|
22
|
+
-- rule_revoked a person ends a rule. Nothing may follow it.
|
|
23
|
+
-- run_noticed a run under a rule is announced (who gets what). It is the
|
|
24
|
+
-- one kind that is not a signature: the runner may file it.
|
|
25
|
+
-- It may not pay before the rule's pause window has passed.
|
|
26
|
+
-- run_held a person stops a noticed run inside its pause window.
|
|
27
|
+
-- payout_approved a person approves ONE payout (recipient, cents, currency).
|
|
28
|
+
-- grant_signed a person signs ONE equity grant.
|
|
29
|
+
-- contract_signed a person signs ONE contract.
|
|
30
|
+
--
|
|
31
|
+
-- WHAT THE DATABASE ENFORCES (not only the code that writes here; ADR 0387 D2):
|
|
32
|
+
-- * append-only: UPDATE and DELETE are refused row by row and TRUNCATE by
|
|
33
|
+
-- statement, by trigger, and the privileges are revoked from PUBLIC;
|
|
34
|
+
-- * personal: every kind except run_noticed must name the web session that
|
|
35
|
+
-- pressed it (session_source = 'web'), and the actor must be a human
|
|
36
|
+
-- builder: a machine principal (builders.system_role set) is refused;
|
|
37
|
+
-- * a narrowing never widens, and nothing follows a revocation.
|
|
38
|
+
-- Who may write at all (the payout.approve permission, rank fresh per request,
|
|
39
|
+
-- ADR 0016) is checked by the server before the insert.
|
|
40
|
+
--
|
|
41
|
+
-- Nothing here moves money. A processor does that (ADR 0385); this records the
|
|
42
|
+
-- authority a transfer must cite. Additive and idempotent.
|
|
43
|
+
|
|
44
|
+
BEGIN;
|
|
45
|
+
|
|
46
|
+
CREATE TABLE IF NOT EXISTS payout_approvals (
|
|
47
|
+
id bigserial PRIMARY KEY,
|
|
48
|
+
kind text NOT NULL CHECK (kind IN (
|
|
49
|
+
'rule_signed', 'rule_narrowed', 'rule_revoked',
|
|
50
|
+
'run_noticed', 'run_held',
|
|
51
|
+
'payout_approved', 'grant_signed', 'contract_signed')),
|
|
52
|
+
rule_id bigint REFERENCES payout_approvals(id) ON DELETE RESTRICT,
|
|
53
|
+
run_key text CHECK (run_key IS NULL OR run_key ~ '^[A-Za-z0-9._:-]{1,80}$'),
|
|
54
|
+
actor_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
55
|
+
actor_rank text,
|
|
56
|
+
session_id bigint,
|
|
57
|
+
session_source text,
|
|
58
|
+
recipient_id bigint REFERENCES builders(id) ON DELETE RESTRICT,
|
|
59
|
+
amount_cents bigint CHECK (amount_cents IS NULL OR amount_cents > 0),
|
|
60
|
+
currency text CHECK (currency IS NULL OR currency ~ '^[A-Z]{3}$'),
|
|
61
|
+
terms jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
62
|
+
note text CHECK (note IS NULL OR length(note) <= 500),
|
|
63
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
64
|
+
|
|
65
|
+
-- A signature names the web session that pressed it.
|
|
66
|
+
CONSTRAINT payout_approvals_personal CHECK (
|
|
67
|
+
kind = 'run_noticed' OR (session_source = 'web' AND session_id IS NOT NULL)
|
|
68
|
+
),
|
|
69
|
+
-- Each kind carries exactly what it needs.
|
|
70
|
+
CONSTRAINT payout_approvals_shape CHECK (
|
|
71
|
+
(kind = 'rule_signed' AND rule_id IS NULL AND run_key IS NULL AND recipient_id IS NULL AND amount_cents IS NULL AND terms <> '{}'::jsonb) OR
|
|
72
|
+
(kind = 'rule_narrowed' AND rule_id IS NOT NULL AND run_key IS NULL AND recipient_id IS NULL AND amount_cents IS NULL AND terms <> '{}'::jsonb) OR
|
|
73
|
+
(kind = 'rule_revoked' AND rule_id IS NOT NULL AND run_key IS NULL AND recipient_id IS NULL AND amount_cents IS NULL) OR
|
|
74
|
+
(kind = 'run_noticed' AND rule_id IS NOT NULL AND run_key IS NOT NULL AND amount_cents IS NOT NULL AND currency IS NOT NULL) OR
|
|
75
|
+
(kind = 'run_held' AND rule_id IS NOT NULL AND run_key IS NOT NULL AND amount_cents IS NULL) OR
|
|
76
|
+
(kind = 'payout_approved' AND rule_id IS NULL AND recipient_id IS NOT NULL AND amount_cents IS NOT NULL AND currency IS NOT NULL) OR
|
|
77
|
+
(kind IN ('grant_signed', 'contract_signed') AND rule_id IS NULL AND recipient_id IS NOT NULL AND terms <> '{}'::jsonb)
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
CREATE INDEX IF NOT EXISTS payout_approvals_rule_idx ON payout_approvals (rule_id, id) WHERE rule_id IS NOT NULL;
|
|
82
|
+
CREATE UNIQUE INDEX IF NOT EXISTS payout_approvals_run_once_idx ON payout_approvals (rule_id, run_key, kind) WHERE run_key IS NOT NULL;
|
|
83
|
+
-- The rules list reads only the three rule kinds; every payout and run row lives
|
|
84
|
+
-- in this same ever-growing table, so the list gets its own small index.
|
|
85
|
+
CREATE INDEX IF NOT EXISTS payout_approvals_rules_idx ON payout_approvals (id)
|
|
86
|
+
WHERE kind IN ('rule_signed', 'rule_narrowed', 'rule_revoked');
|
|
87
|
+
|
|
88
|
+
-- Append-only, by the database.
|
|
89
|
+
CREATE OR REPLACE FUNCTION payout_approvals_refuse_change() RETURNS trigger
|
|
90
|
+
LANGUAGE plpgsql AS $$
|
|
91
|
+
BEGIN
|
|
92
|
+
RAISE EXCEPTION 'payout_approvals is append-only: % refused (a correction is a new row)', TG_OP
|
|
93
|
+
USING ERRCODE = 'insufficient_privilege';
|
|
94
|
+
END;
|
|
95
|
+
$$;
|
|
96
|
+
|
|
97
|
+
DROP TRIGGER IF EXISTS payout_approvals_no_update_delete ON payout_approvals;
|
|
98
|
+
CREATE TRIGGER payout_approvals_no_update_delete
|
|
99
|
+
BEFORE UPDATE OR DELETE ON payout_approvals
|
|
100
|
+
FOR EACH ROW EXECUTE FUNCTION payout_approvals_refuse_change();
|
|
101
|
+
|
|
102
|
+
DROP TRIGGER IF EXISTS payout_approvals_no_truncate ON payout_approvals;
|
|
103
|
+
CREATE TRIGGER payout_approvals_no_truncate
|
|
104
|
+
BEFORE TRUNCATE ON payout_approvals
|
|
105
|
+
FOR EACH STATEMENT EXECUTE FUNCTION payout_approvals_refuse_change();
|
|
106
|
+
|
|
107
|
+
REVOKE UPDATE, DELETE, TRUNCATE ON payout_approvals FROM PUBLIC;
|
|
108
|
+
|
|
109
|
+
-- Personal, never widened, nothing after a revocation.
|
|
110
|
+
CREATE OR REPLACE FUNCTION payout_approvals_check_insert() RETURNS trigger
|
|
111
|
+
LANGUAGE plpgsql AS $$
|
|
112
|
+
DECLARE
|
|
113
|
+
machine text;
|
|
114
|
+
rule_kind text;
|
|
115
|
+
cur jsonb;
|
|
116
|
+
oldv bigint;
|
|
117
|
+
newv bigint;
|
|
118
|
+
cap text;
|
|
119
|
+
BEGIN
|
|
120
|
+
IF NEW.kind <> 'run_noticed' THEN
|
|
121
|
+
SELECT system_role INTO machine FROM builders WHERE id = NEW.actor_id;
|
|
122
|
+
IF machine IS NOT NULL THEN
|
|
123
|
+
RAISE EXCEPTION 'payout_approvals: % must be a person''s own act; machine principal % refused', NEW.kind, machine
|
|
124
|
+
USING ERRCODE = 'insufficient_privilege';
|
|
125
|
+
END IF;
|
|
126
|
+
END IF;
|
|
127
|
+
|
|
128
|
+
IF NEW.rule_id IS NULL THEN
|
|
129
|
+
RETURN NEW;
|
|
130
|
+
END IF;
|
|
131
|
+
|
|
132
|
+
SELECT kind INTO rule_kind FROM payout_approvals WHERE id = NEW.rule_id;
|
|
133
|
+
IF rule_kind IS DISTINCT FROM 'rule_signed' THEN
|
|
134
|
+
RAISE EXCEPTION 'payout_approvals: rule % is not a signed rule', NEW.rule_id
|
|
135
|
+
USING ERRCODE = 'check_violation';
|
|
136
|
+
END IF;
|
|
137
|
+
IF EXISTS (SELECT 1 FROM payout_approvals WHERE rule_id = NEW.rule_id AND kind = 'rule_revoked') THEN
|
|
138
|
+
RAISE EXCEPTION 'payout_approvals: rule % is revoked; nothing may follow it', NEW.rule_id
|
|
139
|
+
USING ERRCODE = 'check_violation';
|
|
140
|
+
END IF;
|
|
141
|
+
|
|
142
|
+
IF NEW.kind = 'rule_narrowed' THEN
|
|
143
|
+
SELECT terms INTO cur FROM payout_approvals
|
|
144
|
+
WHERE (id = NEW.rule_id OR (rule_id = NEW.rule_id AND kind = 'rule_narrowed'))
|
|
145
|
+
ORDER BY id DESC LIMIT 1;
|
|
146
|
+
IF (NEW.terms -> 'formula') IS DISTINCT FROM (cur -> 'formula')
|
|
147
|
+
OR (NEW.terms -> 'schedule') IS DISTINCT FROM (cur -> 'schedule')
|
|
148
|
+
OR (NEW.terms ->> 'currency') IS DISTINCT FROM (cur ->> 'currency') THEN
|
|
149
|
+
RAISE EXCEPTION 'payout_approvals: a narrowing may not change the formula, schedule or currency; sign a new rule'
|
|
150
|
+
USING ERRCODE = 'check_violation';
|
|
151
|
+
END IF;
|
|
152
|
+
IF (NEW.terms ->> 'pause_hours') IS NULL
|
|
153
|
+
OR (NEW.terms ->> 'pause_hours')::bigint < (cur ->> 'pause_hours')::bigint THEN
|
|
154
|
+
RAISE EXCEPTION 'payout_approvals: a narrowing may not shorten the pause window'
|
|
155
|
+
USING ERRCODE = 'check_violation';
|
|
156
|
+
END IF;
|
|
157
|
+
FOREACH cap IN ARRAY ARRAY['per_payout_cents', 'per_run_cents', 'per_month_cents'] LOOP
|
|
158
|
+
oldv := (cur -> 'caps' ->> cap)::bigint;
|
|
159
|
+
newv := (NEW.terms -> 'caps' ->> cap)::bigint;
|
|
160
|
+
IF oldv IS NOT NULL AND (newv IS NULL OR newv > oldv) THEN
|
|
161
|
+
RAISE EXCEPTION 'payout_approvals: a narrowing may not raise or remove the % cap', cap
|
|
162
|
+
USING ERRCODE = 'check_violation';
|
|
163
|
+
END IF;
|
|
164
|
+
END LOOP;
|
|
165
|
+
END IF;
|
|
166
|
+
|
|
167
|
+
IF NEW.kind = 'run_held' AND NOT EXISTS (
|
|
168
|
+
SELECT 1 FROM payout_approvals WHERE rule_id = NEW.rule_id AND run_key = NEW.run_key AND kind = 'run_noticed'
|
|
169
|
+
) THEN
|
|
170
|
+
RAISE EXCEPTION 'payout_approvals: run % was never noticed', NEW.run_key
|
|
171
|
+
USING ERRCODE = 'check_violation';
|
|
172
|
+
END IF;
|
|
173
|
+
|
|
174
|
+
RETURN NEW;
|
|
175
|
+
END;
|
|
176
|
+
$$;
|
|
177
|
+
|
|
178
|
+
DROP TRIGGER IF EXISTS payout_approvals_insert_rules ON payout_approvals;
|
|
179
|
+
CREATE TRIGGER payout_approvals_insert_rules
|
|
180
|
+
BEFORE INSERT ON payout_approvals
|
|
181
|
+
FOR EACH ROW EXECUTE FUNCTION payout_approvals_check_insert();
|
|
182
|
+
|
|
183
|
+
COMMIT;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
-- core_284_payout_gates.sql — who may be paid at all: age tier, sanctions
|
|
2
|
+
-- screening, and a freeze that tells the person (task 1004914, goal 1000092 N10;
|
|
3
|
+
-- ADR 0385 decision 8).
|
|
4
|
+
--
|
|
5
|
+
-- WHY. The owner's legal framework (cited by Article only) puts two gates in front
|
|
6
|
+
-- of every payout. Art. VII, age: under 13 no account takes part in pay at all; 13
|
|
7
|
+
-- to 15 no money; 16 and 17 only with a guardian's co-signature, paid into an
|
|
8
|
+
-- account the guardian controls. Art. X, sanctions: the recipient is screened, and
|
|
9
|
+
-- a match stops the payout. And a stop that touches money someone is owed must
|
|
10
|
+
-- tell them and offer an appeal: pay is never withheld silently.
|
|
11
|
+
--
|
|
12
|
+
-- SHAPE. Six tables, all append-only (a correction is a new row; the latest row
|
|
13
|
+
-- wins), enforced by one shared trigger:
|
|
14
|
+
--
|
|
15
|
+
-- payout_age_records a builder's age tier as recorded, by whom, on what basis.
|
|
16
|
+
-- No date of birth is stored: the tier is all a gate needs.
|
|
17
|
+
-- payout_guardians a guardian link for a 16-17 builder: the guardian (a
|
|
18
|
+
-- builder), the guardian-controlled processor account, and
|
|
19
|
+
-- whether the link stands (a later row with active=false
|
|
20
|
+
-- ends it).
|
|
21
|
+
-- payout_screenings one sanctions screening result per recipient per check:
|
|
22
|
+
-- clear | match | error, who screened (the processor once
|
|
23
|
+
-- N2 chooses one, or a person), and until when a clear
|
|
24
|
+
-- result counts.
|
|
25
|
+
-- payout_freezes one stop on a person's payouts: the reason in words, the
|
|
26
|
+
-- notice they were shown, and the appeal route. A freeze is
|
|
27
|
+
-- NOTIFIED by construction: notice_text and appeal_route
|
|
28
|
+
-- are NOT NULL, so a silent freeze cannot be stored.
|
|
29
|
+
-- payout_freeze_events what happened to a freeze since: appealed, upheld, lifted.
|
|
30
|
+
-- payout_guardian_cosigns a guardian's own co-signature on one payout to a
|
|
31
|
+
-- 16-17 builder, from their own web session.
|
|
32
|
+
--
|
|
33
|
+
-- The gate itself is server code (src/bongos/payout-gates.js), run on every money
|
|
34
|
+
-- route; this file is what it reads and writes.
|
|
35
|
+
|
|
36
|
+
BEGIN;
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS payout_age_records (
|
|
39
|
+
id bigserial PRIMARY KEY,
|
|
40
|
+
builder_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
41
|
+
tier text NOT NULL CHECK (tier IN ('under_13', '13_15', '16_17', 'adult')),
|
|
42
|
+
basis text NOT NULL CHECK (basis IN ('self_declared', 'verified_by_processor', 'verified_by_person')),
|
|
43
|
+
recorded_by bigint REFERENCES builders(id) ON DELETE RESTRICT,
|
|
44
|
+
recorded_at timestamptz NOT NULL DEFAULT now()
|
|
45
|
+
);
|
|
46
|
+
CREATE INDEX IF NOT EXISTS payout_age_records_builder_idx ON payout_age_records (builder_id, id DESC);
|
|
47
|
+
|
|
48
|
+
CREATE TABLE IF NOT EXISTS payout_guardians (
|
|
49
|
+
id bigserial PRIMARY KEY,
|
|
50
|
+
builder_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
51
|
+
guardian_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
52
|
+
guardian_account text CHECK (guardian_account IS NULL OR length(guardian_account) BETWEEN 3 AND 200),
|
|
53
|
+
active boolean NOT NULL,
|
|
54
|
+
recorded_by bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
55
|
+
recorded_at timestamptz NOT NULL DEFAULT now(),
|
|
56
|
+
CONSTRAINT payout_guardians_not_self CHECK (guardian_id <> builder_id),
|
|
57
|
+
CONSTRAINT payout_guardians_account CHECK (active = false OR guardian_account IS NOT NULL)
|
|
58
|
+
);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS payout_guardians_builder_idx ON payout_guardians (builder_id, id DESC);
|
|
60
|
+
|
|
61
|
+
CREATE TABLE IF NOT EXISTS payout_screenings (
|
|
62
|
+
id bigserial PRIMARY KEY,
|
|
63
|
+
builder_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
64
|
+
result text NOT NULL CHECK (result IN ('clear', 'match', 'error')),
|
|
65
|
+
screened_by text NOT NULL CHECK (screened_by ~ '^(processor|person):[A-Za-z0-9._-]{1,60}$'),
|
|
66
|
+
reference text CHECK (reference IS NULL OR length(reference) <= 200),
|
|
67
|
+
valid_until timestamptz,
|
|
68
|
+
recorded_by bigint REFERENCES builders(id) ON DELETE RESTRICT,
|
|
69
|
+
screened_at timestamptz NOT NULL DEFAULT now(),
|
|
70
|
+
CONSTRAINT payout_screenings_clear_expires CHECK (result <> 'clear' OR valid_until IS NOT NULL)
|
|
71
|
+
);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS payout_screenings_builder_idx ON payout_screenings (builder_id, id DESC);
|
|
73
|
+
|
|
74
|
+
CREATE TABLE IF NOT EXISTS payout_freezes (
|
|
75
|
+
id bigserial PRIMARY KEY,
|
|
76
|
+
builder_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
77
|
+
reason text NOT NULL CHECK (reason IN ('sanctions_match', 'sanctions_unscreened', 'age_no_money', 'age_needs_guardian', 'age_unknown', 'manual')),
|
|
78
|
+
notice_text text NOT NULL CHECK (length(notice_text) BETWEEN 20 AND 1000),
|
|
79
|
+
appeal_route text NOT NULL CHECK (length(appeal_route) BETWEEN 1 AND 300),
|
|
80
|
+
owed_cents bigint CHECK (owed_cents IS NULL OR owed_cents > 0),
|
|
81
|
+
frozen_by bigint REFERENCES builders(id) ON DELETE RESTRICT,
|
|
82
|
+
frozen_at timestamptz NOT NULL DEFAULT now()
|
|
83
|
+
);
|
|
84
|
+
CREATE INDEX IF NOT EXISTS payout_freezes_builder_idx ON payout_freezes (builder_id, id DESC);
|
|
85
|
+
|
|
86
|
+
CREATE TABLE IF NOT EXISTS payout_freeze_events (
|
|
87
|
+
id bigserial PRIMARY KEY,
|
|
88
|
+
freeze_id bigint NOT NULL REFERENCES payout_freezes(id) ON DELETE RESTRICT,
|
|
89
|
+
kind text NOT NULL CHECK (kind IN ('appealed', 'upheld', 'lifted')),
|
|
90
|
+
actor_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
91
|
+
statement text NOT NULL CHECK (length(statement) BETWEEN 1 AND 2000),
|
|
92
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
93
|
+
);
|
|
94
|
+
CREATE INDEX IF NOT EXISTS payout_freeze_events_freeze_idx ON payout_freeze_events (freeze_id, id);
|
|
95
|
+
|
|
96
|
+
-- A guardian's own co-signature on one payout to a 16-17 builder: either a
|
|
97
|
+
-- one-off approval or one recipient's line in a run. Their own web session only.
|
|
98
|
+
CREATE TABLE IF NOT EXISTS payout_guardian_cosigns (
|
|
99
|
+
id bigserial PRIMARY KEY,
|
|
100
|
+
builder_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
101
|
+
guardian_id bigint NOT NULL REFERENCES builders(id) ON DELETE RESTRICT,
|
|
102
|
+
approval_id bigint REFERENCES payout_approvals(id) ON DELETE RESTRICT,
|
|
103
|
+
rule_id bigint REFERENCES payout_approvals(id) ON DELETE RESTRICT,
|
|
104
|
+
run_key text,
|
|
105
|
+
session_id bigint NOT NULL,
|
|
106
|
+
session_source text NOT NULL CHECK (session_source = 'web'),
|
|
107
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
108
|
+
CONSTRAINT payout_guardian_cosigns_target CHECK (
|
|
109
|
+
(approval_id IS NOT NULL AND rule_id IS NULL AND run_key IS NULL) OR
|
|
110
|
+
(approval_id IS NULL AND rule_id IS NOT NULL AND run_key IS NOT NULL))
|
|
111
|
+
);
|
|
112
|
+
CREATE UNIQUE INDEX IF NOT EXISTS payout_guardian_cosigns_once_idx
|
|
113
|
+
ON payout_guardian_cosigns (builder_id, COALESCE(approval_id, 0), COALESCE(rule_id, 0), COALESCE(run_key, ''));
|
|
114
|
+
|
|
115
|
+
-- Only the builder's guardian, in a link that stands now, may co-sign.
|
|
116
|
+
CREATE OR REPLACE FUNCTION payout_guardian_cosigns_check() RETURNS trigger
|
|
117
|
+
LANGUAGE plpgsql AS $$
|
|
118
|
+
DECLARE g record;
|
|
119
|
+
BEGIN
|
|
120
|
+
SELECT guardian_id, active INTO g FROM payout_guardians WHERE builder_id = NEW.builder_id ORDER BY id DESC LIMIT 1;
|
|
121
|
+
IF g IS NULL OR NOT g.active OR g.guardian_id <> NEW.guardian_id THEN
|
|
122
|
+
RAISE EXCEPTION 'only the builder''s current guardian may co-sign their payout' USING ERRCODE = 'check_violation';
|
|
123
|
+
END IF;
|
|
124
|
+
RETURN NEW;
|
|
125
|
+
END;
|
|
126
|
+
$$;
|
|
127
|
+
|
|
128
|
+
DROP TRIGGER IF EXISTS payout_guardian_cosigns_rules ON payout_guardian_cosigns;
|
|
129
|
+
CREATE TRIGGER payout_guardian_cosigns_rules
|
|
130
|
+
BEFORE INSERT ON payout_guardian_cosigns
|
|
131
|
+
FOR EACH ROW EXECUTE FUNCTION payout_guardian_cosigns_check();
|
|
132
|
+
|
|
133
|
+
CREATE OR REPLACE FUNCTION payout_gates_refuse_change() RETURNS trigger
|
|
134
|
+
LANGUAGE plpgsql AS $$
|
|
135
|
+
BEGIN
|
|
136
|
+
RAISE EXCEPTION '% is append-only: % refused (a correction is a new row)', TG_TABLE_NAME, TG_OP
|
|
137
|
+
USING ERRCODE = 'insufficient_privilege';
|
|
138
|
+
END;
|
|
139
|
+
$$;
|
|
140
|
+
|
|
141
|
+
DO $$
|
|
142
|
+
DECLARE t text;
|
|
143
|
+
BEGIN
|
|
144
|
+
FOREACH t IN ARRAY ARRAY['payout_age_records', 'payout_guardians', 'payout_screenings', 'payout_freezes', 'payout_freeze_events', 'payout_guardian_cosigns'] LOOP
|
|
145
|
+
EXECUTE format('DROP TRIGGER IF EXISTS %I ON %I', t || '_no_update_delete', t);
|
|
146
|
+
EXECUTE format('CREATE TRIGGER %I BEFORE UPDATE OR DELETE ON %I FOR EACH ROW EXECUTE FUNCTION payout_gates_refuse_change()', t || '_no_update_delete', t);
|
|
147
|
+
EXECUTE format('DROP TRIGGER IF EXISTS %I ON %I', t || '_no_truncate', t);
|
|
148
|
+
EXECUTE format('CREATE TRIGGER %I BEFORE TRUNCATE ON %I FOR EACH STATEMENT EXECUTE FUNCTION payout_gates_refuse_change()', t || '_no_truncate', t);
|
|
149
|
+
EXECUTE format('REVOKE UPDATE, DELETE, TRUNCATE ON %I FROM PUBLIC', t);
|
|
150
|
+
END LOOP;
|
|
151
|
+
END;
|
|
152
|
+
$$;
|
|
153
|
+
|
|
154
|
+
-- An appeal is the frozen person's own; upheld/lifted come after an appeal or
|
|
155
|
+
-- straight from whoever reviews it, and nothing follows a lift.
|
|
156
|
+
CREATE OR REPLACE FUNCTION payout_freeze_events_check() RETURNS trigger
|
|
157
|
+
LANGUAGE plpgsql AS $$
|
|
158
|
+
DECLARE owner_id bigint;
|
|
159
|
+
BEGIN
|
|
160
|
+
SELECT builder_id INTO owner_id FROM payout_freezes WHERE id = NEW.freeze_id;
|
|
161
|
+
IF EXISTS (SELECT 1 FROM payout_freeze_events WHERE freeze_id = NEW.freeze_id AND kind = 'lifted') THEN
|
|
162
|
+
RAISE EXCEPTION 'payout freeze % is already lifted', NEW.freeze_id USING ERRCODE = 'check_violation';
|
|
163
|
+
END IF;
|
|
164
|
+
IF NEW.kind = 'appealed' AND NEW.actor_id IS DISTINCT FROM owner_id THEN
|
|
165
|
+
RAISE EXCEPTION 'only the person whose pay is frozen may appeal it' USING ERRCODE = 'check_violation';
|
|
166
|
+
END IF;
|
|
167
|
+
IF NEW.kind IN ('upheld', 'lifted') AND NEW.actor_id = owner_id THEN
|
|
168
|
+
RAISE EXCEPTION 'a person may not decide the appeal on their own freeze' USING ERRCODE = 'check_violation';
|
|
169
|
+
END IF;
|
|
170
|
+
RETURN NEW;
|
|
171
|
+
END;
|
|
172
|
+
$$;
|
|
173
|
+
|
|
174
|
+
DROP TRIGGER IF EXISTS payout_freeze_events_rules ON payout_freeze_events;
|
|
175
|
+
CREATE TRIGGER payout_freeze_events_rules
|
|
176
|
+
BEFORE INSERT ON payout_freeze_events
|
|
177
|
+
FOR EACH ROW EXECUTE FUNCTION payout_freeze_events_check();
|
|
178
|
+
|
|
179
|
+
COMMIT;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
-- core_285 — a task says what it NEEDS; a builder holds CAPABILITY grants
|
|
2
|
+
-- (task 1004950, ADR 0395).
|
|
3
|
+
--
|
|
4
|
+
-- tasks.needs: what a task needs beyond the ordinary build loop — 'owner' (a
|
|
5
|
+
-- decision, account or permission only the project owner holds) and/or 'server'
|
|
6
|
+
-- (hands on the live host). Empty for most tasks. Its own column on purpose, not a
|
|
7
|
+
-- value of automation_tag (that one says whether work can run unattended).
|
|
8
|
+
--
|
|
9
|
+
-- builders.capability_grants: the capabilities the owner has granted a builder by
|
|
10
|
+
-- hand. Only 'server' today; 'owner' is the seat itself and is never stored here.
|
|
11
|
+
-- Default off.
|
|
12
|
+
--
|
|
13
|
+
-- Both are additive with a default, so a rolled-back release that never names the
|
|
14
|
+
-- columns keeps working.
|
|
15
|
+
--
|
|
16
|
+
-- The claimable_tasks VIEW is re-created with core_283's body byte for byte: a
|
|
17
|
+
-- view freezes `t.*` at CREATE time, so without this it would not carry `needs`.
|
|
18
|
+
-- The two inlined copies (CLAIMABLE_SQL in modules/lifecycle/db-tasks.js and
|
|
19
|
+
-- src/bongos/optimizer.js) read `t.*` at query time and need no change for that;
|
|
20
|
+
-- the viewer filter itself is per-request and lives in those queries, because a
|
|
21
|
+
-- view cannot know who is asking.
|
|
22
|
+
|
|
23
|
+
BEGIN;
|
|
24
|
+
|
|
25
|
+
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS needs text[] NOT NULL DEFAULT '{}'::text[];
|
|
26
|
+
ALTER TABLE tasks DROP CONSTRAINT IF EXISTS tasks_needs_known;
|
|
27
|
+
ALTER TABLE tasks ADD CONSTRAINT tasks_needs_known
|
|
28
|
+
CHECK (needs <@ ARRAY['owner', 'server']::text[]);
|
|
29
|
+
-- The board's need filter asks needs @> ARRAY[$1]; most rows need nothing, so a
|
|
30
|
+
-- partial GIN index keeps it small.
|
|
31
|
+
CREATE INDEX IF NOT EXISTS tasks_needs_idx ON tasks USING gin (needs) WHERE cardinality(needs) > 0;
|
|
32
|
+
|
|
33
|
+
ALTER TABLE builders ADD COLUMN IF NOT EXISTS capability_grants text[] NOT NULL DEFAULT '{}'::text[];
|
|
34
|
+
ALTER TABLE builders DROP CONSTRAINT IF EXISTS builders_capability_grants_known;
|
|
35
|
+
ALTER TABLE builders ADD CONSTRAINT builders_capability_grants_known
|
|
36
|
+
CHECK (capability_grants <@ ARRAY['server']::text[]);
|
|
37
|
+
|
|
38
|
+
DROP VIEW IF EXISTS claimable_tasks;
|
|
39
|
+
CREATE VIEW claimable_tasks AS
|
|
40
|
+
SELECT t.*,
|
|
41
|
+
pms_calibrated_minutes(t.est_minutes, t.kind) AS est_minutes_calibrated
|
|
42
|
+
FROM tasks t
|
|
43
|
+
WHERE t.status = 'ready'
|
|
44
|
+
AND t.title NOT LIKE '\_\_smoke\_\_%' ESCAPE '\'
|
|
45
|
+
AND NOT EXISTS (
|
|
46
|
+
SELECT 1
|
|
47
|
+
FROM task_blockers tb
|
|
48
|
+
LEFT JOIN blockers b ON b.id = tb.blocker_id
|
|
49
|
+
WHERE tb.task_id = t.id
|
|
50
|
+
AND (b.id IS NULL OR b.status = 'open')
|
|
51
|
+
)
|
|
52
|
+
AND NOT EXISTS (
|
|
53
|
+
SELECT 1
|
|
54
|
+
FROM dependencies d
|
|
55
|
+
WHERE ((d.from_kind = 'task' AND d.from_id = t.id)
|
|
56
|
+
OR (d.from_kind = 'goal' AND d.from_id = t.goal_id)
|
|
57
|
+
OR (d.from_kind = 'criterion' AND d.from_id IN (
|
|
58
|
+
SELECT tc.criterion_id FROM task_criteria tc WHERE tc.task_id = t.id)))
|
|
59
|
+
AND ((d.to_kind = 'task' AND EXISTS (SELECT 1 FROM tasks x WHERE x.id = d.to_id AND x.status <> 'shipped'))
|
|
60
|
+
OR (d.to_kind = 'criterion' AND EXISTS (SELECT 1 FROM done_when_criteria x WHERE x.id = d.to_id AND x.satisfied = false))
|
|
61
|
+
OR (d.to_kind = 'goal' AND EXISTS (SELECT 1 FROM goals x WHERE x.id = d.to_id AND x.status <> 'achieved')))
|
|
62
|
+
)
|
|
63
|
+
AND NOT EXISTS (
|
|
64
|
+
SELECT 1 FROM claims c
|
|
65
|
+
JOIN tasks t2 ON t2.id = c.task_id
|
|
66
|
+
WHERE c.released_at IS NULL
|
|
67
|
+
AND NOT (COALESCE(t2.source, '') = 'page-tweak' AND c.worktree_name IS NULL AND c.creator_session_id IS NULL)
|
|
68
|
+
AND t2.touches && t.touches
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
COMMIT;
|
|
72
|
+
|
|
73
|
+
INSERT INTO schema_migrations (version) VALUES ('core_285_task_needs')
|
|
74
|
+
ON CONFLICT (version) DO NOTHING;
|