@bongos/core 1.24.79 → 1.24.81
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 +554 -394
- package/.claude/skills/ask-for-help/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/design/SKILL.md +1 -1
- package/.claude/skills/goal-create/SKILL.md +1 -1
- package/.claude/skills/planning-session/SKILL.md +1 -1
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +11 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/adr/0274-one-kernel-three-role-packs.md +2 -2
- package/docs/adr/0399-the-visor-is-glass-over-the-scene-and-owns-no-camera.md +91 -0
- package/docs/adr/0400-a-chain-reserves-work-and-each-sub-quest-is-still-claimed-alone.md +109 -0
- package/docs/adr/0401-one-options-menu-opens-every-settings-screen-in-a-frame.md +53 -0
- package/docs/adr/0402-helm-is-the-halls-look-and-the-hall-is-dark-only.md +142 -0
- package/docs/adr/README.md +4 -0
- package/docs/api/openapi.json +195 -3
- package/docs/api-reference.md +8 -4
- package/docs/architecture.md +7 -3
- package/docs/copy-inventory.md +393 -378
- package/docs/copy-registry.json +722 -578
- package/docs/design/characters/character-sprites-source.webp +0 -0
- package/docs/file-map.md +7 -8
- package/docs/module-api-changelog.md +4 -0
- package/docs/modules-contract.md +5 -0
- package/docs/onboarding/slash-commands.md +1 -1
- package/docs/packs/{engineer.md → artisan.md} +5 -5
- package/docs/packs/artist.md +50 -4
- package/docs/page-inventory.json +16 -7
- package/docs/page-readings.json +3184 -3338
- package/docs/{packs/ideator.md → recipes/ideation-playbook.md} +5 -48
- package/migrations/core_296_crafts_artisan_artist.sql +72 -0
- package/migrations/core_297_builder_character.sql +30 -0
- package/modules/copy-desk/proposals.js +1 -1
- package/modules/economy/credits-by-craft.js +29 -15
- package/modules/hall-ui/public/ai-studio.html +6 -4
- package/modules/hall-ui/public/ai-studio.states.json +1 -0
- package/modules/hall-ui/public/atlas.html +5 -5
- package/modules/hall-ui/public/atlas.js +3 -1
- package/modules/hall-ui/public/atlas.states.json +1 -0
- package/modules/hall-ui/public/blockers.html +5 -5
- package/modules/hall-ui/public/board-room.html +4 -4
- package/modules/hall-ui/public/board-room.states.json +1 -0
- package/modules/hall-ui/public/characters/artisan.png +0 -0
- package/modules/hall-ui/public/characters/artist.png +0 -0
- package/modules/hall-ui/public/characters/patron.png +0 -0
- package/modules/hall-ui/public/city.css +3 -4
- package/modules/hall-ui/public/city.html +9 -9
- package/modules/hall-ui/public/city.js +17 -56
- package/modules/hall-ui/public/city.states.json +7 -32
- package/modules/hall-ui/public/collab.html +4 -4
- package/modules/hall-ui/public/collab.states.json +1 -0
- package/modules/hall-ui/public/commands.html +4 -4
- package/modules/hall-ui/public/commands.states.json +1 -0
- package/modules/hall-ui/public/controls.css +63 -11
- package/modules/hall-ui/public/copy-desk.html +4 -4
- package/modules/hall-ui/public/copy-desk.states.json +1 -0
- package/modules/hall-ui/public/craft-mode.js +258 -64
- package/modules/hall-ui/public/deploy.html +4 -4
- package/modules/hall-ui/public/deploy.states.json +1 -0
- package/modules/hall-ui/public/diagram-viewer.js +2 -1
- package/modules/hall-ui/public/dom-utils.js +1 -1
- package/modules/hall-ui/public/goal-map.html +9 -6
- package/modules/hall-ui/public/goals.html +5 -5
- package/modules/hall-ui/public/goals.states.json +1 -0
- package/modules/hall-ui/public/government.html +4 -4
- package/modules/hall-ui/public/government.states.json +1 -0
- package/modules/hall-ui/public/hall-render.js +3 -1
- package/modules/hall-ui/public/home-city-draw.js +3 -3
- package/modules/hall-ui/public/home-city.css +4 -0
- package/modules/hall-ui/public/home-city.js +17 -9
- package/modules/hall-ui/public/home-gallery.js +8 -1
- package/modules/hall-ui/public/home-idea-box.css +2 -2
- package/modules/hall-ui/public/home-idea-box.js +3 -3
- package/modules/hall-ui/public/idea.html +5 -5
- package/modules/hall-ui/public/idea.states.json +1 -0
- package/modules/hall-ui/public/ideas.html +5 -5
- package/modules/hall-ui/public/ideas.states.json +1 -0
- package/modules/hall-ui/public/index-founding.states.json +1 -0
- package/modules/hall-ui/public/index-genesis-decide.states.json +1 -0
- package/modules/hall-ui/public/index-genesis-demo.states.json +1 -0
- package/modules/hall-ui/public/index-genesis.states.json +1 -0
- package/modules/hall-ui/public/index.html +11 -7
- package/modules/hall-ui/public/index.states.json +1 -0
- package/modules/hall-ui/public/market-watch-item.html +4 -4
- package/modules/hall-ui/public/market-watch-item.states.json +1 -0
- package/modules/hall-ui/public/market-watch-rules.html +4 -4
- package/modules/hall-ui/public/market-watch-rules.states.json +1 -0
- package/modules/hall-ui/public/market-watch.html +6 -9
- package/modules/hall-ui/public/market-watch.js +22 -45
- package/modules/hall-ui/public/market-watch.states.json +1 -0
- package/modules/hall-ui/public/modules.html +4 -4
- package/modules/hall-ui/public/modules.states.json +1 -0
- package/modules/hall-ui/public/money.html +4 -4
- package/modules/hall-ui/public/money.states.json +1 -0
- package/modules/hall-ui/public/my-agents.html +4 -4
- package/modules/hall-ui/public/my-agents.states.json +1 -0
- package/modules/hall-ui/public/options-menu.js +409 -0
- package/modules/hall-ui/public/paintbrush.js +3 -3
- package/modules/hall-ui/public/palette.js +11 -13
- package/modules/hall-ui/public/primer.html +5 -5
- package/modules/hall-ui/public/primer.states.json +1 -0
- package/modules/hall-ui/public/profile-crafts.js +1 -1
- package/modules/hall-ui/public/profile-roles.js +23 -19
- package/modules/hall-ui/public/profile.css +1 -1
- package/modules/hall-ui/public/profile.html +19 -18
- package/modules/hall-ui/public/profile.js +19 -18
- package/modules/hall-ui/public/profile.states.json +5 -4
- package/modules/hall-ui/public/project-settings.html +4 -4
- package/modules/hall-ui/public/roster.html +4 -4
- package/modules/hall-ui/public/roster.js +3 -3
- package/modules/hall-ui/public/roster.states.json +1 -0
- package/modules/hall-ui/public/settings.css +24 -4
- package/modules/hall-ui/public/settings.html +21 -17
- package/modules/hall-ui/public/settings.js +44 -44
- package/modules/hall-ui/public/settings.states.json +1 -0
- package/modules/hall-ui/public/shell-game.css +110 -0
- package/modules/hall-ui/public/shell-game.js +316 -3
- package/modules/hall-ui/public/shell.js +15 -66
- package/modules/hall-ui/public/sky-interaction.js +7 -0
- package/modules/hall-ui/public/speciality-tools.js +1 -2
- package/modules/hall-ui/public/street-card.states.json +1 -0
- package/modules/hall-ui/public/street-inside.js +3 -4
- package/modules/hall-ui/public/street.css +1 -5
- package/modules/hall-ui/public/street.html +6 -10
- package/modules/hall-ui/public/street.js +8 -59
- package/modules/hall-ui/public/street.states.json +7 -32
- package/modules/hall-ui/public/studio.html +5 -5
- package/modules/hall-ui/public/studio.states.json +1 -0
- package/modules/hall-ui/public/style.css +197 -112
- package/modules/hall-ui/public/task-detail.js +5 -4
- package/modules/hall-ui/public/task.html +5 -5
- package/modules/hall-ui/public/task.states.json +1 -0
- package/modules/hall-ui/public/thinking.css +10 -0
- package/modules/hall-ui/public/thinking.html +9 -6
- package/modules/hall-ui/public/thinking.states.json +1 -0
- package/modules/hall-ui/public/tweak-editor.html +5 -5
- package/modules/hall-ui/public/tweak-editor.js +5 -1
- package/modules/hall-ui/public/tweak-editor.states.json +1 -0
- package/modules/hall-ui/public/update-pill.js +91 -2
- package/modules/hall-ui/public/visor.css +80 -0
- package/modules/hall-ui/public/visor.js +316 -0
- package/modules/hall-ui/public/watch-repo-invite.js +159 -0
- package/modules/hall-ui/public/watch.html +6 -5
- package/modules/hall-ui/public/watch.js +17 -2
- package/modules/hall-ui/public/watch.states.json +1 -0
- package/modules/hall-ui/public/welcome-boot.js +15 -6
- package/modules/hall-ui/public/work-founding.states.json +1 -0
- package/modules/hall-ui/public/work.html +5 -5
- package/modules/hall-ui/public/work.states.json +1 -0
- package/modules/hall-ui/records/builders-roster.md +2 -2
- package/modules/hall-ui/records/craft-mode.md +13 -11
- package/modules/hall-ui/records/game-mode.md +2 -2
- package/modules/hall-ui/records/governor-street.md +1 -1
- package/modules/hall-ui/records/home-city.md +3 -1
- package/modules/hall-ui/records/market-watch-settings.md +2 -0
- package/modules/hall-ui/records/options-menu.md +15 -0
- package/modules/hall-ui/records/profile-roles.md +11 -11
- package/modules/hall-ui/records/sky.md +3 -3
- package/modules/hall-ui/records/start-screen.md +25 -0
- package/modules/hall-ui/records/tweak-editor.md +1 -1
- package/modules/hall-ui/records/update-pill.md +12 -0
- package/modules/hall-ui/records/visor.md +23 -0
- package/modules/hall-ui/records/welcome-tower.md +5 -5
- package/modules/hall-ui/specimens/components.states.json +2 -1
- package/modules/ideas/ideator-stats.js +3 -1
- package/modules/ideas/inbox.js +2 -2
- package/modules/lifecycle/activity.js +1 -1
- package/modules/lifecycle/cascade.js +4 -4
- package/modules/lifecycle/goal-spec-task.js +1 -1
- package/modules/lifecycle/help-requests.js +19 -15
- package/modules/lifecycle/kickoff-checklist.js +3 -3
- package/modules/lifecycle/kickoff-seed.js +1 -1
- package/modules/lifecycle/migrations/lifecycle_021_crafts_artisan_artist.sql +29 -0
- package/modules/lifecycle/mingle.js +4 -4
- package/modules/lifecycle/page-tweak-write.js +1 -1
- package/modules/lifecycle/routes/task-write-routes.js +3 -1
- package/modules/lifecycle/routes/tasks.js +3 -3
- package/modules/lifecycle/ship-card.js +2 -3
- package/modules/lifecycle/task-classifier.js +39 -29
- package/modules/lifecycle/tests/cascade.mjs +7 -7
- package/modules/onboarding/founding-mode.js +3 -0
- package/modules/onboarding/genesis-home.js +21 -1
- package/modules/onboarding/routes/onboarding.js +2 -0
- package/modules/page-notes/notes.js +3 -2
- package/modules/page-notes/split.js +1 -1
- package/modules/platform-identity/craft-rollup.js +35 -7
- package/modules/platform-identity/migrations/platform_identity_036_crafts_artisan_artist.sql +45 -0
- package/modules/platform-identity/platform-identity.js +2 -2
- package/modules/platform-identity/routes/community-leaderboard.js +2 -1
- package/modules/platform-identity/routes/community-search.js +2 -1
- package/modules/platform-identity/routes/sso.js +2 -1
- package/modules/platform-identity/tests/platform-identity.mjs +6 -6
- package/modules/public-landing/public/community.html +3 -5
- package/modules/public-landing/public/index.html +1 -1
- package/modules/public-landing/public/projects.html +1 -1
- package/modules/specialities/migrations/specialities_004_crafts_artisan_artist.sql +30 -0
- package/modules/specialities/routes/specialities.js +2 -2
- package/modules/status-ui/public/status.js +1 -1
- package/modules/ui-design/kit/fixtures/community-leaderboard.json +10 -10
- package/modules/ui-design/kit/fixtures/community-search.json +2 -2
- package/modules/ui-design/kit/fixtures/scouting-recruiter.json +1 -1
- package/modules/ui-design/public/brand.html +2 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +76 -0
- package/scripts/bongos/autobongos-loop.js +1 -1
- package/scripts/bongos/discipline-modes.json +7 -12
- package/scripts/bongos/fitness.js +1 -1
- package/scripts/bongos/gen-hall-sitemap.js +7 -7
- package/scripts/bongos/grade-smoke.js +2 -2
- package/scripts/bongos/init.js +2 -2
- package/scripts/bongos/newcomer-floor.config.json +1 -2
- package/scripts/bongos/newcomer-floor.js +4 -3
- package/scripts/bongos/newcomer-floor.templates.json +10 -12
- package/scripts/bongos/newcomer-floor.test.js +47 -38
- package/scripts/bongos/role-pack-guard.js +5 -4
- package/scripts/bongos/seed-tasks.example.json +3 -3
- package/scripts/bongos/setup.js +20 -107
- package/scripts/bongos/start.js +2 -2
- package/scripts/bongos/task.js +5 -4
- package/scripts/bongos/triage.js +1 -1
- package/scripts/bongos/worktree.js +16 -4
- package/scripts/hall-preview/README.md +2 -3
- package/scripts/setup-builder.js +6 -46
- package/src/bongos/auth-admission.js +8 -8
- package/src/bongos/craft-mode.js +2 -0
- package/src/bongos/db.js +39 -1
- package/src/bongos/github-repos.js +78 -0
- package/src/bongos/module-scope-map.js +22 -0
- package/src/bongos/role-stats.js +27 -11
- package/src/bongos/routes/github.js +100 -2
- package/src/bongos/routes/me.js +33 -0
- package/src/bongos/serve-internal.js +33 -11
- package/src/crafts.js +66 -0
- package/src/hall-helm.js +79 -0
- package/src/module-api.js +6 -1
- package/src/module-loader/manifest-schema.js +16 -0
- package/src/modules.js +2 -2
- package/src/role-responsibilities.js +6 -5
- package/tests/acclaim_feed_evidence_db.mjs +1 -1
- package/tests/activity_rollup_as_of.mjs +1 -1
- package/tests/api_alias_caller_ratchet.mjs +3 -3
- package/tests/application_lifecycle.mjs +1 -1
- package/tests/artist_review.mjs +1 -1
- package/tests/artist_review_goal.mjs +1 -1
- package/tests/blocker_hall_live_proof.mjs +1 -1
- package/tests/blocker_tasks_named_db.mjs +2 -2
- package/tests/canonical_profile_url.mjs +1 -1
- package/tests/cap_newcomer_tasks.mjs +13 -13
- package/tests/character_creation.mjs +294 -0
- package/tests/claim_gate_open_blocker.mjs +2 -2
- package/tests/claim_gate_rebase.mjs +6 -6
- package/tests/claude_materialize.mjs +1 -1
- package/tests/clawback_confirmed_backlog.mjs +3 -3
- package/tests/collab_page.mjs +8 -9
- package/tests/collab_self_settling.mjs +2 -2
- package/tests/community_leaderboard_route.mjs +13 -5
- package/tests/community_proof_db.mjs +3 -0
- package/tests/community_search_route.mjs +7 -6
- package/tests/conductor.mjs +1 -1
- package/tests/conductor_main_e2e.mjs +8 -8
- package/tests/copy_desk_flags_db.mjs +1 -1
- package/tests/copy_desk_page_approve.mjs +2 -2
- package/tests/copy_desk_page_claim.mjs +2 -2
- package/tests/copy_desk_page_next.mjs +1 -1
- package/tests/copy_desk_page_release.mjs +1 -1
- package/tests/copy_desk_page_submit.mjs +2 -2
- package/tests/core_258_box_session_claims_db.mjs +1 -1
- package/tests/craft_mode.mjs +48 -34
- package/tests/craft_mode_home.mjs +20 -20
- package/tests/craft_mode_looks.mjs +16 -15
- package/tests/craft_mode_nav.mjs +31 -29
- package/tests/craft_review_mode.mjs +46 -41
- package/tests/crafts_vocabulary.mjs +70 -0
- package/tests/cross_discipline_priority_direction.mjs +5 -5
- package/tests/design_reference.mjs +1 -1
- package/tests/fitness.mjs +7 -11
- package/tests/funded_project_pays_proof_db.mjs +1 -1
- package/tests/genesis_home.mjs +21 -1
- package/tests/github_chip.mjs +3 -3
- package/tests/github_repo_invite.mjs +379 -0
- package/tests/goal_criterion_confirm_ui.mjs +1 -1
- package/tests/goal_invite_consent.mjs +1 -1
- package/tests/goal_map_page.mjs +2 -2
- package/tests/goal_sort_staleness.mjs +1 -1
- package/tests/goal_spec_task.mjs +1 -1
- package/tests/goal_spec_task_db.mjs +3 -3
- package/tests/grade_engine.mjs +2 -2
- package/tests/grader_no_artifact.mjs +1 -1
- package/tests/grader_prompt_fencing.mjs +1 -1
- package/tests/grader_prompt_window_bound.mjs +1 -1
- package/tests/grader_reader_lens.mjs +2 -2
- package/tests/grader_severity_gate.mjs +1 -1
- package/tests/grader_unbacked_score.mjs +1 -1
- package/tests/hall_approval_queue.mjs +1 -1
- package/tests/hall_board_world.mjs +2 -2
- package/tests/hall_builders_page_pass.mjs +3 -2
- package/tests/hall_city.mjs +16 -30
- package/tests/hall_controls.mjs +52 -4
- package/tests/hall_fullscreen.mjs +27 -3
- package/tests/hall_helm.mjs +187 -0
- package/tests/hall_kit_charts.mjs +1 -1
- package/tests/hall_landing_boot.mjs +1 -1
- package/tests/hall_my_agents.mjs +16 -16
- package/tests/hall_nav.mjs +22 -15
- package/tests/hall_options_menu.mjs +247 -0
- package/tests/hall_oversight_world.mjs +1 -1
- package/tests/hall_palette.mjs +8 -9
- package/tests/hall_profile_world.mjs +3 -3
- package/tests/hall_record_world.mjs +6 -4
- package/tests/hall_settings_world.mjs +2 -1
- package/tests/hall_sitemap.mjs +6 -5
- package/tests/hall_start_screen.mjs +307 -0
- package/tests/hall_street.mjs +15 -28
- package/tests/hall_street_inside.mjs +17 -9
- package/tests/hall_studio_world.mjs +3 -3
- package/tests/hall_topbar_320.mjs +4 -2
- package/tests/hall_visor.mjs +224 -0
- package/tests/help_requests.mjs +47 -15
- package/tests/helpers.mjs +4 -4
- package/tests/home_city.mjs +6 -4
- package/tests/home_city_widget.mjs +33 -14
- package/tests/home_gallery_widget.mjs +4 -4
- package/tests/home_idea_box.mjs +6 -6
- package/tests/hub_community_page.mjs +3 -3
- package/tests/hub_craft_rollup.mjs +75 -36
- package/tests/hub_recent_ships.mjs +3 -2
- package/tests/idea_cascade.mjs +2 -2
- package/tests/idea_credit_streams_db.mjs +1 -1
- package/tests/idea_detail_page.mjs +1 -1
- package/tests/idea_ideate_full.mjs +5 -2
- package/tests/idea_spark_hall.mjs +1 -1
- package/tests/init.mjs +3 -3
- package/tests/instance_manifest.mjs +2 -2
- package/tests/kickoff_checklist.mjs +1 -1
- package/tests/leaderboard_roles.mjs +34 -25
- package/tests/lifecycle_claim_ladder_refs.mjs +55 -0
- package/tests/lightbulb.mjs +14 -10
- package/tests/main_discipline.mjs +8 -8
- package/tests/markdown_line_endings.mjs +1 -1
- package/tests/market_watch_pages.mjs +52 -74
- package/tests/me_streak_leaderboard_queries.mjs +3 -3
- package/tests/migration_allocation.mjs +1 -1
- package/tests/mingle.mjs +36 -21
- package/tests/module-scope-map.mjs +22 -2
- package/tests/module_api.mjs +1 -0
- package/tests/module_contributions.mjs +13 -13
- package/tests/module_manifest.mjs +29 -0
- package/tests/module_skills_hook.mjs +2 -2
- package/tests/modules.mjs +14 -7
- package/tests/nav_permission_atoms.mjs +2 -1
- package/tests/needs_rebase.mjs +1 -1
- package/tests/newcomer_restock.mjs +13 -13
- package/tests/page_notes_panel.mjs +22 -22
- package/tests/page_tweak_no_block.mjs +1 -1
- package/tests/paintbrush.mjs +7 -7
- package/tests/privacy_matrix.mjs +1 -0
- package/tests/profile_activity.mjs +20 -23
- package/tests/profile_earnings.mjs +1 -1
- package/tests/profile_nudge_prefs.mjs +4 -4
- package/tests/profile_role_headline.mjs +73 -46
- package/tests/profile_role_stats.mjs +55 -45
- package/tests/profile_rollup_consent.mjs +19 -6
- package/tests/profile_route.mjs +12 -12
- package/tests/profile_ui_cross_project.mjs +4 -4
- package/tests/project_invite_suggestions.mjs +2 -2
- package/tests/promote_goal_id.mjs +1 -1
- package/tests/publish_manifest.mjs +1 -1
- package/tests/rank_floor_wiring.mjs +1 -1
- package/tests/regrade_actual_minutes.mjs +1 -1
- package/tests/removal_proposal.mjs +2 -2
- package/tests/reward_gate.mjs +4 -4
- package/tests/reward_mode_cost_plus_only.mjs +1 -1
- package/tests/role_responsibilities.mjs +18 -12
- package/tests/role_stats_db.mjs +17 -16
- package/tests/scouting_rollup_fanout.mjs +5 -5
- package/tests/seed_tasks.mjs +5 -2
- package/tests/session_earnings_mirror_db.mjs +1 -1
- package/tests/session_reward_on_land_db.mjs +1 -1
- package/tests/session_reward_reconcile_db.mjs +2 -2
- package/tests/sessions_kit_pager.mjs +1 -1
- package/tests/settings_art_key_absent.mjs +2 -2
- package/tests/settings_main_role.mjs +21 -20
- package/tests/setup_flow.mjs +3 -3
- package/tests/setup_smoke.mjs +20 -0
- package/tests/ship_card.mjs +6 -6
- package/tests/specialities.mjs +16 -4
- package/tests/speciality_db_shape.mjs +6 -6
- package/tests/speciality_injection.mjs +17 -17
- package/tests/speciality_offer_ui.mjs +5 -5
- package/tests/speciality_offered.mjs +6 -5
- package/tests/speciality_session_skills.mjs +7 -7
- package/tests/speciality_skills.mjs +11 -11
- package/tests/speciality_walkthrough_ui.mjs +2 -2
- package/tests/sso_checkin_identity_spoof.mjs +2 -1
- package/tests/task_classifier_vocab.mjs +10 -0
- package/tests/task_cli.mjs +11 -3
- package/tests/task_criteria_unlink_db.mjs +2 -2
- package/tests/task_discipline_required.mjs +38 -16
- package/tests/task_goal_required.mjs +4 -4
- package/tests/task_needs_db.mjs +1 -1
- package/tests/tweak_replaces_artist_review.mjs +1 -1
- package/tests/update_pill.mjs +153 -2
- package/tests/upkeep_home.mjs +1 -1
- package/tests/v3_c1_options_proof.mjs +145 -0
- package/tests/visual_review.mjs +3 -3
- package/tests/watch_applications_queue.mjs +8 -5
- package/tests/watch_sealed_floor.mjs +3 -1
- package/tests/welcome_boot.mjs +12 -9
- package/tests/welcome_boot_browser.mjs +13 -13
- package/tests/worktree_helper.mjs +16 -1
package/src/bongos/routes/me.js
CHANGED
|
@@ -217,6 +217,12 @@ module.exports = function buildMeRouter() {
|
|
|
217
217
|
const openPayoutHoldsP = (async () => {
|
|
218
218
|
try { return await require('../payout-gates').openHoldsFor(req.builder.id); } catch (_) { return null; }
|
|
219
219
|
})();
|
|
220
|
+
// task 1005147: the character this builder holds, or null (not picked yet — the
|
|
221
|
+
// hall then shows character creation). Fail-open to null: a core that runs
|
|
222
|
+
// before core_297 has no column, and that must never break /me.
|
|
223
|
+
const characterP = (async () => {
|
|
224
|
+
try { return await db.getBuilderCharacter(req.builder.id); } catch (_) { return null; }
|
|
225
|
+
})();
|
|
220
226
|
const stuckPageTweaksP = (async () => {
|
|
221
227
|
try {
|
|
222
228
|
const port = seams.resolveOptional('copy-desk.stuckPageTweaksFor');
|
|
@@ -476,6 +482,9 @@ module.exports = function buildMeRouter() {
|
|
|
476
482
|
// when lifecycle is disabled or the read failed, and the shell treats null
|
|
477
483
|
// as OFF — a mode that cannot be read is not on.
|
|
478
484
|
social: socialMode,
|
|
485
|
+
// task 1005147: the builder's character ('artisan' | 'artist' | 'patron'),
|
|
486
|
+
// or null until they pick one. Identity and view only; rank still gates.
|
|
487
|
+
character: await characterP,
|
|
479
488
|
// task 1004422 (BV2.PS03, goal 1000121): PROJECT FOUNDING MODE. Genesis is a
|
|
480
489
|
// mode the EXISTING hall wears (the blend rule, docs/design/project-startup-
|
|
481
490
|
// direction.md), so it rides /me exactly like social: the shell stamps
|
|
@@ -724,6 +733,30 @@ module.exports = function buildMeRouter() {
|
|
|
724
733
|
}
|
|
725
734
|
});
|
|
726
735
|
|
|
736
|
+
// task 1005147 (BONGOS-V3 G2): character creation. Body: { character } —
|
|
737
|
+
// 'artisan' | 'artist' | 'patron', or null to clear it (the hall clears it and
|
|
738
|
+
// signs out: switching character is signing out and back in, spec settled point 2).
|
|
739
|
+
// Identity and view only: no claim, vote, deploy or spend gate reads it
|
|
740
|
+
// (criterion character-is-identity-not-authority). 422 invalid_character otherwise.
|
|
741
|
+
// rank: any-builder — a builder writes only their own row (req.builder.id), the
|
|
742
|
+
// /me/disciplines posture.
|
|
743
|
+
router.put('/me/character', auth.requireBuilder, async (req, res) => {
|
|
744
|
+
// Nullable by the validator's default: null is the deliberate clear.
|
|
745
|
+
if (validateOrRespond(req, res, { character: { type: 'string', maxLength: 32 } }, { strict: true })) return;
|
|
746
|
+
const body = req.body || {};
|
|
747
|
+
if (!Object.prototype.hasOwnProperty.call(body, 'character')) {
|
|
748
|
+
return res.fail('character_required', 400);
|
|
749
|
+
}
|
|
750
|
+
const raw = body.character;
|
|
751
|
+
try {
|
|
752
|
+
const character = await db.setBuilderCharacter(req.builder.id, raw);
|
|
753
|
+
res.json({ character });
|
|
754
|
+
} catch (err) {
|
|
755
|
+
if (err.code === 'invalid_character') return res.fail('invalid_character', 422);
|
|
756
|
+
throw err;
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
|
|
727
760
|
// V3.R10 (#235), criterion C3 — link the authed builder's Anthropic account.
|
|
728
761
|
// Body: { anthropic_email, anthropic_user_id }. Self-attestation: the builder's
|
|
729
762
|
// own Claude Code reads ~/.claude.json oauthAccount during /builder-setup,
|
|
@@ -45,6 +45,7 @@ const { buildInfo } = require('../build-info');
|
|
|
45
45
|
const { buildBongosDownloads, installScriptReader } = require('../bongos-downloads');
|
|
46
46
|
const { clientBranding, branding, applyBrandConditionals } = require('../branding');
|
|
47
47
|
const { themeMarkup } = require('../theme-markup');
|
|
48
|
+
const { applyHelm } = require('../hall-helm');
|
|
48
49
|
const { linkPreviewTags } = require('../link-preview');
|
|
49
50
|
const { prelaunchGate } = require('./prelaunch-gate');
|
|
50
51
|
const { platformVisibilityGate } = require('./platform-visibility-gate');
|
|
@@ -126,8 +127,12 @@ function injectBrandingGlobal(html, brand = clientBranding(), mods = clientModul
|
|
|
126
127
|
// §4), injected the same way as __BRANDING__ so hall/status scripts can hide
|
|
127
128
|
// UI sections for off modules synchronously (no fetch, no race). R60 wires the
|
|
128
129
|
// actual section-hiding; this exposes the data. The link-preview card (task 1005091) rides along.
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
// A HALL page (one that links the hall's sheet) is pinned dark and, unless its pack
|
|
131
|
+
// chose faces of its own, takes the Helm faces (task 1005192, ADR 0402, src/hall-helm.js).
|
|
132
|
+
// Every other page comes back from applyHelm untouched.
|
|
133
|
+
const helm = applyHelm(applyBrandTokens(html, brand), brand.theme);
|
|
134
|
+
const stamped = helm.html;
|
|
135
|
+
const tag = `${clientBundleScriptTag()}<script>window.__BRANDING__=${jsonForScript(brand)};window.__MODULES__=${jsonForScript(mods)};</script>${linkPreviewTags(stamped, brand, safePublicOrigin())}${themeMarkup(brand.theme)}${helm.tail}`;
|
|
131
136
|
return stamped.includes('</head>') ? stamped.replace('</head>', `${tag}</head>`) : tag + stamped;
|
|
132
137
|
}
|
|
133
138
|
|
|
@@ -965,18 +970,35 @@ function mountInternalSurfaces(app) {
|
|
|
965
970
|
return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/roster${qs}`);
|
|
966
971
|
});
|
|
967
972
|
|
|
968
|
-
//
|
|
969
|
-
//
|
|
970
|
-
//
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
|
|
973
|
+
// THE ONE OPTIONS MENU (task 1005144, BONGOS-V3 R04, ADR 0401). Every setting is
|
|
974
|
+
// reached from Settings, which is the options menu now; the screens that were pages
|
|
975
|
+
// of their own open INSIDE it, in a frame that asks for the old address with
|
|
976
|
+
// ?embed=1. A plain visit to an old address (a bookmark, a doc, a Discord message)
|
|
977
|
+
// lands on the menu with that screen open (?open=<entry>), and the browser carries
|
|
978
|
+
// the old #anchor across the redirect because the Location names none. The framed
|
|
979
|
+
// request passes through to the page and its own page gate below. 302 and no gate,
|
|
980
|
+
// as the redirects that follow: the redirect discloses nothing, Settings has its own
|
|
981
|
+
// sign-in gate, and each screen keeps its page gate and its routes (ADR 0016).
|
|
982
|
+
// options-menu.js RETIRED is this map's twin (tests/hall_options_menu.mjs holds them
|
|
983
|
+
// equal). market-watch-settings was the owner's tab on /market-watch (task 1005045)
|
|
984
|
+
// and a page before that; it has no page left, so it never passes through.
|
|
985
|
+
const RETIRED_TO_OPTIONS = Object.freeze({
|
|
986
|
+
'my-agents': 'my-agents',
|
|
987
|
+
'project-settings': 'project-settings',
|
|
988
|
+
modules: 'modules',
|
|
989
|
+
government: 'permissions',
|
|
990
|
+
'market-watch-rules': 'market-watch-rules',
|
|
991
|
+
'market-watch-settings': 'market-watch',
|
|
992
|
+
});
|
|
993
|
+
const RETIRED_OPTIONS_RE = /^\/builders\/(my-agents|project-settings|modules|government|market-watch-rules|market-watch-settings)(?:\.html)?\/?$/;
|
|
974
994
|
app.use((req, res, next) => {
|
|
975
995
|
if (isModuleSurfaceHost(req)) return next();
|
|
976
996
|
if (req.method !== 'GET' && req.method !== 'HEAD') return next();
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
997
|
+
const m = RETIRED_OPTIONS_RE.exec(path.posix.normalize(req.path));
|
|
998
|
+
if (!m) return next();
|
|
999
|
+
const embed = new URLSearchParams(req.url.includes('?') ? req.url.slice(req.url.indexOf('?') + 1) : '').get('embed');
|
|
1000
|
+
if (embed === '1' && m[1] !== 'market-watch-settings') return next();
|
|
1001
|
+
return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/settings?open=${RETIRED_TO_OPTIONS[m[1]]}`);
|
|
980
1002
|
});
|
|
981
1003
|
|
|
982
1004
|
// Pages folded into another page; 302 and no gate, as above. Task 1005047: Fleet costs is
|
package/src/crafts.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// src/crafts.js — the craft vocabulary, in one place (task 1005141, BONGOS-V3 C1).
|
|
3
|
+
//
|
|
4
|
+
// The game conversion (docs/specs/bongos-v3-game-conversion.md, "Task outline / G1",
|
|
5
|
+
// settled point 6) names the three characters a builder holds:
|
|
6
|
+
//
|
|
7
|
+
// artisan — builds: code, infra, Bongos itself (was 'engineer')
|
|
8
|
+
// artist — how the project looks and reads, AND its ideas (was 'artist' + 'ideator')
|
|
9
|
+
// patron — watches, proposes, and (with a seat) governs (was the 'governing' craft mode)
|
|
10
|
+
//
|
|
11
|
+
// 'ui' stays a module-contributed discipline (ADR 0272); it is not a character.
|
|
12
|
+
//
|
|
13
|
+
// THE OLD SPELLINGS STILL ARRIVE. A rolled-back release, a co-tenant instance on an
|
|
14
|
+
// older core (federated craft rollups), an old CLI, a bookmarked ?discipline=engineer
|
|
15
|
+
// and a help request's historical 'builder' all still say the old word. Every input
|
|
16
|
+
// boundary runs a value through normalizeCraft() so the old word lands as the new one;
|
|
17
|
+
// nothing downstream should ever compare against 'engineer' or 'ideator' again. The
|
|
18
|
+
// database keeps ACCEPTING the old words too (core_296's CHECKs are a superset) because
|
|
19
|
+
// `bongos upgrade` rolls code back without rolling the database back — a contraction
|
|
20
|
+
// that drops them is a later, separate migration.
|
|
21
|
+
//
|
|
22
|
+
// The crafts a TASK can carry are artisan and artist. Patron is a character, not a
|
|
23
|
+
// kind of work: a task is built (artisan) or made/imagined (artist), and governing
|
|
24
|
+
// work is a vote, not a task.
|
|
25
|
+
//
|
|
26
|
+
// ONLY THE TWO FUNCTIONS ARE EXPORTED. A list of the crafts, display labels and a
|
|
27
|
+
// label helper were exported here once and nothing read them, which the dead-code
|
|
28
|
+
// ratchet (knip) counts as a regression. Add one back when a caller needs it.
|
|
29
|
+
|
|
30
|
+
// Old spelling → new spelling. 'builder' is the help-request spelling of the
|
|
31
|
+
// engineering craft (ADR 0174); 'building'/'artistry'/'ideating'/'governing' are the
|
|
32
|
+
// hall's old craft-mode ids (modules/hall-ui/public/craft-mode.js).
|
|
33
|
+
const LEGACY = Object.freeze({
|
|
34
|
+
engineer: 'artisan',
|
|
35
|
+
engineering: 'artisan',
|
|
36
|
+
builder: 'artisan',
|
|
37
|
+
building: 'artisan',
|
|
38
|
+
ideator: 'artist',
|
|
39
|
+
ideating: 'artist',
|
|
40
|
+
artistry: 'artist',
|
|
41
|
+
governing: 'patron',
|
|
42
|
+
governor: 'patron',
|
|
43
|
+
ruler: 'patron',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Any spelling → today's. Non-strings pass through untouched (callers validate);
|
|
47
|
+
// an unknown word passes through too, so a module-contributed craft is never eaten.
|
|
48
|
+
function normalizeCraft(value) {
|
|
49
|
+
if (typeof value !== 'string') return value;
|
|
50
|
+
const v = value.trim().toLowerCase();
|
|
51
|
+
return Object.prototype.hasOwnProperty.call(LEGACY, v) ? LEGACY[v] : v;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// A list of crafts → today's spellings, deduped in first-seen order. The fold matters:
|
|
55
|
+
// ['artist', 'ideator'] is one craft now, not two.
|
|
56
|
+
function normalizeCrafts(list) {
|
|
57
|
+
if (!Array.isArray(list)) return list;
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const item of list) {
|
|
60
|
+
const c = normalizeCraft(item);
|
|
61
|
+
if (!out.includes(c)) out.push(c);
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { normalizeCraft, normalizeCrafts };
|
package/src/hall-helm.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/hall-helm.js — Helm, the look the builders' hall wears (task 1005192, ADR 0402).
|
|
2
|
+
//
|
|
3
|
+
// The hall is the game: everything after sign-in. It is dark only, and it reads in
|
|
4
|
+
// the two Helm faces unless its pack chose faces of its own. Both are decided HERE,
|
|
5
|
+
// at serve time, for one reason each:
|
|
6
|
+
//
|
|
7
|
+
// * DARK ONLY. A fixed html[data-theme="dark"] in the served markup selects the
|
|
8
|
+
// hall sheet's dark tier before first paint, with nothing to flash and nothing
|
|
9
|
+
// for the OS preference to reach (the ADR 0204 move, on the hall's own
|
|
10
|
+
// attribute). shell.js sets the same value again for a page read without it.
|
|
11
|
+
// * THE FACES. The pack splices --font-display / --font-body as a plain :root
|
|
12
|
+
// after every sheet, for every surface, so the landing and the hall would
|
|
13
|
+
// always read the same face. The hall sheet carries the Helm stacks as its own
|
|
14
|
+
// tokens (--helm-display / --helm-body) and hands them to the two font tokens
|
|
15
|
+
// under html[data-faces="helm"], which outranks the pack's :root. This file
|
|
16
|
+
// stamps that attribute, and links the two families, only for a hall whose
|
|
17
|
+
// pack still names the platform's default faces. A pack that names its own
|
|
18
|
+
// (config/branding.json, or a look from the style library with faces of its
|
|
19
|
+
// own, ADR 0358) is never stamped and keeps them.
|
|
20
|
+
//
|
|
21
|
+
// A hall page is recognised by what makes it one: it links the hall's sheet. So one
|
|
22
|
+
// seam (serve-internal.js injectBrandingGlobal) covers the server, the preview
|
|
23
|
+
// harness and the design kit's stub, and the landing, the hub and the status page,
|
|
24
|
+
// which never link that sheet, are never touched. PURE given `defaults`.
|
|
25
|
+
'use strict';
|
|
26
|
+
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const { escapeHtml: esc } = require('./html-escape');
|
|
30
|
+
|
|
31
|
+
// Barlow Condensed (the name voice) and Barlow (everything said to you), swapped in.
|
|
32
|
+
const HELM_FONTS_HREF = 'https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;500;600&family=Barlow:wght@400;500&display=swap';
|
|
33
|
+
const HALL_SHEET_RE = /<link\b[^>]*\bhref="\/builders\/style\.css[^"]*"/i;
|
|
34
|
+
const HTML_TAG_RE = /<html\b([^>]*)>/i;
|
|
35
|
+
|
|
36
|
+
function isHallPage(html) {
|
|
37
|
+
return HALL_SHEET_RE.test(String(html || ''));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// The platform's default faces: the neutral pack's two stacks, read once. A hall
|
|
41
|
+
// whose resolved pack still carries exactly these has not chosen faces.
|
|
42
|
+
let _defaults = null;
|
|
43
|
+
function defaultFaces() {
|
|
44
|
+
if (_defaults) return _defaults;
|
|
45
|
+
try {
|
|
46
|
+
const { resolveCoreRoot } = require('./instance-config'); // eslint-disable-line global-require
|
|
47
|
+
const pack = JSON.parse(fs.readFileSync(path.join(resolveCoreRoot(), 'config', 'branding.neutral.json'), 'utf8'));
|
|
48
|
+
const fonts = (pack.theme && pack.theme.ui && pack.theme.ui.fonts) || {};
|
|
49
|
+
_defaults = { display: String(fonts.display || ''), body: String(fonts.body || '') };
|
|
50
|
+
} catch { _defaults = { display: '', body: '' }; }
|
|
51
|
+
return _defaults;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Whether this hall takes the Helm faces: its pack names the default ones (or none).
|
|
55
|
+
function wearsHelmFaces(theme, defaults = defaultFaces()) {
|
|
56
|
+
const fonts = (theme && theme.fonts) || {};
|
|
57
|
+
const same = (mine, theirs) => !mine || mine === theirs;
|
|
58
|
+
return same(fonts.display, defaults.display) && same(fonts.body, defaults.body);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Set one attribute on the document's <html> tag, replacing a value already there.
|
|
62
|
+
function stampHtml(html, name, value) {
|
|
63
|
+
return html.replace(HTML_TAG_RE, (tag, attrs) => {
|
|
64
|
+
const rest = attrs.replace(new RegExp(`\\s${name}(?:="[^"]*")?`, 'i'), '');
|
|
65
|
+
return `<html${rest} ${name}="${esc(value)}">`;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The whole transform for one served page: a page that does not link the hall's
|
|
70
|
+
// sheet comes back untouched. `tail` is markup for the end of <head>.
|
|
71
|
+
function applyHelm(html, theme, defaults = defaultFaces()) {
|
|
72
|
+
if (!isHallPage(html)) return { html, tail: '' };
|
|
73
|
+
let out = stampHtml(html, 'data-theme', 'dark');
|
|
74
|
+
if (!wearsHelmFaces(theme, defaults)) return { html: out, tail: '' };
|
|
75
|
+
out = stampHtml(out, 'data-faces', 'helm');
|
|
76
|
+
return { html: out, tail: `<link rel="stylesheet" href="${esc(HELM_FONTS_HREF)}">` };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { applyHelm, isHallPage, wearsHelmFaces, stampHtml, HELM_FONTS_HREF };
|
package/src/module-api.js
CHANGED
|
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
|
|
|
81
81
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
82
82
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
83
83
|
|
|
84
|
-
const CORE_VERSION = '1.24.
|
|
84
|
+
const CORE_VERSION = '1.24.81'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
85
85
|
|
|
86
86
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
87
87
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -134,6 +134,11 @@ module.exports = {
|
|
|
134
134
|
// sleep(ms) — the one server-side "wait N milliseconds" (a Promise over setTimeout).
|
|
135
135
|
// The browser twin is window.OTB.sleep; fitness fails on a third copy (task 1004893).
|
|
136
136
|
sleep,
|
|
137
|
+
// crafts — the craft vocabulary (artisan · artist · patron, plus module 'ui') and
|
|
138
|
+
// normalizeCraft/normalizeCrafts, which turn an old spelling ('artisan',
|
|
139
|
+
// 'artist', 'builder') into today's. Every craft read from outside goes through
|
|
140
|
+
// it (task 1005141, src/crafts.js).
|
|
141
|
+
get crafts() { return require('./crafts'); },
|
|
137
142
|
|
|
138
143
|
// --- auth & rank (ADR 0016) — the gates the loader composes around a module's
|
|
139
144
|
// routes; a module declares the rank it needs and never wires auth itself.
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
// // what update, rollout channels and re-assessment name.
|
|
17
17
|
// "coreVersion": "^1.0.0", // REQUIRED. semver range of src/module-api.js CORE_VERSION it supports.
|
|
18
18
|
// "default": false, // OPTIONAL (default false). Enablement fail-safe when no config names it.
|
|
19
|
+
// "language": "javascript", // OPTIONAL (default "javascript", task 1004794). What the module's code is
|
|
20
|
+
// // written in — one of MODULE_LANGUAGES. A closed list: only languages
|
|
21
|
+
// // the core can actually run are on it.
|
|
19
22
|
// "author": "…", // OPTIONAL. Attribution (ADR 0107 §2) — portable, not a cross-GDS FK.
|
|
20
23
|
// "origin": "…", // OPTIONAL. Origin instance/project this module was built in.
|
|
21
24
|
// "license": "…", // OPTIONAL. e.g. "AGPL-3.0" — the license this module is offered under.
|
|
@@ -138,6 +141,14 @@ function isClaudeArtifactUrl(v) {
|
|
|
138
141
|
return u.protocol === 'https:' && !u.username && !u.password && (host === 'claude.ai' || host.endsWith('.claude.ai'));
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
// The languages a module may declare (task 1004794). Closed on purpose: a value is on
|
|
145
|
+
// this list only once the core can RUN a module written in it, so a manifest that says
|
|
146
|
+
// "python" today fails loudly at validation instead of being loaded as JavaScript and
|
|
147
|
+
// failing somewhere far from the cause. How a separate-program module runs beside
|
|
148
|
+
// Bongos is decided in its own ADR (task 1004797); that work adds its languages here.
|
|
149
|
+
const MODULE_LANGUAGES = Object.freeze(['javascript']);
|
|
150
|
+
const DEFAULT_MODULE_LANGUAGE = 'javascript';
|
|
151
|
+
|
|
141
152
|
const MAINTENANCE_STATUSES = ['core-maintained', 'maintained', 'deprecated', 'orphaned'];
|
|
142
153
|
const MAINTENANCE_KEYS = ['status', 'since', 'removeAfter', 'successor', 'note'];
|
|
143
154
|
// Statuses that describe a module on its way out — these owe a deprecation path.
|
|
@@ -213,6 +224,11 @@ function validateManifest(obj) {
|
|
|
213
224
|
// --- optional default ---
|
|
214
225
|
if ('default' in obj && typeof obj.default !== 'boolean') E('default: must be a boolean when present');
|
|
215
226
|
|
|
227
|
+
// --- optional language (task 1004794) — absent means "javascript" ---
|
|
228
|
+
if ('language' in obj && !MODULE_LANGUAGES.includes(obj.language)) {
|
|
229
|
+
E(`language: ${JSON.stringify(obj.language)} is not a supported module language (allowed: ${MODULE_LANGUAGES.join(', ')}; omit it for "${DEFAULT_MODULE_LANGUAGE}")`);
|
|
230
|
+
}
|
|
231
|
+
|
|
216
232
|
// --- optional attribution (ADR 0107 §2 — portable credit, not a cross-GDS FK) ---
|
|
217
233
|
for (const k of ATTRIBUTION_KEYS) {
|
|
218
234
|
if (k in obj && !isStr(obj[k])) E(`${k}: must be a non-empty string when present`);
|
package/src/modules.js
CHANGED
|
@@ -86,7 +86,7 @@ const MODULE_REGISTRY = {};
|
|
|
86
86
|
// shipping a pixel-art pipeline. Making it core aligns the one outlier list with
|
|
87
87
|
// the DB CHECK and keeps the copyright exclusion untouched. Order mirrors the
|
|
88
88
|
// migration 176 enum (engineer | artist | ideator | ui).
|
|
89
|
-
const CORE_DISCIPLINES = ['
|
|
89
|
+
const CORE_DISCIPLINES = ['artisan', 'artist'];
|
|
90
90
|
|
|
91
91
|
const KNOWN_MODULES = Object.keys(MODULE_REGISTRY);
|
|
92
92
|
|
|
@@ -475,7 +475,7 @@ function isModuleEnabled(name, set = modules()) {
|
|
|
475
475
|
const enabledModules = (set = modules()) => knownModules().filter((k) => set[k] === true);
|
|
476
476
|
|
|
477
477
|
// Disciplines this instance offers = core + every enabled module's declared
|
|
478
|
-
// disciplines, deduped, stable order. Vanilla = ['
|
|
478
|
+
// disciplines, deduped, stable order. Vanilla = ['artisan','artist'].
|
|
479
479
|
function enabledDisciplines(set = modules()) {
|
|
480
480
|
const reg = effectiveRegistry();
|
|
481
481
|
const out = [...CORE_DISCIPLINES];
|
|
@@ -29,15 +29,16 @@
|
|
|
29
29
|
'use strict';
|
|
30
30
|
|
|
31
31
|
// discipline key (tasks.discipline / builders.preferred_disciplines) → the owner's
|
|
32
|
-
// sentence. Keys mirror CORE_DISCIPLINES order (
|
|
33
|
-
//
|
|
32
|
+
// sentence. Keys mirror CORE_DISCIPLINES order (artisan | artist), the
|
|
33
|
+
// core_296 order. A module-contributed discipline (ui-design → 'ui') has
|
|
34
34
|
// no statement here on purpose: a craft a module brings is that module's to
|
|
35
35
|
// describe, and inventing one for it would be exactly the paraphrase this file
|
|
36
36
|
// forbids. Consumers must therefore treat "no statement" as normal, not as an error.
|
|
37
37
|
const ROLE_RESPONSIBILITIES = Object.freeze({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
artisan: 'Running and optimizing the running of Claude nonstop, and ensuring Artists can continue to interface with that system effectively.',
|
|
39
|
+
// The artist holds both of the owner's sentences since the ideator folded in
|
|
40
|
+
// (task 1005141, BONGOS-V3): the craft names changed, the words did not.
|
|
41
|
+
artist: 'No slop in the appearance and text of the project; the story, emotion and ideology of the project are communicated effectively; project purpose and gravitas are upheld. Make good ideas; be a philosopher / thought leader for the project; enable Artisans to scope and Artists to create with maximum efficiency — a baseline for creating scopes of work.',
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
// Provenance, carried with the text so every rendering can say where it came from
|
|
@@ -111,7 +111,7 @@ try {
|
|
|
111
111
|
]) {
|
|
112
112
|
await q(
|
|
113
113
|
`INSERT INTO tasks (id, version_id, title, status, shipped_by, shipped_at, discipline, kind)
|
|
114
|
-
VALUES ($1, $2, $3, 'shipped', $4, now(), '
|
|
114
|
+
VALUES ($1, $2, $3, 'shipped', $4, now(), 'artisan', 'feature')`,
|
|
115
115
|
[id, VER, title, SHIPPER],
|
|
116
116
|
);
|
|
117
117
|
}
|
|
@@ -57,7 +57,7 @@ const pin = (rel, exports) => {
|
|
|
57
57
|
await t('reportActivityRollup sends Bongos-Report-As-Of, stamped before the totals were read', async () => {
|
|
58
58
|
const senderPool = mockPool((sql) => {
|
|
59
59
|
if (/FROM builders WHERE id/.test(sql)) {
|
|
60
|
-
return { rows: [{ github_id: 42, total_credits: 7, karma: 1, rank: 'metic', preferred_disciplines: ['
|
|
60
|
+
return { rows: [{ github_id: 42, total_credits: 7, karma: 1, rank: 'metic', preferred_disciplines: ['artisan'] }] };
|
|
61
61
|
}
|
|
62
62
|
if (/count\(\*\)/.test(sql)) return { rows: [{ n: 3 }] };
|
|
63
63
|
return { rows: [] };
|
|
@@ -89,7 +89,7 @@ test('the exclusions are real and each one is reachable', () => {
|
|
|
89
89
|
'docs/architecture.md',
|
|
90
90
|
'docs/canonical-permissions.md',
|
|
91
91
|
'docs/recipes/parallel-goal-waves.md',
|
|
92
|
-
'docs/packs/
|
|
92
|
+
'docs/packs/artisan.md',
|
|
93
93
|
'docs/onboarding/drachmae-and-karma.md',
|
|
94
94
|
]) {
|
|
95
95
|
assert.equal(scan.isExcludedPath(p), false, `${p} must NOT be excluded`);
|
|
@@ -112,11 +112,11 @@ test('docs/ is SPLIT: the frozen record stays out, live instruction is now count
|
|
|
112
112
|
'docs/governance/rank-map-2026-08-06.md': 'GET /api/gds/tasks\n',
|
|
113
113
|
'docs/recipes/failed-ship-recovery-proof.md': 'POST /api/gds/claims/1/resolve\n',
|
|
114
114
|
'docs/recipes/how-to-do-a-thing.md': 'node scripts/bongos/api.js GET /api/gds/tasks/1\n',
|
|
115
|
-
'docs/packs/
|
|
115
|
+
'docs/packs/artisan.md': 'create a task via POST /api/gds/tasks\n',
|
|
116
116
|
}));
|
|
117
117
|
assert.deepEqual(
|
|
118
118
|
Object.keys(r.byFile).sort(),
|
|
119
|
-
['docs/packs/
|
|
119
|
+
['docs/packs/artisan.md', 'docs/recipes/how-to-do-a-thing.md'],
|
|
120
120
|
'only the instructional docs count — and they MUST count, or the narrowing did nothing',
|
|
121
121
|
);
|
|
122
122
|
assert.equal(r.total, 2);
|
|
@@ -661,7 +661,7 @@ test('list-pending: the real vouch writer’s row resolves its profile and guild
|
|
|
661
661
|
|
|
662
662
|
test('list-pending: a vouched TAKEOVER resolves the vouched applicant, not whoever filed first', async () => {
|
|
663
663
|
reset();
|
|
664
|
-
hubViews['8001'] = { profile: { view: 'sliver', sliver: { disciplines: ['
|
|
664
|
+
hubViews['8001'] = { profile: { view: 'sliver', sliver: { disciplines: ['artisan'] } }, guild: null };
|
|
665
665
|
await applyAs('ada', { [VOUCH_HEADER]: vouchFor('ada', { githubId: 8001, handle: 'ada-h' }) });
|
|
666
666
|
const r = await call('GET', '/api/gds/access-requests');
|
|
667
667
|
const ada = r.json.requests.find((x) => x.github_login === 'ada');
|
package/tests/artist_review.mjs
CHANGED
|
@@ -214,7 +214,7 @@ test('the panel names a public page as site:<first path segment>, and its ideas
|
|
|
214
214
|
|
|
215
215
|
test('the public button and the hall palette wear one name', () => {
|
|
216
216
|
const craft = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/craft-mode.js'), 'utf8');
|
|
217
|
-
const tool = /id: 'palette', icon: '
|
|
217
|
+
const tool = /id: 'palette', icon: '[^']+', short: '([^']+)', label: '([^']+)'/.exec(craft);
|
|
218
218
|
assert.ok(tool, 'the palette tool');
|
|
219
219
|
assert.ok(LOADER.includes("var SHORT = '" + tool[1] + "';"), 'the short name');
|
|
220
220
|
assert.ok(LOADER.includes("var LABEL = '" + tool[2] + "';"), 'the full name');
|
|
@@ -100,7 +100,7 @@ await test('createTask with artistReview lands in the goal; without one the catc
|
|
|
100
100
|
await createTask({ versionId: 'BONGOS-V3', title: 'Tweak: Home (builders:home)', discipline: 'artist', artistReview: true }, { client: c });
|
|
101
101
|
assert.equal(c.state.tasks[0].goal_id, 91, 'the studio task names the artist review goal');
|
|
102
102
|
await assert.rejects(
|
|
103
|
-
() => createTask({ versionId: 'BONGOS-V3', title: 'plain', discipline: '
|
|
103
|
+
() => createTask({ versionId: 'BONGOS-V3', title: 'plain', discipline: 'artisan' }, { client: fakeClient() }),
|
|
104
104
|
/goalId is required/, 'an ordinary goal-less create still throws');
|
|
105
105
|
});
|
|
106
106
|
|
|
@@ -183,7 +183,7 @@ async function seed() {
|
|
|
183
183
|
// since core_247 (task 1002988); every real create path resolves one.
|
|
184
184
|
await q(
|
|
185
185
|
`INSERT INTO tasks (id,version_id,title,status,credits_reward,kind,discipline)
|
|
186
|
-
VALUES ($1,$2,'bhlp fixture task','ready',10,'feature','
|
|
186
|
+
VALUES ($1,$2,'bhlp fixture task','ready',10,'feature','artisan')`,
|
|
187
187
|
[TASK, VER],
|
|
188
188
|
);
|
|
189
189
|
}
|
|
@@ -43,8 +43,8 @@ try {
|
|
|
43
43
|
await q(`INSERT INTO versions (id,name,status,track) VALUES ($1,'names','shipped','internal') ON CONFLICT (id) DO NOTHING`, [VER]);
|
|
44
44
|
// T2 first and with no name: the read must order by id and keep a null name.
|
|
45
45
|
await q(`INSERT INTO tasks (id,version_id,title,status,credits_reward,kind,discipline,short_name)
|
|
46
|
-
VALUES ($1,$3,'Payout approvals','backlog',10,'feature','
|
|
47
|
-
($2,$3,'#12','backlog',10,'feature','
|
|
46
|
+
VALUES ($1,$3,'Payout approvals','backlog',10,'feature','artisan','payout approvals'),
|
|
47
|
+
($2,$3,'#12','backlog',10,'feature','artisan',NULL)`, [T1, T2, VER]);
|
|
48
48
|
const holding = await blockers.createBlocker({ title: 'names fixture: holds two', bodyMd: '', source: 'test', sourceRef: `${REF}-a` });
|
|
49
49
|
const empty = await blockers.createBlocker({ title: 'names fixture: holds none', bodyMd: '', source: 'test', sourceRef: `${REF}-b` });
|
|
50
50
|
await blockers.linkBlockerToTask({ blockerId: holding.id, taskId: T2 }); // linked out of id order on purpose
|
|
@@ -280,7 +280,7 @@ const BUILDER = {
|
|
|
280
280
|
total_credits: 12,
|
|
281
281
|
karma: 3,
|
|
282
282
|
created_at: '2026-01-01T00:00:00.000Z',
|
|
283
|
-
preferred_disciplines: ['
|
|
283
|
+
preferred_disciplines: ['artisan'],
|
|
284
284
|
// The private columns the whitelist exists to keep out. Sentinel values so a
|
|
285
285
|
// raw-text scan of the serialized body bites even if a key were renamed.
|
|
286
286
|
anthropic_email: 'SENTINEL-anthropic-email',
|
|
@@ -23,31 +23,31 @@ function t(name, fn) {
|
|
|
23
23
|
|
|
24
24
|
// A claimable feed row (only the fields capNewcomerTasks reads).
|
|
25
25
|
const nc = (id, discipline) => ({ id, discipline, newcomer_friendly: true });
|
|
26
|
-
const real = (id, discipline = '
|
|
26
|
+
const real = (id, discipline = 'artisan') => ({ id, discipline, newcomer_friendly: false });
|
|
27
27
|
const ids = (rows) => rows.map((r) => r.id);
|
|
28
28
|
|
|
29
|
-
// ---- the reported case: 3 artist + 2
|
|
30
|
-
t('caps an artist+
|
|
29
|
+
// ---- the reported case: 3 artist + 2 artisan newcomer seats (was ideator before task 1005141 folded it into artist) ----
|
|
30
|
+
t('caps an artist+artisan pool of 5 down to 3, both lanes represented', () => {
|
|
31
31
|
const feed = [
|
|
32
32
|
nc(1259, 'artist'), nc(1260, 'artist'), nc(1261, 'artist'),
|
|
33
|
-
nc(1176, '
|
|
33
|
+
nc(1176, 'artisan'), nc(1177, 'artisan'),
|
|
34
34
|
];
|
|
35
35
|
const out = capNewcomerTasks(feed);
|
|
36
36
|
assert.equal(out.length, 3, 'exactly 3 newcomer rows kept');
|
|
37
|
-
// Round-robin SELECTS artist[0]=1259,
|
|
37
|
+
// Round-robin SELECTS artist[0]=1259, artisan[0]=1176, artist[1]=1260; the
|
|
38
38
|
// output preserves INPUT order (we filter the ordered array), so 1260 (which
|
|
39
39
|
// came before 1176 in the feed) lands ahead of it.
|
|
40
40
|
assert.deepEqual(ids(out), [1259, 1260, 1176]);
|
|
41
41
|
// Both disciplines are represented before a duplicate fills the third slot.
|
|
42
42
|
const disciplines = new Set(out.map((r) => r.discipline));
|
|
43
|
-
assert.ok(disciplines.has('artist') && disciplines.has('
|
|
43
|
+
assert.ok(disciplines.has('artist') && disciplines.has('artisan'));
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
// ---- non-newcomer (real) rows are never dropped ----
|
|
47
47
|
t('real version tasks pass through untouched and unbounded', () => {
|
|
48
48
|
const feed = [
|
|
49
49
|
real(900), real(901), real(902), real(903),
|
|
50
|
-
nc(1259, 'artist'), nc(1260, 'artist'), nc(1261, 'artist'), nc(1176, '
|
|
50
|
+
nc(1259, 'artist'), nc(1260, 'artist'), nc(1261, 'artist'), nc(1176, 'artisan'),
|
|
51
51
|
];
|
|
52
52
|
const out = capNewcomerTasks(feed);
|
|
53
53
|
const reals = out.filter((r) => r.newcomer_friendly !== true);
|
|
@@ -59,19 +59,19 @@ t('real version tasks pass through untouched and unbounded', () => {
|
|
|
59
59
|
// ---- ordering is preserved (we filter the input array, not re-sort) ----
|
|
60
60
|
t('preserves original feed ordering across mixed rows', () => {
|
|
61
61
|
const feed = [
|
|
62
|
-
real(900), nc(1259, 'artist'), real(901), nc(1176, '
|
|
63
|
-
nc(1260, 'artist'), nc(1261, 'artist'), nc(1177, '
|
|
62
|
+
real(900), nc(1259, 'artist'), real(901), nc(1176, 'artisan'),
|
|
63
|
+
nc(1260, 'artist'), nc(1261, 'artist'), nc(1177, 'artisan'),
|
|
64
64
|
];
|
|
65
65
|
const out = capNewcomerTasks(feed);
|
|
66
|
-
// Kept newcomer ids (round-robin over [artist,
|
|
67
|
-
// artist[0]=1259,
|
|
66
|
+
// Kept newcomer ids (round-robin over [artist, artisan] groups by appearance):
|
|
67
|
+
// artist[0]=1259, artisan[0]=1176, artist[1]=1260.
|
|
68
68
|
// Output keeps input order, dropping only un-kept newcomer rows (1261, 1177).
|
|
69
69
|
assert.deepEqual(ids(out), [900, 1259, 901, 1176, 1260]);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
// ---- no-op when at or under the cap ----
|
|
73
73
|
t('returns the list unchanged when newcomer rows <= cap', () => {
|
|
74
|
-
const feed = [nc(1, 'artist'), nc(2, '
|
|
74
|
+
const feed = [nc(1, 'artist'), nc(2, 'artisan'), real(3)];
|
|
75
75
|
const out = capNewcomerTasks(feed);
|
|
76
76
|
assert.equal(out.length, 3);
|
|
77
77
|
assert.deepEqual(ids(out), [1, 2, 3]);
|
|
@@ -106,7 +106,7 @@ t('tolerates non-array input', () => {
|
|
|
106
106
|
|
|
107
107
|
// ---- custom cap argument ----
|
|
108
108
|
t('honors a custom cap', () => {
|
|
109
|
-
const feed = [nc(1, 'artist'), nc(2, '
|
|
109
|
+
const feed = [nc(1, 'artist'), nc(2, 'artisan'), nc(3, 'artist'), nc(4, 'artisan')];
|
|
110
110
|
assert.equal(capNewcomerTasks(feed, 2).length, 2);
|
|
111
111
|
assert.deepEqual(ids(capNewcomerTasks(feed, 2)), [1, 2]);
|
|
112
112
|
});
|