@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
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
// Seed the tasks for the ideator surface on goal 1000095 (BONGOS-V2, Working area 6).
|
|
2
|
-
// Source of truth: docs/specs/bongos-v2-ideator-surface.md (status: final).
|
|
3
|
-
// Visual direction: docs/design/ideator-surface-direction.md. Decisions: idea 1001072.
|
|
4
|
-
// Planning session 2026-09-19, meta task 1004076.
|
|
5
|
-
//
|
|
6
|
-
// Idempotent on (source, source_ref). Re-running resolves to the existing rows.
|
|
7
|
-
// criterion_ids are SLUGS, never positional "Cn" — goal 1000095 is one of several
|
|
8
|
-
// goals on this version, so a positional ref resolves against the wrong scope and
|
|
9
|
-
// POST /tasks now refuses it outright (learning 1000188, task 1003108).
|
|
10
|
-
//
|
|
11
|
-
// Ends by diffing the criterion links it planned against what the goal actually
|
|
12
|
-
// carries, and exits non-zero on any mismatch (the seeding contract's self-check).
|
|
13
|
-
//
|
|
14
|
-
// Run: node scripts/bongos/seed-bv2-ideator-surface-tasks.js
|
|
15
|
-
const os = require('os');
|
|
16
|
-
const { baselineSeedCriteria, expectedCriterionCounts, verifySeedCriteriaLinks } = require('./seed-verify-criteria');
|
|
17
|
-
const sess = require(os.homedir() + '/.config/cloudbongos/gds-session.json');
|
|
18
|
-
const BASE = sess.api_base.replace(/\/$/, '');
|
|
19
|
-
const TOKEN = sess.token;
|
|
20
|
-
const VER = 'BONGOS-V2';
|
|
21
|
-
const GOAL = 1000095;
|
|
22
|
-
const SRC = 'planning-session';
|
|
23
|
-
const REF_PREFIX = 'bv2-ideator-surface';
|
|
24
|
-
const H = { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' };
|
|
25
|
-
|
|
26
|
-
const CRIT = {
|
|
27
|
-
lost: 'ideator-nothing-is-lost',
|
|
28
|
-
fork: 'ideator-tag-survives-the-fork',
|
|
29
|
-
sky: 'ideator-sky-tells-the-truth',
|
|
30
|
-
full: 'ideator-full-idea-shapes-the-space',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const SPEC = 'Spec: `docs/specs/bongos-v2-ideator-surface.md` (final). Design of record: idea 1001072 + `docs/design/ideator-surface-direction.md`.';
|
|
34
|
-
|
|
35
|
-
const { sleep } = require('../../src/sleep'); // the one shared sleep (task 1004893)
|
|
36
|
-
async function api(method, path, body, _tries = 0) {
|
|
37
|
-
await sleep(400); // base throttle to stay under the per-IP rate limit
|
|
38
|
-
const res = await fetch(`${BASE}/api/bongos${path}`, {
|
|
39
|
-
method, headers: H, body: body ? JSON.stringify(body) : undefined,
|
|
40
|
-
});
|
|
41
|
-
const text = await res.text();
|
|
42
|
-
let json; try { json = JSON.parse(text); } catch { json = { _raw: text.slice(0, 300) }; }
|
|
43
|
-
if (res.status === 429 && _tries < 8) {
|
|
44
|
-
const wait = ((json.error && json.error.details && json.error.details.retry_after_seconds) || 30) + 1;
|
|
45
|
-
console.log(` …rate-limited, waiting ${wait}s`);
|
|
46
|
-
await sleep(wait * 1000);
|
|
47
|
-
return api(method, path, body, _tries + 1);
|
|
48
|
-
}
|
|
49
|
-
return { status: res.status, ok: res.ok, json };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// TOPOLOGICAL ORDER. The BV2.R## prefix is assigned from this array's index, so
|
|
53
|
-
// for any task A depending on B, B sits earlier and carries the lower number.
|
|
54
|
-
// Nothing else on BONGOS-V2 uses a rank prefix; these 27 carry one so the batch
|
|
55
|
-
// reads in its planned order inside each priority bucket.
|
|
56
|
-
const TASKS = [
|
|
57
|
-
// ---- T1: the two spikes. Neither auto-promotes (kind='spike', ADR 0015).
|
|
58
|
-
{
|
|
59
|
-
ref: 'R07', kind: 'spike', discipline: 'engineer', priority: 5, crit: [CRIT.fork],
|
|
60
|
-
title: 'Spike: how provenance is recorded without widening the task hierarchy',
|
|
61
|
-
desc: `Output an ADR naming the edge shape, where it is written, and who writes it when work is split or rewritten.
|
|
62
|
-
|
|
63
|
-
**The constraint that makes this a spike.** \`modules/lifecycle/hierarchy.js\` caps task parentage at ONE level (a parent may not itself have a parent; a task with children may not become a child) and refuses cross-version trees. Both are deliberate — ship rollup and claim safety lean on them. A descent of the shape idea → task → rewrite → split → removal is four levels and crosses a version boundary, so it cannot ride on that relation, and **widening it is explicitly out of scope** (spec decision 8).
|
|
64
|
-
|
|
65
|
-
Provenance is a different relation from work decomposition. The expected home is \`ideas_task_lineage\`, which is already many-ideas-to-one-task and carries none of those invariants — but confirm that before assuming it.
|
|
66
|
-
|
|
67
|
-
**Also settle:** who writes the edge. Parentage today is a manual explicit act, so an edge that depends on someone remembering will have holes. ${SPEC}`,
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
ref: 'R14', kind: 'spike', discipline: 'engineer', priority: 5, crit: [CRIT.sky],
|
|
71
|
-
title: 'Spike: what counts as removed, and who says so',
|
|
72
|
-
desc: `Output an ADR settling the removal signal: its source, and whether it is derived or declared.
|
|
73
|
-
|
|
74
|
-
**Nothing records this today.** \`shipped\` is terminal (\`modules/lifecycle/rot.js\` says so in as many words), there is no reverted status, and \`tasks.merged_into_task_id\` is CHECK-constrained to abandoned rows so it can only ever describe work that was cut before it landed. This signal is what lets brightness fall at all.
|
|
75
|
-
|
|
76
|
-
**The owner's decision (spec decision 13) is the shape, not the detail:** the system PROPOSES and a person CONFIRMS. \`modules/lifecycle/cascade.js\` already implements ship-generates-work and ships with an EMPTY rule set, so the mechanism exists and only the rule is new. A derived-only signal dims the sky for refactors, renames and file moves; a declared-only one rots because nobody remembers.
|
|
77
|
-
|
|
78
|
-
Nearest existing signal to build the derivation on: \`modules/economy/credits.js\` \`netNegativeBonusAmount\`, which already reads \`lines_removed > lines_added\` from a ship's own diff — the only place the system counts deletion. ${SPEC}`,
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
// ---- Zero-dep surfaces and settings.
|
|
82
|
-
{
|
|
83
|
-
ref: 'R01', kind: 'feature', discipline: 'ui', priority: 4, crit: [CRIT.lost],
|
|
84
|
-
title: 'The receipt persists',
|
|
85
|
-
desc: `A filing's outcome is stated and survives the next keystroke; the last several outcomes stay reachable on the surface that filed them.
|
|
86
|
-
|
|
87
|
-
**This is the bug that started the session.** The hall already prints "Filed as task N under <goal>" on a routed capture (\`modules/hall-ui/public/ideas.js\`, the \`#fq-result\` line) — and then clears it the moment the composer is touched again. The signal exists; its half-life is about four seconds.
|
|
88
|
-
|
|
89
|
-
Done-when: a person files a thought, keeps working, and can still see what became of it without navigating anywhere. ${SPEC}`,
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
ref: 'R02', kind: 'feature', discipline: 'ui', priority: 4, crit: [CRIT.lost],
|
|
93
|
-
title: 'Gravity is visible before release',
|
|
94
|
-
desc: `The goal that will catch a thought is shown while composing, and letting it float is an explicit choice rather than an empty dropdown.
|
|
95
|
-
|
|
96
|
-
**No new routing.** \`modules/ideas/routing.js\` \`decideLanding\` already decides this at capture time, and \`GET /goals/suggestions\` already feeds the composer's goal select. This task makes the decision legible at the moment it is made — the invisible dropdown is the direct cause of the confusion this whole scope came from (spec decision 2).
|
|
97
|
-
|
|
98
|
-
Done-when: before releasing a thought, a person can see which goal will catch it, change it, or deliberately let it float. ${SPEC}`,
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
ref: 'R05', kind: 'feature', discipline: 'engineer', priority: 3, crit: [CRIT.lost],
|
|
102
|
-
title: 'The per-instance dials: the ignite floor and the decay rate',
|
|
103
|
-
desc: `Both configured rather than constant, sharing one home. Defaults preserve today's behaviour and halving respectively.
|
|
104
|
-
|
|
105
|
-
**Two dials, one surface** (spec decisions 3 and 16). The ignite floor — who may convert a quick idea directly into claimable work — is today the \`METIC_PLUS\` constant in \`modules/ideas/routing.js\`. The decay rate — how fast an ideator's share falls per hop of the descent — is new in R11. The owner's ruling is that both belong to the project, not to the platform: a project of one wants every thought claimable, a project of fifty wants a nod.
|
|
106
|
-
|
|
107
|
-
They are scoped together deliberately so a second dial does not grow its own separate home. Do not widen either default as a side effect. ${SPEC}`,
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
ref: 'R20', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.full],
|
|
111
|
-
title: 'The new five questions as a versioned template',
|
|
112
|
-
desc: `\`base-v2\` ships beside \`base-v1\`; ideas already filed keep scoring against the questions they were actually asked.
|
|
113
|
-
|
|
114
|
-
**The owner's question set, in order:** what made you have the idea; scope as best as you can here; what should an engineer or artist know prior to building; how is this long term justified in the project; what does a successful implementation of the idea look like.
|
|
115
|
-
|
|
116
|
-
**This is cheaper than it looks.** \`modules/ideas/templates.js\` already registers templates by key and every idea stores its \`template_key\`, so this is a second entry in the registry, NOT a migration. Renaming a label is free; only renaming a key would be a data migration, and this does not need to.
|
|
117
|
-
|
|
118
|
-
One field survives outright (what made you have the idea ≈ \`why_had\`) and one roughly (the long-term justification ≈ \`fit\`). Three are new and one is dropped. ${SPEC}`,
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
// ---- The provenance spine.
|
|
122
|
-
{
|
|
123
|
-
ref: 'R08', kind: 'feature', discipline: 'engineer', priority: 5, crit: [CRIT.fork], deps: ['R07'],
|
|
124
|
-
title: 'Provenance edges on the idea lineage ledger',
|
|
125
|
-
desc: `task→task descent edges recorded, to arbitrary depth and across version boundaries. The task hierarchy's one-level cap and its cross-version refusal are untouched.
|
|
126
|
-
|
|
127
|
-
Build to whatever R07's ADR settled. The write side that exists today is \`modules/ideas/inbox.js\` \`linkIdeaTask\`, an upsert on \`(task_id, idea_id)\`; \`ideas_task_lineage\` has been many-ideas-to-one-task since \`ideas_006\`.
|
|
128
|
-
|
|
129
|
-
**The property that matters:** the tag must survive a rewrite, a split and a merge. If it only survives one hop the result is a link, not a story — and, after R11, someone does not get paid. ${SPEC}`,
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
ref: 'R04', kind: 'refactor', discipline: 'ui', priority: 3, crit: [CRIT.lost], deps: ['R01'],
|
|
133
|
-
title: 'Fold the Full Idea form and the spark queue into the new surface',
|
|
134
|
-
desc: `Developing a thought is a step taken AFTER a one-line filing, never a gate before it. No dead links from \`/idea/:id\`, the command palette, or the nav.
|
|
135
|
-
|
|
136
|
-
**What the ideas page actually is today:** three stacked sections — the spark queue ("waiting to be developed"), the quick composer, and the five-field Full Idea composer with its live completeness meter. All three need a home. The Board Room does NOT — it is reached from the left nav and is not behind this page.
|
|
137
|
-
|
|
138
|
-
**The risk this task carries** (spec decision 18): a spark's whole value is that it costs nothing to have. Folding a five-question form onto the same surface is the most likely way to slow the one-line path. R06a exists to catch that; do not treat it as someone else's problem. ${SPEC}`,
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
ref: 'R11', kind: 'feature', discipline: 'engineer', priority: 5, crit: [CRIT.fork], deps: ['R08'],
|
|
142
|
-
title: 'The ideator payout follows the descent, decayed by depth',
|
|
143
|
-
desc: `A descendant's ship pays the origin at the decayed rate. Idempotent. Existing first-hop behaviour preserved exactly.
|
|
144
|
-
|
|
145
|
-
**The seam exists.** \`modules/economy/credits.js\` \`awardIdeaTaskShipCredit\` already resolves a shipped task back to its source idea and splits the payout — but only via the EARLIEST lineage link, one hop. A task split off later, or a child of a child, pays nobody.
|
|
146
|
-
|
|
147
|
-
**Decay** (spec decision 16): full share at the direct task, halving at each hop, so an infinite descent is worth at most twice the direct task. The rate is a dial (R05), not a constant.
|
|
148
|
-
|
|
149
|
-
**The boundary that stops farming** (spec decision 7): descendant WORK pays the origin; a separate idea that merely cites the origin does not. The descent displays more than the payout counts. ${SPEC}`,
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
ref: 'R09', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.fork], deps: ['R08'],
|
|
153
|
-
title: 'The descent reads to arbitrary depth and across versions',
|
|
154
|
-
desc: `One query, not per-hop round trips.
|
|
155
|
-
|
|
156
|
-
**Three findings inherited from abandoned task 1004054**, whose predecessor was built, graded and lost. They are free design requirements, not someone else's mess:
|
|
157
|
-
|
|
158
|
-
1. **Test at the RENDERED layer, not just the data layer.** The prior attempt tested only the server-side trail builder, which is exactly why finding 2 went uncaught.
|
|
159
|
-
2. **A merged idea's triage note lives on the MERGED row, not the target row.** The prior attempt read it from the target, so the headline scenario was the broken one.
|
|
160
|
-
3. **Collapse the hops into one query or a CTE.** The prior attempt ran 4–6 serial round trips per read.
|
|
161
|
-
|
|
162
|
-
Today's reader is \`modules/ideas/inbox.js\` \`listIdeaTrailForBuilder\`, which returns \`lineage_tasks[]\` — one hop only. ${SPEC}`,
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
ref: 'R15', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.sky], deps: ['R14'],
|
|
166
|
-
title: 'The cascade rule that proposes a removal',
|
|
167
|
-
desc: `A ship that deletes an earlier shipped task's work files a QUESTION, not a verdict. \`shipped\` stays terminal and no credit is ever clawed back.
|
|
168
|
-
|
|
169
|
-
Build to R14's ADR. \`modules/lifecycle/cascade.js\` already implements ship-generates-work and declares an EMPTY rule set — this is the first rule, so expect the mechanism to be sound and unexercised.
|
|
170
|
-
|
|
171
|
-
**Do not make this a verdict.** The owner chose propose-then-confirm precisely because refactors, renames and file moves all look like deletions to a diff. A rule that marks work removed on its own evidence will dim the sky for reasons that are not real. ${SPEC}`,
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
ref: 'R21', kind: 'feature', discipline: 'engineer', priority: 3, crit: [CRIT.full], deps: ['R20'],
|
|
175
|
-
title: 'Substance floors for the new Full Idea fields',
|
|
176
|
-
desc: `Floors chosen deliberately for each new field, because the completeness score feeds credits.
|
|
177
|
-
|
|
178
|
-
\`modules/ideas/completeness.js\` scores against the per-field \`substanceMin\` declared in the template; \`base-v1\` used a flat 40 characters for all five. The new fields are not interchangeable — "scope as best as you can here" and "what made you have the idea" do not deserve the same floor.
|
|
179
|
-
|
|
180
|
-
**The never-invent-a-field rule gets stronger here, not weaker** (the Ideator pack). The new set is deliberately resistant to ghost-writing: nobody but the filer can answer what made them have the idea. A floor that pushes an agent to pad a thin answer destroys exactly that property. A short answer scores short — that is the scorer working. ${SPEC}`,
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
ref: 'R22', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.full], deps: ['R20'],
|
|
184
|
-
title: 'A Full Idea carries a recommendation: goal or criterion',
|
|
185
|
-
desc: `The recommendation is carried from filing through to the board item and shown to voters.
|
|
186
|
-
|
|
187
|
-
**The ideator does not decide** (spec decision 9). They recommend; the board decides. The reasoning the owner accepted is that scope is the thing you learn last — the person who had the thought writes it, the people reading it judge how big it turned out to be.
|
|
188
|
-
|
|
189
|
-
The board item payload is \`boardWindowPayload\` in \`modules/ideas/routes/inbox.js\`; the section labels the Board Room renders are a hand-mirror of the template's field keys (\`modules/hall-ui/public/board-room.js\`, test-pinned) — so a template change and a board change move together. **The Board Room's own flow is unchanged.** ${SPEC}`,
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
ref: 'R12', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.fork], deps: ['R11'],
|
|
193
|
-
title: 'Size is the sum of those payouts',
|
|
194
|
-
desc: `Computable from the ledger for any idea. Compressed for drawing, with the true figure available.
|
|
195
|
-
|
|
196
|
-
**Size is credits, not karma** (spec decision 5). \`karma_log\` carries no task key and no idea key — \`migrations/040_builder_karma.sql\` states that this is deliberate — so "the karma a thought caused" is not a quantity that can be computed. Credits are already idea-attributed at ship, so they can.
|
|
197
|
-
|
|
198
|
-
Size never falls. That is what makes it independent of brightness, and what lets a thought whose work was torn out still render large. Sums are long-tailed, so compress for drawing and show the real number in the panel. ${SPEC}`,
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
ref: 'R10', kind: 'feature', discipline: 'ui', priority: 4, crit: [CRIT.fork], deps: ['R09'],
|
|
202
|
-
title: 'The descent surface on the idea detail page',
|
|
203
|
-
desc: `One thought at the top, everything it caused falling away below — including the branches that went out.
|
|
204
|
-
|
|
205
|
-
\`/idea/:id\` (\`modules/hall-ui/public/idea.js\`) is the canonical per-idea record and stays; this is its new section. It already renders a one-line "became task N" outcome, which this replaces with the full story.
|
|
206
|
-
|
|
207
|
-
Done-when: a person can trace a thing shipped months later back to the line that caused it, through rewrites, splits and merges. See \`docs/design/mocks/ideator-surface/Descent.dc.html\` for the agreed shape — it is a shape, not a pixel contract. ${SPEC}`,
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
ref: 'R15a', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.sky], deps: ['R15'],
|
|
211
|
-
title: 'Confirming or dismissing a removal proposal',
|
|
212
|
-
desc: `On the existing awaiting-nod surface, at the same floor as the nod (Metic+, \`task.promote\`). No new permission and no new page.
|
|
213
|
-
|
|
214
|
-
An unanswered proposal **rots on the normal clock** (spec decision 14) rather than accumulating — \`modules/lifecycle/rot.js\` and the \`GET /inbox/rotting\` feed already exist and this rides them.
|
|
215
|
-
|
|
216
|
-
The surface is \`modules/hall-ui/public/goal-inbox.js\`, which already renders the awaiting-nod queue and the rot feed side by side. Confirming is what makes brightness fall; dismissing is what stops a refactor from dimming the sky. ${SPEC}`,
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
ref: 'R23', kind: 'feature', discipline: 'engineer', priority: 3, crit: [CRIT.full], deps: ['R22'],
|
|
220
|
-
title: 'Ratify a Full Idea into a goal',
|
|
221
|
-
desc: `The goal half beside the existing criterion half. One transaction. The five answers land on the goal.
|
|
222
|
-
|
|
223
|
-
**The criterion half already exists** and is the model to follow: \`modules/ideas/inbox.js\` \`ratifyCriterionProposal\` writes \`done_when_criteria\` + the idea status + karma + the audit row in a single transaction, and \`POST /inbox/:id/ratify\` fronts it.
|
|
224
|
-
|
|
225
|
-
**Nothing today turns a filed thought into a goal** — goals come from a separate planning act — so this is the genuinely new path of the four in this criterion. \`POST /goals\` validates \`scope_modules\` against the live roster and escalates to Archon for a protected scope; a ratification path has to respect both. ${SPEC}`,
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
ref: 'R12a', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.fork], deps: ['R12'],
|
|
229
|
-
title: 'Backfill lineage for existing ideas, without backfilling money',
|
|
230
|
-
desc: `Pre-existing ideas carry a size computed from the descent they actually have. No credits are paid retroactively.
|
|
231
|
-
|
|
232
|
-
**And the consequence is surfaced, not hidden** (spec decision 17). For those ideas size is NOT a sum of payouts actually made, so it needs a historical marker or the number quietly lies about what it means. The owner chose this deliberately: it makes the sky meaningful on day one without spending credits again on work already paid for once.
|
|
233
|
-
|
|
234
|
-
Done-when: every idea filed before this shipped has a size, and anywhere that size is shown makes clear which kind of number it is. ${SPEC}`,
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
ref: 'R03', kind: 'feature', discipline: 'ui', priority: 3, crit: [CRIT.lost], deps: ['R12'],
|
|
238
|
-
title: 'The room',
|
|
239
|
-
desc: `A plain overview of one's own thinking: what was filed, what it became, its size, and whether it still burns.
|
|
240
|
-
|
|
241
|
-
**This is the page a person opens daily**, and it does the real work — the sky is ambient, the room is useful. It is deliberately unglamorous: rows, a size, a state.
|
|
242
|
-
|
|
243
|
-
**It is the author's own view. The profile keeps a compact public trail** (spec decision 19) — the two are not the same surface. \`modules/hall-ui/public/profile.js\` already renders an idea trail from \`GET /inbox/by-builder/:id\`; that stays as what someone else sees on you.
|
|
244
|
-
|
|
245
|
-
See \`docs/design/mocks/ideator-surface/Room.dc.html\` for the agreed shape. ${SPEC}`,
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
ref: 'R16', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.sky], deps: ['R15a', 'R12'],
|
|
249
|
-
title: 'Brightness — the live share of a descent',
|
|
250
|
-
desc: `Computable per idea, and it falls when a removal is confirmed.
|
|
251
|
-
|
|
252
|
-
**Graded, not binary** (spec decision 20): a thought whose descent is five things with three still alive is partly lit, and goes out only when nothing survives. That is what makes a star visibly dim as the project removes pieces of what it caused.
|
|
253
|
-
|
|
254
|
-
**Brightness never claws back credits.** The ledger is not rewritten when the project changes its mind — only the picture changes. Size, which R12 owns, does not move. ${SPEC}`,
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
ref: 'R06', kind: 'verify', discipline: 'engineer', priority: 3, crit: [CRIT.lost], deps: ['R01', 'R03'],
|
|
258
|
-
title: 'Proof: a thought filed from the hall is traceable an hour later without asking anyone',
|
|
259
|
-
desc: `**The live demonstration that \`ideator-nothing-is-lost\` is met.**
|
|
260
|
-
|
|
261
|
-
A person files a thought, leaves, comes back later, and finds what became of it unaided — no asking, no database, no one else's help. If they filed it aimed, they can see which goal caught it; if they let it float, they can see that nothing has claimed it yet.
|
|
262
|
-
|
|
263
|
-
This is the exact scenario that failed on 2026-09-19 and produced this whole scope: a thought was filed, it silently became a task under a goal, and the person who filed it could not find it five minutes later. The proof is that this can no longer happen. ${SPEC}`,
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
ref: 'R06a', kind: 'verify', discipline: 'engineer', priority: 3, crit: [CRIT.lost], deps: ['R04'],
|
|
267
|
-
title: 'Proof: the one-line path did not get slower',
|
|
268
|
-
desc: `**The guard on spec decision 18**, which is the decision in this scope carrying the most risk.
|
|
269
|
-
|
|
270
|
-
Filing a spark takes the same gestures and the same time it does today, with the Full Idea form present on the same surface. Measure it against today's path, not against an impression.
|
|
271
|
-
|
|
272
|
-
A spark's whole value is that it costs nothing to have. If the fast path picks up an extra step, a scroll, or a decision, this scope will have built something beautiful that people stop using — and they will go back to firing one-liners into Discord. A green build is not the evidence here; a timed comparison is. ${SPEC}`,
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
ref: 'R13', kind: 'verify', discipline: 'engineer', priority: 3, crit: [CRIT.fork], deps: ['R10', 'R12'],
|
|
276
|
-
title: 'Proof: a task split twice pays its origin at the decayed rate and appears in the descent',
|
|
277
|
-
desc: `**The live demonstration that \`ideator-tag-survives-the-fork\` is met.**
|
|
278
|
-
|
|
279
|
-
A real thought becomes a real task; that task is split; one of its children is split again; each ships. The origin is paid at the decayed rate at every hop, and the whole chain reads as one descent.
|
|
280
|
-
|
|
281
|
-
This is the criterion everything else stands on. If the tag dies at the first fork, the descent is a link, the size is wrong, and the sky is decoration over a number that does not mean anything. Demonstrate it on real work, not a fixture. ${SPEC}`,
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
ref: 'R17', kind: 'feature', discipline: 'engineer', priority: 4, crit: [CRIT.sky], deps: ['R16'],
|
|
285
|
-
title: 'The five states resolve from ledger facts',
|
|
286
|
-
desc: `star / protostar / satellite / remnant / cold body — each DERIVED, none a UI guess.
|
|
287
|
-
|
|
288
|
-
- **star** — caused work that is still alive
|
|
289
|
-
- **protostar** — filed, not yet ignited. Include the well-vs-open-space distinction: caught by a goal and awaiting a nod is a different state from claimed by nothing, and position is what carries it (spec decision, direction doc)
|
|
290
|
-
- **satellite** — folded into another thought; its value counts there
|
|
291
|
-
- **remnant** — it burned, then the project removed it; it keeps its size
|
|
292
|
-
- **cold body** — declined at triage; it never ignited, so it has no size at all
|
|
293
|
-
|
|
294
|
-
Done-when: every state is computed from the ledger, and no surface has to infer one. ${SPEC}`,
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
ref: 'R24', kind: 'verify', discipline: 'engineer', priority: 3, crit: [CRIT.full], deps: ['R23'],
|
|
298
|
-
title: 'Proof: a Full Idea ratified as a goal produces a goal carrying its five answers',
|
|
299
|
-
desc: `**The live demonstration that \`ideator-full-idea-shapes-the-space\` is met.**
|
|
300
|
-
|
|
301
|
-
A Full Idea is written through the new five questions, carries a recommendation, goes to the Board Room unchanged, is ratified as a GOAL, and the resulting goal carries those five answers natively — scope from "scope as best as you can here", done-when from "what does a successful implementation look like".
|
|
302
|
-
|
|
303
|
-
Run it end to end through the real Board Room. The point of this criterion is that an ideator's best work becomes the structure other people's thinking lands in; a goal that arrives without its answers has not demonstrated that. ${SPEC}`,
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
ref: 'R18', kind: 'feature', discipline: 'ui', priority: 3, crit: [CRIT.sky], deps: ['R17', 'R03'],
|
|
307
|
-
title: 'The sky',
|
|
308
|
-
desc: `The ambient view over the five states: size compressed, brightness live, goals as gravity wells and their criteria as the rings inside them.
|
|
309
|
-
|
|
310
|
-
**It arrives last on purpose.** By this point it draws numbers and states the rest of the work has already produced, which is what keeps it cheap. A galaxy is not trivial, and this scope only earns it because nothing else depends on it.
|
|
311
|
-
|
|
312
|
-
**It is never the only path to anything it shows** (spec decision 11). Every fact in the sky is reachable from the room or the descent.
|
|
313
|
-
|
|
314
|
-
See \`docs/design/mocks/ideator-surface/Sky.dc.html\`, and \`docs/design/ideator-surface-direction.md\` for why the project's own design system — a black void and one accent that lights only what is live — already supplies this. ${SPEC}`,
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
ref: 'R19', kind: 'verify', discipline: 'engineer', priority: 3, crit: [CRIT.sky], deps: ['R18'],
|
|
318
|
-
title: 'Proof: a large dead thought renders large and dead',
|
|
319
|
-
desc: `**The live demonstration that \`ideator-sky-tells-the-truth\` is met**, and the single scenario this criterion exists for.
|
|
320
|
-
|
|
321
|
-
A thought that caused substantial work which the project has since removed shows at **full size and zero brightness**, and is distinguishable at a glance from a thought that never caused anything at all.
|
|
322
|
-
|
|
323
|
-
That distinction is the whole reason size and brightness are two channels instead of one number. If those two render alike, the criterion is not met however green the build is. ${SPEC}`,
|
|
324
|
-
},
|
|
325
|
-
];
|
|
326
|
-
|
|
327
|
-
async function main() {
|
|
328
|
-
const ids = {};
|
|
329
|
-
const criterionIdListsPerTask = [];
|
|
330
|
-
|
|
331
|
-
// BASELINE, before anything is written. Goal 1000095 already held six criteria
|
|
332
|
-
// with live task links before this seed existed; verifying against the pre-seed
|
|
333
|
-
// snapshot plus the plan keeps those from reading as false mismatches while still
|
|
334
|
-
// catching any drift (task 1004668 moved this into seed-verify-criteria.js).
|
|
335
|
-
const baseline = await baselineSeedCriteria({ goalId: GOAL, fetchGoal: (gid) => api('GET', `/goals/${gid}`) });
|
|
336
|
-
|
|
337
|
-
console.log(`Seeding ${TASKS.length} tasks into goal ${GOAL} (${VER})…\n`);
|
|
338
|
-
|
|
339
|
-
for (let i = 0; i < TASKS.length; i++) {
|
|
340
|
-
const t = TASKS[i];
|
|
341
|
-
const rank = String(i + 1).padStart(2, '0');
|
|
342
|
-
const title = `BV2.R${rank} — ${t.title}`;
|
|
343
|
-
const r = await api('POST', '/tasks', {
|
|
344
|
-
version_id: VER,
|
|
345
|
-
goal_id: GOAL,
|
|
346
|
-
title,
|
|
347
|
-
description: t.desc,
|
|
348
|
-
kind: t.kind,
|
|
349
|
-
discipline: t.discipline,
|
|
350
|
-
priority: t.priority,
|
|
351
|
-
criterion_ids: t.crit,
|
|
352
|
-
source: SRC,
|
|
353
|
-
source_ref: `${REF_PREFIX}-${t.ref}`,
|
|
354
|
-
});
|
|
355
|
-
if (!r.ok) {
|
|
356
|
-
console.log(` ${t.ref} FAILED (${r.status}): ${JSON.stringify(r.json).slice(0, 300)}`);
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
ids[t.ref] = r.json.task.id;
|
|
360
|
-
// Count only links THIS run created. A re-run replays an existing task
|
|
361
|
-
// (`idempotent: true`) whose links the baseline already holds; counting it
|
|
362
|
-
// again would expect them twice (task 1004668).
|
|
363
|
-
if (!r.json.idempotent) criterionIdListsPerTask.push(t.crit);
|
|
364
|
-
console.log(` ${t.ref} → ${r.json.task.id} ${title.slice(0, 74)}`);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
console.log('\nWiring dependencies…');
|
|
368
|
-
for (const t of TASKS) {
|
|
369
|
-
if (!t.deps || !t.deps.length) continue;
|
|
370
|
-
const id = ids[t.ref];
|
|
371
|
-
if (!id) { console.log(` ${t.ref}: skipped, task was not created`); continue; }
|
|
372
|
-
const depIds = t.deps.map((d) => ids[d]).filter(Boolean);
|
|
373
|
-
if (depIds.length !== t.deps.length) {
|
|
374
|
-
console.log(` ${t.ref}: WARNING — ${t.deps.length - depIds.length} dep(s) unresolved`);
|
|
375
|
-
}
|
|
376
|
-
if (!depIds.length) continue;
|
|
377
|
-
const r = await api('POST', `/tasks/${id}/dependencies`, { depends_on_task_ids: depIds });
|
|
378
|
-
console.log(` ${t.ref} depends on ${t.deps.join(', ')} → ${r.ok ? 'OK' : `FAILED ${r.status} ${JSON.stringify(r.json).slice(0, 180)}`}`);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
console.log('');
|
|
382
|
-
await verifySeedCriteriaLinks({
|
|
383
|
-
goalId: GOAL,
|
|
384
|
-
expectedCounts: expectedCriterionCounts(criterionIdListsPerTask),
|
|
385
|
-
baseline,
|
|
386
|
-
fetchGoal: (gid) => api('GET', `/goals/${gid}`),
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
main().catch((e) => { console.error(e); process.exit(1); });
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# GDS batch-claim smoke test. Renamed from PMS in V3.R01.
|
|
3
|
-
#
|
|
4
|
-
# Exercises POST /api/bongos/claims/batch:
|
|
5
|
-
# Case A: 2 ready tasks, disjoint touches → 201, 2 claim rows.
|
|
6
|
-
# Case B: 2 ready tasks, OVERLAPPING touches → 409, BATCH_TOUCHES_CONFLICT,
|
|
7
|
-
# no claims persisted (both tasks stay 'ready').
|
|
8
|
-
# Case C: 1 ready + 1 already-claimed → 409, ALREADY_CLAIMED enumerated,
|
|
9
|
-
# no partial commit (the still-ready one is NOT consumed).
|
|
10
|
-
#
|
|
11
|
-
# Creates synthetic '__smoke__' tasks that are filtered from the public surfaces.
|
|
12
|
-
# Cleans up by releasing/abandoning every claim it creates and marking tasks
|
|
13
|
-
# 'abandoned' so they don't linger.
|
|
14
|
-
#
|
|
15
|
-
# Usage: bash scripts/bongos/smoke-batch-claim.sh
|
|
16
|
-
# Env: GDS_API_BASE / PMS_API_BASE override host (default: this instance's own,
|
|
17
|
-
# resolved from the session + branding pack — see lib.sh)
|
|
18
|
-
|
|
19
|
-
set -euo pipefail
|
|
20
|
-
|
|
21
|
-
# shellcheck source=scripts/bongos/lib.sh
|
|
22
|
-
source "$(dirname "$0")/lib.sh"
|
|
23
|
-
|
|
24
|
-
API="$(gds_api_base)"
|
|
25
|
-
TOKEN="$(gds_resolve_token)" || exit $?
|
|
26
|
-
|
|
27
|
-
# task 1003166 (G02): resolve the version at RUNTIME rather than naming one. A
|
|
28
|
-
# literal works until that version ships, then `version_closed` refuses every
|
|
29
|
-
# create and the smoke fails as if the code broke (idea 1000823).
|
|
30
|
-
VERSION_ID=$(node "$(dirname "$0")/version-select.js") || {
|
|
31
|
-
echo "FAIL: could not resolve a building version — see version-select.js" >&2
|
|
32
|
-
exit 1
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
ts=$(date +%Y%m%d-%H%M%S)
|
|
36
|
-
|
|
37
|
-
# Wrapper: METHOD PATH [BODY] → echoes "HTTP_CODE\nPAYLOAD". Never exits.
|
|
38
|
-
call() {
|
|
39
|
-
local method="$1" path="$2" body="${3:-}"
|
|
40
|
-
local args=(-sS -X "${method}" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -w "\n%{http_code}")
|
|
41
|
-
if [[ -n "${body}" ]]; then args+=(-d "${body}"); fi
|
|
42
|
-
curl "${args[@]}" "${API}${path}"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
# split into code + payload
|
|
46
|
-
parse_call() {
|
|
47
|
-
local out="$1"
|
|
48
|
-
CODE="${out##*$'\n'}"
|
|
49
|
-
PAYLOAD="${out%$'\n'*}"
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
mk_task() {
|
|
53
|
-
# mk_task <suffix> <touches-csv> → echoes new task id
|
|
54
|
-
local suffix="$1" touches_csv="$2"
|
|
55
|
-
local body
|
|
56
|
-
body=$(python3 -c "
|
|
57
|
-
import json,sys
|
|
58
|
-
t = '${touches_csv}'.split(',')
|
|
59
|
-
print(json.dumps({
|
|
60
|
-
'version_id': '${VERSION_ID}',
|
|
61
|
-
'title': '__smoke__ batch-claim ${ts}-${suffix}',
|
|
62
|
-
'description': 'smoke',
|
|
63
|
-
'status': 'ready',
|
|
64
|
-
'touches': t,
|
|
65
|
-
'est_minutes': 1,
|
|
66
|
-
'manual_degree': 1,
|
|
67
|
-
'priority': 5,
|
|
68
|
-
'automation_tag': 'overnight-safe',
|
|
69
|
-
'credits_reward': 0,
|
|
70
|
-
'source': 'smoke',
|
|
71
|
-
'source_ref': '${ts}-${suffix}'
|
|
72
|
-
}))")
|
|
73
|
-
parse_call "$(call POST /api/bongos/tasks "${body}")"
|
|
74
|
-
python3 -c "import json,sys; print(json.loads('''${PAYLOAD}''')['task']['id'])"
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
# Track created tasks + claims for cleanup.
|
|
78
|
-
CREATED_TASKS=()
|
|
79
|
-
CREATED_CLAIMS=()
|
|
80
|
-
cleanup() {
|
|
81
|
-
local leaked=()
|
|
82
|
-
echo
|
|
83
|
-
echo "--- cleanup ---"
|
|
84
|
-
for cid in "${CREATED_CLAIMS[@]:-}"; do
|
|
85
|
-
[[ -z "${cid}" ]] && continue
|
|
86
|
-
call POST "/api/bongos/claims/${cid}/resolve" \
|
|
87
|
-
'{"outcome":"abandoned","notes_md":"smoke cleanup"}' >/dev/null || true
|
|
88
|
-
done
|
|
89
|
-
for tid in "${CREATED_TASKS[@]:-}"; do
|
|
90
|
-
[[ -z "${tid}" ]] && continue
|
|
91
|
-
# task 1002856: this used to POST /tasks/:id/promote {"status":"abandoned"},
|
|
92
|
-
# which is the backlog->ready transition and ignores a status field — so it
|
|
93
|
-
# never abandoned anything, and `>/dev/null 2>&1 || true` swallowed the
|
|
94
|
-
# failure. Every run since has leaked its fixtures as status='ready'
|
|
95
|
-
# (the 2026-05-11 batch, then 1001920-1001925 from 2026-07-03). The real
|
|
96
|
-
# endpoint is POST /tasks/:id/abandon {"reason": ...}.
|
|
97
|
-
if ! call POST "/api/bongos/tasks/${tid}/abandon" \
|
|
98
|
-
'{"reason":"smoke cleanup (scripts/bongos/smoke-batch-claim.sh)"}' >/dev/null 2>&1; then
|
|
99
|
-
leaked+=("${tid}")
|
|
100
|
-
fi
|
|
101
|
-
done
|
|
102
|
-
# Never silently leave debris: a failed cleanup is reported, not swallowed.
|
|
103
|
-
# The claimable feed filters `__smoke__%` by title, so a leak is a ledger
|
|
104
|
-
# accuracy problem (it inflates open-task counts and goal rollups), not a
|
|
105
|
-
# builder-facing one — but it still needs a human to clear it.
|
|
106
|
-
if [[ ${#leaked[@]} -gt 0 ]]; then
|
|
107
|
-
echo "WARN: could not abandon ${#leaked[@]} smoke task(s): ${leaked[*]}"
|
|
108
|
-
echo "WARN: clear them with: node scripts/bongos/api.js POST /tasks/<id>/abandon --body '{\"reason\":\"smoke residue\"}'"
|
|
109
|
-
fi
|
|
110
|
-
echo "cleanup done"
|
|
111
|
-
}
|
|
112
|
-
trap cleanup EXIT
|
|
113
|
-
|
|
114
|
-
fail=0
|
|
115
|
-
ok() { echo "PASS $*"; }
|
|
116
|
-
bad() { echo "FAIL $*"; fail=1; }
|
|
117
|
-
|
|
118
|
-
echo "=== GDS batch-claim smoke ==="
|
|
119
|
-
echo "API: ${API}"
|
|
120
|
-
echo
|
|
121
|
-
|
|
122
|
-
# ---- Case A: disjoint touches → 201 with 2 claim rows ------------------
|
|
123
|
-
echo "--- A: disjoint-touches batch (expect 201) ---"
|
|
124
|
-
TA1=$(mk_task A1 "__smoke__/batch/${ts}/a1")
|
|
125
|
-
TA2=$(mk_task A2 "__smoke__/batch/${ts}/a2")
|
|
126
|
-
CREATED_TASKS+=("${TA1}" "${TA2}")
|
|
127
|
-
echo " created tasks ${TA1}, ${TA2}"
|
|
128
|
-
|
|
129
|
-
BODY_A=$(python3 -c "import json; print(json.dumps({'task_ids':[${TA1},${TA2}],'worktree_name':'__smoke_batch__'}))")
|
|
130
|
-
parse_call "$(call POST /api/bongos/claims/batch "${BODY_A}")"
|
|
131
|
-
if [[ "${CODE}" == "201" ]]; then
|
|
132
|
-
N=$(python3 -c "import json,sys; print(len(json.loads('''${PAYLOAD}''')['claims']))")
|
|
133
|
-
if [[ "${N}" == "2" ]]; then
|
|
134
|
-
ok "A: 201 with 2 claim rows"
|
|
135
|
-
# capture both claim ids for cleanup
|
|
136
|
-
while read -r cid; do CREATED_CLAIMS+=("${cid}"); done < <(python3 -c "
|
|
137
|
-
import json,sys
|
|
138
|
-
d = json.loads('''${PAYLOAD}''')
|
|
139
|
-
for c in d['claims']: print(c['id'])
|
|
140
|
-
")
|
|
141
|
-
else
|
|
142
|
-
bad "A: expected 2 claims, got ${N}"
|
|
143
|
-
fi
|
|
144
|
-
else
|
|
145
|
-
bad "A: expected 201, got ${CODE} → ${PAYLOAD}"
|
|
146
|
-
fi
|
|
147
|
-
|
|
148
|
-
# ---- Case B: overlapping touches → 409 BATCH_TOUCHES_CONFLICT ----------
|
|
149
|
-
echo
|
|
150
|
-
echo "--- B: overlapping-touches batch (expect 409, no claims) ---"
|
|
151
|
-
TB1=$(mk_task B1 "__smoke__/batch/${ts}/shared")
|
|
152
|
-
TB2=$(mk_task B2 "__smoke__/batch/${ts}/shared")
|
|
153
|
-
CREATED_TASKS+=("${TB1}" "${TB2}")
|
|
154
|
-
echo " created tasks ${TB1}, ${TB2} (both touch __smoke__/batch/${ts}/shared)"
|
|
155
|
-
|
|
156
|
-
BODY_B=$(python3 -c "import json; print(json.dumps({'task_ids':[${TB1},${TB2}]}))")
|
|
157
|
-
parse_call "$(call POST /api/bongos/claims/batch "${BODY_B}")"
|
|
158
|
-
if [[ "${CODE}" == "409" ]]; then
|
|
159
|
-
REASON=$(python3 -c "
|
|
160
|
-
import json,sys
|
|
161
|
-
d = json.loads('''${PAYLOAD}''')
|
|
162
|
-
fs = d.get('failures', [])
|
|
163
|
-
reasons = sorted({f.get('reason','') for f in fs})
|
|
164
|
-
print(','.join(reasons))
|
|
165
|
-
")
|
|
166
|
-
if [[ "${REASON}" == *"BATCH_TOUCHES_CONFLICT"* ]]; then
|
|
167
|
-
ok "B: 409 with BATCH_TOUCHES_CONFLICT (reasons: ${REASON})"
|
|
168
|
-
else
|
|
169
|
-
bad "B: 409 but missing BATCH_TOUCHES_CONFLICT (reasons: ${REASON})"
|
|
170
|
-
fi
|
|
171
|
-
# Confirm neither task got claimed (status still 'ready').
|
|
172
|
-
for tid in "${TB1}" "${TB2}"; do
|
|
173
|
-
parse_call "$(call GET "/api/bongos/tasks/${tid}")"
|
|
174
|
-
S=$(python3 -c "import json,sys; print(json.loads('''${PAYLOAD}''')['task']['status'])")
|
|
175
|
-
if [[ "${S}" == "ready" ]]; then
|
|
176
|
-
ok "B: task #${tid} stayed 'ready' (rollback worked)"
|
|
177
|
-
else
|
|
178
|
-
bad "B: task #${tid} status = ${S} (rollback failed)"
|
|
179
|
-
fi
|
|
180
|
-
done
|
|
181
|
-
else
|
|
182
|
-
bad "B: expected 409, got ${CODE} → ${PAYLOAD}"
|
|
183
|
-
fi
|
|
184
|
-
|
|
185
|
-
# ---- Case C: one ready + one already-claimed → 409, no partial commit --
|
|
186
|
-
echo
|
|
187
|
-
echo "--- C: one ready + one already-claimed (expect 409, ready task NOT consumed) ---"
|
|
188
|
-
TC1=$(mk_task C1 "__smoke__/batch/${ts}/c1")
|
|
189
|
-
TC2=$(mk_task C2 "__smoke__/batch/${ts}/c2")
|
|
190
|
-
CREATED_TASKS+=("${TC1}" "${TC2}")
|
|
191
|
-
echo " created tasks ${TC1} (will pre-claim), ${TC2} (still ready)"
|
|
192
|
-
|
|
193
|
-
# Pre-claim TC1 via the single-claim path so it's already 'active'.
|
|
194
|
-
parse_call "$(call POST /api/bongos/claims "{\"task_id\":${TC1}}")"
|
|
195
|
-
if [[ "${CODE}" == "201" ]]; then
|
|
196
|
-
PRE_CLAIM_ID=$(python3 -c "import json,sys; print(json.loads('''${PAYLOAD}''')['claim']['id'])")
|
|
197
|
-
CREATED_CLAIMS+=("${PRE_CLAIM_ID}")
|
|
198
|
-
ok "C: pre-claim of #${TC1} = claim #${PRE_CLAIM_ID}"
|
|
199
|
-
else
|
|
200
|
-
bad "C: pre-claim failed (${CODE}); skipping rest of case"
|
|
201
|
-
fi
|
|
202
|
-
|
|
203
|
-
BODY_C=$(python3 -c "import json; print(json.dumps({'task_ids':[${TC1},${TC2}]}))")
|
|
204
|
-
parse_call "$(call POST /api/bongos/claims/batch "${BODY_C}")"
|
|
205
|
-
if [[ "${CODE}" == "409" ]]; then
|
|
206
|
-
ENUM=$(python3 -c "
|
|
207
|
-
import json,sys
|
|
208
|
-
d = json.loads('''${PAYLOAD}''')
|
|
209
|
-
fs = d.get('failures', [])
|
|
210
|
-
ids = sorted({int(f['task_id']) for f in fs if 'task_id' in f})
|
|
211
|
-
print(','.join(str(i) for i in ids))
|
|
212
|
-
")
|
|
213
|
-
# Must enumerate the failure for #TC1 (TASK_NOT_READY since it's now 'active').
|
|
214
|
-
if [[ "${ENUM}" == *"${TC1}"* ]]; then
|
|
215
|
-
ok "C: 409 enumerates failing task (#${TC1}) in failures[]"
|
|
216
|
-
else
|
|
217
|
-
bad "C: 409 but failures[] missing #${TC1} (got: ${ENUM})"
|
|
218
|
-
fi
|
|
219
|
-
# Confirm TC2 stayed 'ready' (no partial commit).
|
|
220
|
-
parse_call "$(call GET "/api/bongos/tasks/${TC2}")"
|
|
221
|
-
S=$(python3 -c "import json,sys; print(json.loads('''${PAYLOAD}''')['task']['status'])")
|
|
222
|
-
if [[ "${S}" == "ready" ]]; then
|
|
223
|
-
ok "C: task #${TC2} stayed 'ready' (no partial commit)"
|
|
224
|
-
else
|
|
225
|
-
bad "C: task #${TC2} status = ${S} (partial commit leaked)"
|
|
226
|
-
fi
|
|
227
|
-
else
|
|
228
|
-
bad "C: expected 409, got ${CODE} → ${PAYLOAD}"
|
|
229
|
-
fi
|
|
230
|
-
|
|
231
|
-
echo
|
|
232
|
-
echo "--- summary ---"
|
|
233
|
-
if [[ "${fail}" -eq 0 ]]; then
|
|
234
|
-
echo "OK: batch-claim smoke passed"
|
|
235
|
-
else
|
|
236
|
-
echo "FAIL: one or more checks failed"
|
|
237
|
-
fi
|
|
238
|
-
exit "${fail}"
|