@bongos/core 1.23.22 → 1.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +902 -407
- 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 +90 -0
- package/clients/bongos-client/index.cjs +90 -0
- package/clients/bongos-client/index.d.ts +149 -4
- package/clients/bongos-client/index.mjs +90 -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/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 +5 -1
- package/docs/api/openapi.json +8555 -5876
- package/docs/api-reference.md +63 -10
- package/docs/architecture.md +37 -1
- package/docs/canonical-permissions.md +1 -1
- package/docs/copy-inventory.md +420 -337
- package/docs/copy-registry.json +1321 -500
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +5 -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 +55 -6
- package/docs/page-readings.json +1185 -1011
- 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/catalog.js +6 -0
- package/modules/government/db.js +34 -0
- package/modules/government/docket.js +36 -0
- package/modules/government/migrations/government_035_payout_approve.sql +29 -0
- package/modules/government/pollers/board-expiry.js +108 -20
- package/modules/government/protected-surfaces.json +1 -0
- package/modules/government/routes/government.js +22 -0
- package/modules/government/routing.js +39 -5
- package/modules/government/waiting.js +74 -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 +156 -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/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 +24 -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 +220 -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/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 +3 -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/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_protected_surfaces.mjs +1 -1
- package/tests/government_routes.mjs +1 -1
- 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,156 @@
|
|
|
1
|
+
// blockers-by-person.js — open blockers grouped UNDER THE PEOPLE they wait on (task 1004999).
|
|
2
|
+
//
|
|
3
|
+
// WHY. The Blockers page listed every open blocker under one heading, "Requires project
|
|
4
|
+
// owner fix", whoever it actually waited on. A blocker has no assignee; who it waits on
|
|
5
|
+
// is the seat its ask names (owner, a building's Lead, a board), and who sits in that
|
|
6
|
+
// seat changes. The server resolves it at read time with the City's own routing
|
|
7
|
+
// (GET /government/blockers/people), so this page and the City can never disagree about
|
|
8
|
+
// whose a blocker is. This file only draws the answer.
|
|
9
|
+
//
|
|
10
|
+
// The viewer's own group comes first, marked "You". A blocker that resolves to nobody is
|
|
11
|
+
// shown under "Nobody yet" — never dropped. A signed-out viewer, or one the read refuses,
|
|
12
|
+
// sees no section at all: the queue below stays the page.
|
|
13
|
+
//
|
|
14
|
+
// UMD like blockers-lib.js: the pure `peopleHtml` is required by
|
|
15
|
+
// tests/blockers_people.mjs; the mount runs only in a browser.
|
|
16
|
+
(function (root, factory) {
|
|
17
|
+
var mod = factory();
|
|
18
|
+
if (typeof module !== 'undefined' && module.exports) module.exports = mod;
|
|
19
|
+
else root.OTBBlockersPeople = mod;
|
|
20
|
+
}(typeof self !== 'undefined' ? self : this, function () {
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
function nameOf(p) {
|
|
24
|
+
return (p && (p.name || p.login)) || (p && p.id != null ? 'Builder ' + p.id : 'Someone');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// "as the owner" · "as the Planning Lead" · "as the owner, since Planning has no Lead".
|
|
28
|
+
function asWords(item) {
|
|
29
|
+
var as = item.as || 'their seat';
|
|
30
|
+
if (item.fell_to_owner && /^lead:([a-z]+)$/.test(String(item.asked || ''))) {
|
|
31
|
+
var b = String(item.asked).slice(5);
|
|
32
|
+
return 'as ' + as + ', since ' + b.charAt(0).toUpperCase() + b.slice(1) + ' has no Lead';
|
|
33
|
+
}
|
|
34
|
+
return 'as ' + as;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function rowHtml(item, o) {
|
|
38
|
+
var esc = o.esc;
|
|
39
|
+
var title = esc(item.title || ('Blocker ' + item.id));
|
|
40
|
+
var href = o.blockerHref ? o.blockerHref(item.id) : null;
|
|
41
|
+
var titleHtml = href
|
|
42
|
+
? '<a class="ov-row__title" href="' + esc(href) + '">' + title + '</a>'
|
|
43
|
+
: '<span class="ov-row__title">' + title + '</span>';
|
|
44
|
+
var facts = [esc(asWords(item))];
|
|
45
|
+
if (item.opened_at) facts.push('filed ' + esc(o.fmtDate(item.opened_at)));
|
|
46
|
+
var q = item.question ? '<span class="ov-row__sub">' + esc(item.question) + '</span>' : '';
|
|
47
|
+
return '<li class="ov-row ov-row--dense"><div class="ov-row__main">' + titleHtml + q
|
|
48
|
+
+ '<div class="ov-row__facts"><span class="ov-row__when">' + facts.join(' · ') + '</span></div></div></li>';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function groupHtml(name, n, note, items, o) {
|
|
52
|
+
return '<div class="ov-group">'
|
|
53
|
+
+ '<div class="ov-group__head"><span class="ov-group__name">' + o.esc(name) + '</span>'
|
|
54
|
+
+ '<span class="ov-group__n">' + n + '</span>'
|
|
55
|
+
+ (note ? '<span class="ov-group__note">' + o.esc(note) + '</span>' : '')
|
|
56
|
+
+ '</div><ul class="ov-rows">' + items.map(function (it) { return rowHtml(it, o); }).join('') + '</ul></div>';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// peopleHtml(data, { esc, fmtDate, viewerId, blockerHref }) → markup, or '' when there
|
|
60
|
+
// is nothing to show. `data` is GET /government/blockers/people.
|
|
61
|
+
function peopleHtml(data, o) {
|
|
62
|
+
if (!o || typeof o.esc !== 'function' || typeof o.fmtDate !== 'function') {
|
|
63
|
+
throw new TypeError('peopleHtml: opts.esc and opts.fmtDate are required');
|
|
64
|
+
}
|
|
65
|
+
if (!data || !data.available) return '';
|
|
66
|
+
var people = Array.isArray(data.people) ? data.people.slice() : [];
|
|
67
|
+
var nobody = Array.isArray(data.nobody) ? data.nobody : [];
|
|
68
|
+
if (!people.length && !nobody.length) return '';
|
|
69
|
+
var me = o.viewerId == null ? null : String(o.viewerId);
|
|
70
|
+
people.sort(function (a, b) { return (a.person.id === me ? 0 : 1) - (b.person.id === me ? 0 : 1); });
|
|
71
|
+
var html = people.map(function (g) {
|
|
72
|
+
var mine = g.person.id === me;
|
|
73
|
+
return groupHtml(mine ? 'You' : nameOf(g.person), g.count, mine ? 'waiting on you' : '', g.items, o);
|
|
74
|
+
}).join('');
|
|
75
|
+
if (nobody.length) html += groupHtml('Nobody yet', nobody.length, 'no one sits in the seat it waits on', nobody, o);
|
|
76
|
+
return html;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// mineHtml(data, opts) → the viewer's own rows, for Collab's "waiting on you" block.
|
|
80
|
+
// data is GET /government/blockers/for-me ({ available, count, items }).
|
|
81
|
+
function mineHtml(data, o) {
|
|
82
|
+
if (!o || typeof o.esc !== 'function' || typeof o.fmtDate !== 'function') {
|
|
83
|
+
throw new TypeError('mineHtml: opts.esc and opts.fmtDate are required');
|
|
84
|
+
}
|
|
85
|
+
if (!data || !data.available || !Array.isArray(data.items) || !data.items.length) return '';
|
|
86
|
+
return data.items.map(function (it) { return rowHtml(it, o); }).join('');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
var blockerHref = function (id) { return '#/blocker/' + encodeURIComponent(id); };
|
|
90
|
+
|
|
91
|
+
// Collab's block: only what waits on the viewer. Hidden when nothing does.
|
|
92
|
+
function mountMine(doc, kit) {
|
|
93
|
+
var section = doc.getElementById('cb-blockers-scroll');
|
|
94
|
+
var list = doc.getElementById('cb-blockers');
|
|
95
|
+
if (!section || !list) return;
|
|
96
|
+
fetch('/api/bongos/government/blockers/for-me', { credentials: 'same-origin' })
|
|
97
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
98
|
+
.catch(function () { return null; })
|
|
99
|
+
.then(function (data) {
|
|
100
|
+
var html = mineHtml(data, { esc: kit.esc, fmtDate: kit.fmtAbsDate, blockerHref: blockerHref });
|
|
101
|
+
if (!html) return;
|
|
102
|
+
list.innerHTML = html;
|
|
103
|
+
var n = doc.getElementById('cb-blockers-n');
|
|
104
|
+
if (n) n.textContent = String(data.count);
|
|
105
|
+
section.hidden = false;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// The browser half: fetch, draw, reveal. Any failure leaves the section hidden.
|
|
110
|
+
// Collab loads dom-utils but not the kit, so the two helpers fall back to the shell's.
|
|
111
|
+
function helpers() {
|
|
112
|
+
var kit = window.OTBKit || {};
|
|
113
|
+
var esc = kit.esc || (window.OTB && window.OTB.escapeHtml);
|
|
114
|
+
var fmtDate = kit.fmtAbsDate || function (iso) {
|
|
115
|
+
var d = new Date(iso);
|
|
116
|
+
return isNaN(d.getTime()) ? '' : d.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric' });
|
|
117
|
+
};
|
|
118
|
+
return typeof esc === 'function' ? { esc: esc, fmtAbsDate: fmtDate } : null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function mount() {
|
|
122
|
+
var doc = typeof document !== 'undefined' ? document : null;
|
|
123
|
+
var h = doc ? helpers() : null;
|
|
124
|
+
if (!h) return;
|
|
125
|
+
mountMine(doc, h);
|
|
126
|
+
var section = doc.getElementById('blockers-people-scroll');
|
|
127
|
+
var body = doc.getElementById('blockers-people-body');
|
|
128
|
+
var kit = h;
|
|
129
|
+
if (!section || !body) return;
|
|
130
|
+
var get = function (path) {
|
|
131
|
+
return fetch('/api/bongos' + path, { credentials: 'same-origin' })
|
|
132
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
133
|
+
.catch(function () { return null; });
|
|
134
|
+
};
|
|
135
|
+
Promise.all([get('/government/blockers/people'), get('/me')]).then(function (res) {
|
|
136
|
+
var data = res[0];
|
|
137
|
+
var me = res[1] && (res[1].builder || res[1]);
|
|
138
|
+
var html = peopleHtml(data, {
|
|
139
|
+
esc: kit.esc,
|
|
140
|
+
fmtDate: kit.fmtAbsDate,
|
|
141
|
+
viewerId: me && me.id != null ? me.id : null,
|
|
142
|
+
blockerHref: blockerHref,
|
|
143
|
+
});
|
|
144
|
+
if (!html) return;
|
|
145
|
+
body.innerHTML = html;
|
|
146
|
+
section.hidden = false;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
151
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);
|
|
152
|
+
else mount();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { peopleHtml: peopleHtml, mineHtml: mineHtml, asWords: asWords };
|
|
156
|
+
}));
|
|
@@ -101,5 +101,19 @@
|
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
// Which human signal a blocker wears (task 1004946, owner 2026-10-28): every
|
|
105
|
+
// blocker waits on a person, but some ask them to DO a thing and some to
|
|
106
|
+
// CHOOSE. The stored ask (government_asks, on GET /blockers as `ask`) says
|
|
107
|
+
// which: two or more REAL options is a choice, a HUMAN DECISION. An option
|
|
108
|
+
// that only defers ("Not yet", "later") is no choice at all, so "Do it / Not
|
|
109
|
+
// yet" is a HUMAN TASK, as is a blocker with no ask. One rule for the Blockers
|
|
110
|
+
// page and Home's slice, so the two never disagree.
|
|
111
|
+
var DEFER_KEYS = /^(later|not[-_ ]?yet|notnow|not[-_ ]now|wait|defer|skip|no)$/i;
|
|
112
|
+
function humanKind(b) {
|
|
113
|
+
var opts = b && b.ask && Array.isArray(b.ask.options) ? b.ask.options : [];
|
|
114
|
+
var real = opts.filter(function (o) { return o && !DEFER_KEYS.test(String(o.key || '')); });
|
|
115
|
+
return real.length >= 2 ? 'decision' : 'task';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { blockingLine: blockingLine, blockerFacts: blockerFacts, humanKind: humanKind };
|
|
105
119
|
}));
|
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
bar. task 1003456 added the write half: filing (any signed-in
|
|
34
34
|
builder) below the heading, and per-row Resolve/Link (blocker.review
|
|
35
35
|
— metic+archon) rendered by blockers.js when the viewer holds it. -->
|
|
36
|
+
<!-- task 1004999: the same open blockers, grouped under the people they wait
|
|
37
|
+
on (GET /government/blockers/people — the City's routing). Hidden until
|
|
38
|
+
that read answers; the queue below stays the page without it. -->
|
|
39
|
+
<section class="scroll" id="blockers-people-scroll" aria-labelledby="blockers-people-h" hidden>
|
|
40
|
+
<h2 id="blockers-people-h" class="scroll__h">Waiting on people</h2>
|
|
41
|
+
<div id="blockers-people-body"></div>
|
|
42
|
+
</section>
|
|
36
43
|
<section class="scroll" id="blockers-scroll" aria-labelledby="blockers-h">
|
|
37
44
|
<h2 id="blockers-h" class="scroll__h">Requires project owner fix<span class="count-pill" id="blockers-count" hidden></span></h2>
|
|
38
45
|
<div id="blockers-file-area"></div>
|
|
@@ -67,5 +74,6 @@
|
|
|
67
74
|
<script src="/builders/blockers-lib.js"></script>
|
|
68
75
|
<script src="/builders/hall-ask.js"></script>
|
|
69
76
|
<script src="/builders/blockers.js"></script>
|
|
77
|
+
<script src="/builders/blockers-by-person.js"></script>
|
|
70
78
|
</body>
|
|
71
79
|
</html>
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
// bookkeeping, so it stays a plain control beside the facts (owner
|
|
148
148
|
// 2026-10-23: a card only where the page asks the reader to decide).
|
|
149
149
|
return window.OTBAsk.html({
|
|
150
|
-
attrs: { 'data-id': id },
|
|
150
|
+
attrs: { 'data-id': id }, human: lib.humanKind(b),
|
|
151
151
|
q: b.title || ('Blocker ' + id), qHref: blockerHref(id),
|
|
152
152
|
saidHtml: facts.noteHtml + '<div class="ov-row__facts blockers-facts">' + facts.factsHtml
|
|
153
153
|
+ '<button type="button" class="btn-ghost blockers-facts__link" data-link-open="' + kit.esc(id) + '">Link a task</button></div>',
|
|
@@ -89,6 +89,9 @@
|
|
|
89
89
|
// a private goal, so this must not invent one either.
|
|
90
90
|
case 'GOAL_MEMBERSHIP_REQUIRED':
|
|
91
91
|
return errMessage(data) || 'That task belongs to a goal you are not a member of.';
|
|
92
|
+
// task 1004950: the task needs hands on the live server.
|
|
93
|
+
case 'NEEDS_SERVER_ACCESS':
|
|
94
|
+
return errMessage(data) || 'This task needs server access, which you do not have yet, so pick another task or ask the project owner for it.';
|
|
92
95
|
case 'DEPS_NOT_SHIPPED':
|
|
93
96
|
return 'That task waits on unshipped dependencies. Pick another for now.';
|
|
94
97
|
// 10. the module-level sibling of TOUCHES_CONFLICT, and a REFUSAL where
|
|
@@ -63,6 +63,17 @@
|
|
|
63
63
|
<button class="cb-ask__copy cb-copy-all" id="cb-for-me-copy" type="button" hidden></button>
|
|
64
64
|
</section>
|
|
65
65
|
|
|
66
|
+
<!-- task 1004999: open blockers whose seat resolves to this viewer (as the
|
|
67
|
+
owner, a building's Lead, or a board member) — GET
|
|
68
|
+
/government/blockers/for-me, drawn by blockers-by-person.js. Hidden
|
|
69
|
+
until something waits on them. -->
|
|
70
|
+
<section class="scroll cb-block" id="cb-blockers-scroll" aria-labelledby="cb-blockers-h" hidden>
|
|
71
|
+
<div class="cb-block__head">
|
|
72
|
+
<h2 class="scroll__h" id="cb-blockers-h">Blockers waiting on you <span class="cb-block__n" id="cb-blockers-n"></span></h2>
|
|
73
|
+
</div>
|
|
74
|
+
<ul class="ov-rows" id="cb-blockers"></ul>
|
|
75
|
+
</section>
|
|
76
|
+
|
|
66
77
|
<section class="scroll cb-block" aria-labelledby="cb-mine-h">
|
|
67
78
|
<div class="cb-block__head">
|
|
68
79
|
<h2 class="scroll__h" id="cb-mine-h">Your asks <span class="cb-block__n" id="cb-mine-n"></span></h2>
|
|
@@ -155,5 +166,6 @@
|
|
|
155
166
|
<script src="/builders/palette.js" defer></script>
|
|
156
167
|
<script src="/builders/collab-lib.js?v=2026-09-24-archive"></script>
|
|
157
168
|
<script src="/builders/collab.js?v=2026-09-24-archive"></script>
|
|
169
|
+
<script src="/builders/blockers-by-person.js"></script>
|
|
158
170
|
</body>
|
|
159
171
|
</html>
|
|
@@ -750,7 +750,10 @@
|
|
|
750
750
|
// one) opens it on arrival, in any mode.
|
|
751
751
|
var note = /[?&]note=([1-9]\d{0,17})(?:&|$)/.exec((window.location && window.location.search) || '');
|
|
752
752
|
var notes = toolFor('palette');
|
|
753
|
-
|
|
753
|
+
// The Studio reads the doc as a full page of its own (studio.js), so the
|
|
754
|
+
// side sheet stays shut there.
|
|
755
|
+
var onStudio = document.body && document.body.getAttribute('data-page') === 'studio';
|
|
756
|
+
if (note && notes && !onStudio) loadTool(notes).then(function (api) { api.openNote(note[1]); }, function () { /* the page still works */ });
|
|
754
757
|
}
|
|
755
758
|
// Once a signed-in builder is known, ask — unless this session already has a
|
|
756
759
|
// mode, already saw the picker, or the page opts out.
|
|
@@ -62,7 +62,10 @@
|
|
|
62
62
|
const pill = (s === 'open' || s === 'legacy_satisfied') ? ''
|
|
63
63
|
: `<span class="crit-state crit-state--${esc(s)}">${esc(STATE_LABELS[s])}</span>`;
|
|
64
64
|
const tag = c && c.backend_only ? '<span class="crit-tag" title="No screen to record: this is signed off without a video">Backend-only</span>' : '';
|
|
65
|
-
|
|
65
|
+
// task 1004922: a criterion waiting on a person to try it is a HUMAN TASK.
|
|
66
|
+
const ask = typeof window !== 'undefined' && window.OTBAsk;
|
|
67
|
+
const human = s === 'awaiting_uat' && ask ? ask.human('task') : '';
|
|
68
|
+
return human + pill + tag;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
// The trailing action. canReview = the viewer may sign off (Metic+; the server
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Drachmae & karma — {{worldName}}</title>
|
|
7
|
-
<meta name="description" content="How
|
|
7
|
+
<meta name="description" content="How your work is counted and how you earn trust: the cost-plus {{currency}} reward, the kind multipliers, the monthly budget cap, and how karma differs from {{currency}}.">
|
|
8
8
|
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>◈</text></svg>">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -522,7 +522,7 @@
|
|
|
522
522
|
});
|
|
523
523
|
// Was a link only; now it answers in place: the overlay is where it resolves.
|
|
524
524
|
return window.OTBAsk.html({
|
|
525
|
-
cls: 'ginbox__item',
|
|
525
|
+
cls: 'ginbox__item', human: lib.humanKind(b),
|
|
526
526
|
where: 'Waiting on the project owner',
|
|
527
527
|
q: b.title || `Blocker ${b.id}`, qHref: `#/blocker/${encodeURIComponent(b.id)}`,
|
|
528
528
|
saidHtml: facts.factsHtml + facts.noteHtml,
|
|
@@ -336,6 +336,8 @@
|
|
|
336
336
|
const kindTag = window.OTB.kindIsUnset(t.kind)
|
|
337
337
|
? ''
|
|
338
338
|
: `<span class="task__tag">${escapeHtml(t.kind)}</span>`;
|
|
339
|
+
// task 1004950 — what the task needs beyond the build loop, said in words.
|
|
340
|
+
const needsTags = (Array.isArray(t.needs) ? t.needs : []).map((n) => `<span class="task__tag task__tag--needs">${n === 'owner' ? 'needs the project owner' : n === 'server' ? 'needs server access' : escapeHtml(n)}</span>`).join('');
|
|
339
341
|
const newcomerTag = t.newcomer_friendly ? '<span class="task__tag task__tag--newcomer">newcomer</span>' : '';
|
|
340
342
|
const rankTag = rankTagHtml(t.requires_rank);
|
|
341
343
|
const touches = Array.isArray(t.touches) ? t.touches : [];
|
|
@@ -387,6 +389,7 @@
|
|
|
387
389
|
${kindTag}
|
|
388
390
|
${disciplineTag}
|
|
389
391
|
${newcomerTag}
|
|
392
|
+
${needsTags}
|
|
390
393
|
${rankTag}
|
|
391
394
|
${t.priority ? `<span class="task__tag">${escapeHtml(t.priority)}</span>` : ''}
|
|
392
395
|
<span>${minutesText}</span>
|
|
@@ -924,6 +927,7 @@
|
|
|
924
927
|
// task 1953 — my own pending request (any viewer) + the goal's pending queue
|
|
925
928
|
// (managers only); async, re-renders Actions + the pending panel on arrival.
|
|
926
929
|
loadGoalMembership(g.id, canManage);
|
|
930
|
+
if (window.OTBNotes) window.OTBNotes.mount($('#goal-notes'), { goal_id: g.id }); // task 1004745
|
|
927
931
|
void sat; void crit; // now read inside renderCompletion()
|
|
928
932
|
}
|
|
929
933
|
function renderActions(g, { joined, canManage }) {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
styles now live in style.css's hall-kit section. -->
|
|
18
18
|
<link rel="stylesheet" href="/builders/hall-ask.css">
|
|
19
19
|
<link rel="stylesheet" href="/builders/goals.css">
|
|
20
|
+
<link rel="stylesheet" href="/builders/notes-panel.css">
|
|
20
21
|
</head>
|
|
21
22
|
<body data-page="goals" data-page-title="Goals">
|
|
22
23
|
|
|
@@ -130,6 +131,12 @@
|
|
|
130
131
|
</div>
|
|
131
132
|
</div>
|
|
132
133
|
|
|
134
|
+
<!-- task 1004745: notes on the goal, grouped by purpose (notes-panel.js). -->
|
|
135
|
+
<div class="scroll goal-notes">
|
|
136
|
+
<h2 class="scroll__h">Notes</h2>
|
|
137
|
+
<div id="goal-notes"></div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
133
140
|
<div class="scroll" style="margin-top:20px">
|
|
134
141
|
<div class="goal-work__head">
|
|
135
142
|
<h2 class="scroll__h">Tasks</h2>
|
|
@@ -271,6 +278,7 @@
|
|
|
271
278
|
<script src="/builders/goal-author-lib.js"></script>
|
|
272
279
|
<script src="/builders/goal-author.js"></script>
|
|
273
280
|
<script src="/builders/hall-ask.js"></script>
|
|
281
|
+
<script src="/builders/notes-panel.js"></script>
|
|
274
282
|
<script src="/builders/goals-render.js?v=2026-09-30-uat2"></script>
|
|
275
283
|
<script src="/builders/goals-page.js?v=2026-09-30-uat2"></script>
|
|
276
284
|
</body>
|
|
@@ -344,8 +344,12 @@
|
|
|
344
344
|
const format = askFormat(human);
|
|
345
345
|
// the server's route (task 1004705), when the docket was routed
|
|
346
346
|
const served = ctx.routed ? obj(base.route) : null;
|
|
347
|
+
// task 1004999: the server names who sits in the seat now, so a card can say
|
|
348
|
+
// "waits on Lars, the owner" and not only "waits on the owner".
|
|
349
|
+
const servedPeople = served ? arr(served.people).filter((p) => p && p.id != null)
|
|
350
|
+
.map((p) => ({ id: str(p.id), login: str(p.login), name: str(p.name || p.login) })) : [];
|
|
347
351
|
const route = served
|
|
348
|
-
? { seat: str(served.seat), label: str(served.label), person: null, fell_to_owner: !!served.fell_to_owner, members:
|
|
352
|
+
? { seat: str(served.seat), label: str(served.label), person: servedPeople[0] || null, people: servedPeople, fell_to_owner: !!served.fell_to_owner, members: servedPeople.map((p) => p.id) }
|
|
349
353
|
: ctx.resolveSeat(human && human.waits_on, base.building);
|
|
350
354
|
const nobody = served && obj(served.nobody) ? { why: str(served.nobody.why), outcome: served.nobody.outcome == null ? null : str(served.nobody.outcome) } : null;
|
|
351
355
|
// an ask nobody answers has done what its "if nobody answers" line says
|
|
@@ -375,7 +379,7 @@
|
|
|
375
379
|
human: human || null,
|
|
376
380
|
format,
|
|
377
381
|
shown: format.ok ? human : null,
|
|
378
|
-
waits_on: { seat: route.seat, label: route.label, person: route.person || null, fell_to_owner: !!route.fell_to_owner, real: !!served },
|
|
382
|
+
waits_on: { seat: route.seat, label: route.label, person: route.person || null, people: route.people || (route.person ? [route.person] : []), fell_to_owner: !!route.fell_to_owner, real: !!served },
|
|
379
383
|
needs_you: needsYou,
|
|
380
384
|
nobody,
|
|
381
385
|
// the card's words are the agent's own stored ask (task 1004703)
|
|
@@ -33,6 +33,11 @@ html[data-theme="dark"] body {
|
|
|
33
33
|
.hask:last-child { margin-bottom: 0; }
|
|
34
34
|
.hask__where { display: flex; align-items: center; gap: 8px; min-height: 24px; font-size: 13px; color: var(--ink-soft); min-width: 0; }
|
|
35
35
|
.hask__dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
|
|
36
|
+
/* the human signal (task 1004922): a person on the hall's "needs you" tint,
|
|
37
|
+
HUMAN TASK or HUMAN DECISION; also drawn bare beside an Awaiting UAT row */
|
|
38
|
+
.hhuman { flex: none; display: inline-flex; align-items: center; gap: 7px; min-height: 28px; padding: 3px 12px 3px 8px; box-sizing: border-box; border-radius: 999px; background: var(--accent-tint); color: var(--accent-ink); font-family: var(--font-mono); font-size: 12.5px; font-weight: 700; letter-spacing: 0.07em; line-height: 1; white-space: nowrap; vertical-align: middle; }
|
|
39
|
+
.hhuman__i { flex: none; width: 20px; height: 20px; }
|
|
40
|
+
.hask__human { display: flex; align-items: center; min-height: 24px; }
|
|
36
41
|
.hask__where-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
37
42
|
.hask__q { margin: 0; font-size: 21px; font-weight: 300; letter-spacing: -0.02em; line-height: 1.2; overflow-wrap: anywhere; }
|
|
38
43
|
.hask__q a { color: inherit; text-decoration: none; }
|
|
@@ -10,11 +10,20 @@
|
|
|
10
10
|
// path: switching a row to the card changes how it looks, never what it calls.
|
|
11
11
|
// An option with `href` is a link (an act that needs its own panel).
|
|
12
12
|
//
|
|
13
|
-
// OTBAsk.html({ where, q, qHref, said, saidHtml, tag, cls, attrs,
|
|
13
|
+
// OTBAsk.html({ where, q, qHref, said, saidHtml, tag, cls, attrs, human,
|
|
14
14
|
// row: bool, opts: [{ label, result, rec, href, attrs }] })
|
|
15
15
|
//
|
|
16
16
|
// Every string is escaped here; `saidHtml` is the one pre-built slot, for a
|
|
17
17
|
// caller whose facts are already escaped markup (blockerFacts).
|
|
18
|
+
//
|
|
19
|
+
// THE HUMAN SIGNAL (task 1004922, owner 2026-10-28). Everything that waits on
|
|
20
|
+
// a person says so with a person: HUMAN DECISION (a person at a fork) when
|
|
21
|
+
// they must choose, HUMAN TASK (a person with a hand up) when they must do
|
|
22
|
+
// something only a person can do. Every card is a decision unless the caller
|
|
23
|
+
// passes `human: 'task'` (a blocker's owner fix); `human: false` drops it.
|
|
24
|
+
// `OTBAsk.human(kind)` is the same badge for a surface that is not a card
|
|
25
|
+
// (the Awaiting UAT row). The chat half of the rule — `🙋 **HUMAN TASK:**` /
|
|
26
|
+
// `🔀 **HUMAN DECISION:**` — lives in docs/voice.md and the Conductor.
|
|
18
27
|
(function () {
|
|
19
28
|
// The shared escaper: window.OTB in the page; under Node (this file is also require()d as a
|
|
20
29
|
// CommonJS lib — see its module.exports and its tests) dom-utils.js's own export (task 1004876).
|
|
@@ -32,19 +41,46 @@
|
|
|
32
41
|
: `<button class="${cls}" type="button"${attrsOf(o.attrs)}>${inner}</button>`;
|
|
33
42
|
}
|
|
34
43
|
|
|
44
|
+
// 18px grid, 1.5 stroke, currentColor — the rail's glyph recipe.
|
|
45
|
+
const HUMAN = {
|
|
46
|
+
task: {
|
|
47
|
+
label: 'HUMAN TASK',
|
|
48
|
+
title: 'A person needs to do this',
|
|
49
|
+
svg: '<circle cx="8" cy="5" r="2.2"/><path d="M3.5 16v-2.5a4.5 4.5 0 0 1 9 0V16"/><path d="M12.5 9.5 14.5 3"/>',
|
|
50
|
+
},
|
|
51
|
+
decision: {
|
|
52
|
+
label: 'HUMAN DECISION',
|
|
53
|
+
title: 'A person needs to decide this',
|
|
54
|
+
svg: '<circle cx="7" cy="6" r="2.2"/><path d="M2.5 16v-2a4.5 4.5 0 0 1 9 0v2"/><path d="M13 2.5v3M13 5.5l-2 2.2M13 5.5l2 2.2"/>',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function human(kind) {
|
|
59
|
+
const h = HUMAN[kind];
|
|
60
|
+
if (!h) return '';
|
|
61
|
+
return `<span class="hhuman hhuman--${kind}" title="${h.title}">`
|
|
62
|
+
+ `<svg class="hhuman__i" viewBox="0 0 18 18" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${h.svg}</svg>`
|
|
63
|
+
+ `<span class="hhuman__t">${h.label}</span></span>`;
|
|
64
|
+
}
|
|
65
|
+
|
|
35
66
|
function html(c) {
|
|
36
67
|
const tag = c.tag || 'li';
|
|
37
68
|
const opts = (c.opts || []).filter(Boolean);
|
|
38
69
|
const q = c.qHref ? `<a href="${esc(c.qHref)}">${esc(c.q)}</a>` : esc(c.q);
|
|
70
|
+
const badge = human(c.human === undefined ? 'decision' : c.human);
|
|
71
|
+
const where = c.where ? `<span class="hask__where-t">${esc(c.where)}</span>` : '';
|
|
39
72
|
return `<${tag} class="hask${c.cls ? ' ' + esc(c.cls) : ''}"${attrsOf(c.attrs)}>`
|
|
40
|
-
|
|
73
|
+
// With a where line the badge leads it; without one (the Blockers page
|
|
74
|
+
// dropped its repeated eyebrow) the badge stands on a line of its own.
|
|
75
|
+
+ (where ? `<div class="hask__where">${badge || '<span class="hask__dot" aria-hidden="true"></span>'}${where}</div>`
|
|
76
|
+
: (badge ? `<div class="hask__human">${badge}</div>` : ''))
|
|
41
77
|
+ `<h4 class="hask__q">${q}</h4>`
|
|
42
78
|
+ (c.saidHtml ? `<div class="hask__said">${c.saidHtml}</div>` : (c.said ? `<p class="hask__said">${esc(c.said)}</p>` : ''))
|
|
43
79
|
+ (opts.length ? `<div class="hask__opts${c.row ? ' hask__opts--row' : ''}">${opts.map(optHtml).join('')}</div>` : '')
|
|
44
80
|
+ `</${tag}>`;
|
|
45
81
|
}
|
|
46
82
|
|
|
47
|
-
const api = { html, esc };
|
|
83
|
+
const api = { html, esc, human };
|
|
48
84
|
if (typeof window !== 'undefined') window.OTBAsk = api;
|
|
49
85
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
50
86
|
})();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* modules/hall-ui/public/notes-panel.css — the Notes panel on task and goal pages
|
|
2
|
+
(task 1004745). Notes sit in groups by purpose; each group is a small block with
|
|
3
|
+
its newest few notes and a fold for the rest. Uses the hall's tokens only. */
|
|
4
|
+
|
|
5
|
+
.np-group { margin: var(--sp-md) 0 0; }
|
|
6
|
+
.np-group__h { font-size: 14px; margin: 0 0 var(--sp-xs); display: flex; align-items: baseline; gap: var(--sp-sm); flex-wrap: wrap; }
|
|
7
|
+
.np-count { font-size: 12px; color: var(--ink-soft); }
|
|
8
|
+
.np-group__hint { font-size: 12px; font-weight: normal; color: var(--ink-soft); }
|
|
9
|
+
.np-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-sm); }
|
|
10
|
+
.np-note { border: var(--line) solid var(--border); border-radius: 8px; padding: var(--sp-sm) var(--sp-md); background: var(--surface); }
|
|
11
|
+
.np-note__body { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
12
|
+
.np-note__meta { margin: var(--sp-xs) 0 0; font-size: 12px; color: var(--ink-soft); display: flex; gap: var(--sp-md); flex-wrap: wrap; align-items: center; }
|
|
13
|
+
.np-move select { font-size: 12px; }
|
|
14
|
+
.np-link { background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; font-size: 12px; text-decoration: underline; }
|
|
15
|
+
.np-empty { color: var(--ink-soft); }
|
|
16
|
+
.np-form { margin: var(--sp-lg) 0 0; display: grid; gap: var(--sp-sm); }
|
|
17
|
+
.np-field { display: grid; gap: var(--sp-xs); font-size: 13px; }
|
|
18
|
+
.np-field textarea { width: 100%; box-sizing: border-box; font: inherit; }
|
|
19
|
+
.np-actions { display: flex; gap: var(--sp-md); align-items: end; flex-wrap: wrap; }
|
|
20
|
+
.np-field--inline { display: flex; align-items: center; gap: var(--sp-sm); }
|