@bongos/core 1.24.78 → 1.24.80
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 +498 -353
- 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/README.md +3 -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 +390 -370
- package/docs/copy-registry.json +719 -518
- 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 +15 -4
- package/docs/page-readings.json +3182 -3336
- 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 +4 -4
- package/modules/hall-ui/public/atlas.html +5 -5
- package/modules/hall-ui/public/atlas.js +3 -1
- package/modules/hall-ui/public/blockers.html +5 -5
- package/modules/hall-ui/public/board-room.html +4 -4
- 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 +2 -1
- package/modules/hall-ui/public/city.html +9 -6
- package/modules/hall-ui/public/city.js +7 -1
- package/modules/hall-ui/public/collab.html +4 -4
- package/modules/hall-ui/public/commands.html +4 -4
- package/modules/hall-ui/public/copy-desk.html +4 -4
- package/modules/hall-ui/public/craft-mode.js +258 -64
- package/modules/hall-ui/public/deploy.html +4 -4
- 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/government.html +4 -4
- 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/ideas.html +5 -5
- package/modules/hall-ui/public/index.html +11 -7
- package/modules/hall-ui/public/market-watch-item.html +4 -4
- package/modules/hall-ui/public/market-watch-rules.html +4 -4
- 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/modules.html +4 -4
- package/modules/hall-ui/public/money.html +4 -4
- package/modules/hall-ui/public/my-agents.html +4 -4
- 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/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 +4 -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/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/shell-game.css +65 -0
- package/modules/hall-ui/public/shell-game.js +283 -3
- package/modules/hall-ui/public/shell.js +7 -26
- 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-inside.js +3 -4
- package/modules/hall-ui/public/street.html +4 -4
- package/modules/hall-ui/public/studio.html +5 -5
- package/modules/hall-ui/public/style.css +73 -47
- package/modules/hall-ui/public/task-detail.js +5 -4
- package/modules/hall-ui/public/task.html +5 -5
- package/modules/hall-ui/public/thinking.css +10 -0
- package/modules/hall-ui/public/thinking.html +9 -6
- 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/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/welcome-boot.js +15 -6
- package/modules/hall-ui/public/work.html +5 -5
- 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 +21 -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/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/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +76 -0
- package/scripts/bongos/autobongos-host.js +19 -4
- package/scripts/bongos/autobongos-loop.js +1 -1
- package/scripts/bongos/claude-materialize.js +20 -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/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 +26 -9
- package/src/crafts.js +66 -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/autobongos_host.mjs +19 -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 +75 -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/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 +1 -1
- package/tests/hall_city.mjs +1 -1
- package/tests/hall_fullscreen.mjs +27 -3
- 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 +2 -2
- package/tests/hall_settings_world.mjs +2 -1
- package/tests/hall_sitemap.mjs +6 -5
- package/tests/hall_start_screen.mjs +244 -0
- package/tests/hall_street.mjs +2 -2
- package/tests/hall_street_inside.mjs +17 -9
- package/tests/hall_studio_world.mjs +3 -3
- 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
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_doc": "The ROLE REGISTRY: discipline -> the operating playbook that craft works from (task 1267; reshaped by task 1002990). Since the kernel/pack split, a CORE craft names a `pack` — a markdown role pack under docs/packs/ that carries everything the root CLAUDE.md kernel deliberately does not, because it is true for one craft only. A module-contributed discipline may instead name a `skill` (a slash-command playbook under .claude/skills/), which is how `ui` -> /design still works (ADR 0272 keeps ui-design's contribution unchanged). An entry may carry both; it must carry at least one. GET /me reads this (src/bongos/craft-mode.js) and puts each active claim's entry — `directive` plus pack path — on the claim as `craft_mode`; the Conductor hook injects the one for the claim bound to the session's worktree, so the session opens the right playbook without anyone reading terminal output (task 1002992). claim.js reads it too, printing only the one role line and running `materializeModuleSkills`. There is NO classification step — `discipline` is already a column on the task. One registry, never a server-side copy. A missing or invalid version of this file must never break a claim (claim.js fails open) — but fitness.js DOES hard-fail CI when an entry names a pack that is not in the tree, so the fail-open runtime cannot hide a broken registry from the build.
|
|
2
|
+
"_doc": "The ROLE REGISTRY: discipline -> the operating playbook that craft works from (task 1267; reshaped by task 1002990). Since the kernel/pack split, a CORE craft names a `pack` — a markdown role pack under docs/packs/ that carries everything the root CLAUDE.md kernel deliberately does not, because it is true for one craft only. A module-contributed discipline may instead name a `skill` (a slash-command playbook under .claude/skills/), which is how `ui` -> /design still works (ADR 0272 keeps ui-design's contribution unchanged). An entry may carry both; it must carry at least one. GET /me reads this (src/bongos/craft-mode.js) and puts each active claim's entry — `directive` plus pack path — on the claim as `craft_mode`; the Conductor hook injects the one for the claim bound to the session's worktree, so the session opens the right playbook without anyone reading terminal output (task 1002992). claim.js reads it too, printing only the one role line and running `materializeModuleSkills`. There is NO classification step — `discipline` is already a column on the task. One registry, never a server-side copy. A missing or invalid version of this file must never break a claim (claim.js fails open) — but fitness.js DOES hard-fail CI when an entry names a pack that is not in the tree, so the fail-open runtime cannot hide a broken registry from the build. THE GAME'S CHARACTERS (task 1005141, BONGOS-V3): engineer is now artisan, and the ideator folded into the artist, so there are two core packs — artisan.md and artist.md (which carries the ideation half). Patron is a character, not a task craft, and has no pack yet. A task row still holding an old spelling is read as the new one (src/crafts.js normalizeCraft). A mode entry may also set \"materializeModuleSkills\": true to have the claim land that mode's module-owned skills into this checkout's .claude/skills/ (task 1003486) — opt-in per discipline, core-checkout only, and fail-open like the hint itself.",
|
|
3
3
|
"modes": {
|
|
4
|
-
"
|
|
5
|
-
"pack": "docs/packs/
|
|
6
|
-
"label": "
|
|
7
|
-
"directive": "This is an
|
|
8
|
-
},
|
|
9
|
-
"ideator": {
|
|
10
|
-
"pack": "docs/packs/ideator.md",
|
|
11
|
-
"label": "Ideation session",
|
|
12
|
-
"directive": "This is an IDEATOR task. Read the Ideator pack now and work from it — it is the operating playbook for ideation and the only place the experience is defined. It inverts the usual dynamic: you (the human ideator) are the creative driver and Claude is your sounding board / research arm / devil's advocate, not a content generator filing ideas on command. Quick ideas stay ONE LINE, and after one is filed Claude asks whether to develop it into a Full Idea — five questions, drawn out of the conversation in the ideator's own words and scored on completeness. If no human is present (autonomous / bypass-permissions), the pack runs a self-adversarial divergence-and-critique loop instead of dumping a fixed batch. THE SURFACE, not just the behavior (ADR 0271): an ideator session is QUIET about its own machinery. Report outcomes, not mechanism — do not narrate tool calls, file paths, script names, API routes or task/claim bookkeeping unless they ask or something actually failed. Speak in the language of the idea, not the plumbing. If a step breaks, say plainly what broke and what you need — quiet never means hiding a problem."
|
|
4
|
+
"artisan": {
|
|
5
|
+
"pack": "docs/packs/artisan.md",
|
|
6
|
+
"label": "Artisan session",
|
|
7
|
+
"directive": "This is an ARTISAN task (the craft once called engineer). Read the Artisan pack now, before working — it is the operating playbook for this craft and the only place the standard build loop is written down: understand the code before editing -> make the smallest correct, scoped change -> VERIFY it (smoke tests + the preview/verification workflow when observable) -> /builder-ship with real notes. Building is already the default way you'd work, so the pack is the standard made explicit (plus the autonomous-mode guardrails: be conservative, verify hard, don't wander), not a redirect."
|
|
13
8
|
},
|
|
14
9
|
"artist": {
|
|
15
10
|
"pack": "docs/packs/artist.md",
|
|
16
|
-
"label": "
|
|
17
|
-
"directive": "This is an ARTIST task. Read the Artist pack now and work from it — it is the operating playbook for
|
|
11
|
+
"label": "Artist session",
|
|
12
|
+
"directive": "This is an ARTIST task. Read the Artist pack now and work from it — it is the operating playbook for the artist craft, which holds both halves of what used to be two crafts (task 1005141): how the project LOOKS AND READS, and its IDEAS. It is show-first and low-text: communicate in pictures and rendered surfaces, not paragraphs about what ran. Interpret as little as possible — take the brief literally, you are the artist's hands and not their art director — and arrive precalculated: load the look, have the options already rendered, never open with a form of intake questions. The artist's subject is how the project LOOKS AND READS, which is core (ADR 0272); a pixel-art or asset pipeline is a host module the pack drives only when the instance ships one. If no human is present (autonomous / bypass-permissions), make it, gate it hard, stage the passes and park the visuals for human review. When the work is an idea rather than a surface, the pack's ideation half applies: It inverts the usual dynamic: you (the human artist) are the creative driver and Claude is your sounding board / research arm / devil's advocate, not a content generator filing ideas on command. Quick ideas stay ONE LINE, and after one is filed Claude asks whether to develop it into a Full Idea — five questions, drawn out of the conversation in the ideator's own words and scored on completeness. If no human is present (autonomous / bypass-permissions), the pack runs a self-adversarial divergence-and-critique loop instead of dumping a fixed batch. THE SURFACE, not just the behavior (ADR 0271): an ideation session is QUIET about its own machinery. Report outcomes, not mechanism — do not narrate tool calls, file paths, script names, API routes or task/claim bookkeeping unless they ask or something actually failed. Speak in the language of the idea, not the plumbing. If a step breaks, say plainly what broke and what you need — quiet never means hiding a problem."
|
|
18
13
|
},
|
|
19
14
|
"ui": {
|
|
20
15
|
"skill": "design",
|
|
@@ -524,7 +524,7 @@ const KERNEL_FILES = [
|
|
|
524
524
|
'src/module-api.js', // the published doorway
|
|
525
525
|
'src/modules.js', // the registry/resolver facade
|
|
526
526
|
'src/module-seams.js', // the seam registry (ports/events/contributions)
|
|
527
|
-
'src/role-responsibilities.js', 'src/html-escape.js', 'src/sleep.js', // owner's role statements (task 1003732) + THE server HTML escaper (task 1004876) + THE server sleep (task 1004893) — all import nothing
|
|
527
|
+
'src/crafts.js', 'src/role-responsibilities.js', 'src/html-escape.js', 'src/sleep.js', // the craft vocabulary (task 1005141) + owner's role statements (task 1003732) + THE server HTML escaper (task 1004876) + THE server sleep (task 1004893) — all import nothing
|
|
528
528
|
'src/module-loader/loader.js', // the disk loader (discover → validate → mount)
|
|
529
529
|
'src/module-loader/manifest-schema.js', // the manifest validator
|
|
530
530
|
'src/module-loader/semver.js', // coreVersion compatibility
|
|
@@ -46,7 +46,7 @@ const vm = require('vm');
|
|
|
46
46
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
47
47
|
const HALL = 'modules/hall-ui/public';
|
|
48
48
|
const OUT = 'docs/hall-sitemap.md';
|
|
49
|
-
const CRAFTS = ['
|
|
49
|
+
const CRAFTS = ['artisan', 'artist', 'patron']; // the game's characters (task 1005141)
|
|
50
50
|
|
|
51
51
|
// URLs the server answers that are not a page file in the hall. A link to one is not broken.
|
|
52
52
|
// A NEW route a page links to belongs here, with where the server serves it; the audit fails
|
|
@@ -69,7 +69,7 @@ const ROUTES = {
|
|
|
69
69
|
// Targets that leave the hall on purpose: sign-in, the hub's /account (platform-server.js;
|
|
70
70
|
// settings.js points the link at the hub), the API, module assets, and any file with an extension.
|
|
71
71
|
const EXTERNAL = [/^\/api\//, /^\/auth\b/, /^\/login\b/, /^\/logout\b/, /^\/signin\b/, /^\/status\b/,
|
|
72
|
-
/^\/account\b/, /^\/modules\//, /^\/docs\b/, /^\/diagrams\//, /^\/healthz\b/, /^\/version\b/, /\.[a-z0-9]{2,5}$/i];
|
|
72
|
+
/^\/account\b/, /^\/modules\//, /^\/docs\b/, /^\/diagrams\//, /^\/characters\//, /^\/healthz\b/, /^\/version\b/, /\.[a-z0-9]{2,5}$/i];
|
|
73
73
|
|
|
74
74
|
// Pages whose words come from a markdown file they fetch. A link in that file to a hall page's
|
|
75
75
|
// own file (../../modules/hall-ui/public/<page>.html) opens the page in the hall (primer.js,
|
|
@@ -155,6 +155,7 @@ const LINK_RES = [
|
|
|
155
155
|
/href=\\?["']\$\{(?![^}]*\bhub)[^}]*\}(\/[a-z0-9-][^"'\\\s$`{]*)/g,
|
|
156
156
|
/Href\([^)]*\)\s*\{\s*return\s+'(\/[^'\s$]*)'/g, // function itemHref(id) { return '/x?id=' + … }
|
|
157
157
|
/\bhref\s*:\s*'(\/[^'\s$]*)'/g, // { href: '/x' }
|
|
158
|
+
/\bembed\s*:\s*'(\/[^'\s$]*)'/g, // { embed: '/x' } — an options menu entry opens it in a frame (task 1005144)
|
|
158
159
|
/\.href\s*=\s*'(\/[^'\s$]*)'/g, // a.href = '/x'
|
|
159
160
|
/setAttribute\(\s*'href'\s*,\s*'(\/[^'\s$]*)'/g, // setAttribute('href', '/x')
|
|
160
161
|
/location\.(?:assign|replace)\(\s*'(\/[^'\s$]*)'/g, // location.assign('/x')
|
|
@@ -288,12 +289,11 @@ function dedupe(list) {
|
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
// ---- the document ----
|
|
291
|
-
const CRAFT_NAME = {
|
|
292
|
+
const CRAFT_NAME = { artisan: 'Artisan', artist: 'Artist', patron: 'Patron' };
|
|
292
293
|
const CRAFT_HOME = {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
governing: '/street — the governor\'s street (Home hops there)',
|
|
294
|
+
artisan: '/ — the project city',
|
|
295
|
+
artist: '/ — the gallery',
|
|
296
|
+
patron: '/street — the governor\'s street (Home hops there)',
|
|
297
297
|
};
|
|
298
298
|
function list(xs) { return xs.length ? xs.map((x) => '`' + x + '`').join(', ') : '—'; }
|
|
299
299
|
function render(m) {
|
|
@@ -35,7 +35,7 @@ Acceptance:
|
|
|
35
35
|
- One unit test in tests/util.mjs covers two cases (positive + negative).
|
|
36
36
|
Out of scope: anything to do with timezones; calendar-aware durations.`,
|
|
37
37
|
kind: 'feature',
|
|
38
|
-
discipline: '
|
|
38
|
+
discipline: 'artisan',
|
|
39
39
|
version_id: 'GDS-V3',
|
|
40
40
|
};
|
|
41
41
|
const GOOD_VALUE_SUMMARY =
|
|
@@ -85,7 +85,7 @@ Acceptance:
|
|
|
85
85
|
- One smoke test that exercises the charge → row → rollback paths.
|
|
86
86
|
Out of scope: refund flow.`,
|
|
87
87
|
kind: 'feature',
|
|
88
|
-
discipline: '
|
|
88
|
+
discipline: 'artisan',
|
|
89
89
|
version_id: 'GDS-V3',
|
|
90
90
|
};
|
|
91
91
|
const BAD_VALUE_SUMMARY =
|
package/scripts/bongos/init.js
CHANGED
|
@@ -784,7 +784,7 @@ async function seedViaApi(spec, { dryRun, log = console.log, api = apiCall } = {
|
|
|
784
784
|
attempted++;
|
|
785
785
|
const res = await api('POST', '/api/bongos/tasks', {
|
|
786
786
|
version_id: vp.id, goal_id: goalId, title: t.title, description: t.description || '', status: 'ready',
|
|
787
|
-
kind: t.kind || 'infra', discipline: t.discipline || '
|
|
787
|
+
kind: t.kind || 'infra', discipline: t.discipline || 'artisan', source: 'bongos-init', ...extra,
|
|
788
788
|
});
|
|
789
789
|
if (!res.ok) failures.push({ title: t.title, status: res.status, detail: JSON.stringify(res.data) });
|
|
790
790
|
return res.ok;
|
|
@@ -794,7 +794,7 @@ async function seedViaApi(spec, { dryRun, log = console.log, api = apiCall } = {
|
|
|
794
794
|
let created = 0;
|
|
795
795
|
for (const t of starters) {
|
|
796
796
|
// task 1002988: `discipline: undefined` used to sit in this extras object, and
|
|
797
|
-
// because `...extra` spreads AFTER postTask's own `t.discipline || '
|
|
797
|
+
// because `...extra` spreads AFTER postTask's own `t.discipline || 'artisan'`
|
|
798
798
|
// it clobbered that default back to nothing — which is why a freshly scaffolded
|
|
799
799
|
// instance was born with role-less starter tasks. Dropped, so starters inherit
|
|
800
800
|
// the same default every other seeded task already got.
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
// session, so the floor silently drained to 0/0/0 and stayed there.
|
|
11
11
|
//
|
|
12
12
|
// WHAT THIS DOES NOW: each discipline defines a LIST of EVERGREEN chores in
|
|
13
|
-
// `newcomer-floor.templates.json` (
|
|
14
|
-
// docs-hygiene sweep; artist = generate an asset
|
|
13
|
+
// `newcomer-floor.templates.json` (artisan = "add a unit test" + the constrained
|
|
14
|
+
// docs-hygiene sweep; artist = generate an asset, or file ideas). A chore's
|
|
15
15
|
// definition never changes, yet running it is always valuable. Each chore keeps its
|
|
16
16
|
// OWN standing pool to its OWN per-template `floor`; when a chore's ready copies drop
|
|
17
17
|
// below that floor, this script stamps fresh IDENTICAL claimable COPIES of it. No AI,
|
|
@@ -64,7 +64,7 @@ const HARD_MAX_MINUTES = 60; // newcomer scope ceiling, never exceeded (task 614
|
|
|
64
64
|
// It is the craft VOCABULARY, which is why 'unclassified' is absent. Keep in step
|
|
65
65
|
// with the keys in newcomer-floor.templates.json ('ui' added by task 1003487); the
|
|
66
66
|
// server-side restock reads the live offered set instead (newcomer-restock.js).
|
|
67
|
-
const VALID_DISCIPLINES = new Set(['
|
|
67
|
+
const VALID_DISCIPLINES = new Set(['artisan', 'artist', 'ui']);
|
|
68
68
|
// Every status a copy's source_ref could occupy — scanned so `seq` is globally
|
|
69
69
|
// monotonic and never recycles a ref already used by a shipped/abandoned copy.
|
|
70
70
|
const ALL_STATUSES = ['backlog', 'ready', 'active', 'completed', 'confirmed', 'shipped', 'blocked', 'abandoned'];
|
|
@@ -307,6 +307,7 @@ async function main() {
|
|
|
307
307
|
discipline: arg('--discipline', args),
|
|
308
308
|
configPath: arg('--config', args) || DEFAULT_CONFIG_PATH,
|
|
309
309
|
};
|
|
310
|
+
if (FLAGS.discipline) FLAGS.discipline = ({ engineer: 'artisan', ideator: 'artist' })[FLAGS.discipline] || FLAGS.discipline;
|
|
310
311
|
if (FLAGS.discipline && !VALID_DISCIPLINES.has(FLAGS.discipline)) {
|
|
311
312
|
console.error(`bad --discipline "${FLAGS.discipline}"; valid: ${[...VALID_DISCIPLINES].join(', ')}`);
|
|
312
313
|
process.exit(2);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_doc": "The evergreen newcomer chores, grouped BY DISCIPLINE. Each discipline maps to a LIST of chore templates (was a single object before task 1488 — a discipline can now offer several first-builder chores so newcomers are not funnelled into one). The deterministic replenisher (scripts/bongos/newcomer-floor.js) and the on-claim restock (modules/onboarding/newcomer-restock.js) stamp disposable claimable COPIES from each template; the TEMPLATE is never claimed and never ships. Each copy's source_ref is tmpl:<discipline>:<template_id>:<seq> (monotonic), so identical-title copies never collide with the dedup. Per-template `floor` sets that chore’s standing pool depth; if a template omits it, the discipline default in newcomer-floor.config.json applies. credits_reward, est_minutes and floor all live here (per chore). version_id is per-template so each chore lands on the correct track; it MUST point at an open (non-shipped) version — update it at version close, or the replenisher's open-version guard refuses to stamp and the floor silently stops refilling (task 1002958: the engineer pins still read the long-shipped GDS-V4 and artist+ideator read V2, a version this ladder never had, so NO discipline could restock). touches[] is intentionally empty so multiple same-discipline newcomers can claim copies concurrently; git-merge is the real collision detector. To ADD a chore: append an entry to the discipline's array. To CHANGE one: edit it here — no code change needed. UI offers two browser-free chores (a token-drift fix measured by validate-design.js, and an accessible-name fix) so a newcomer whose only craft is ui is never starved for work on a machine with no Chrome/Edge for the kit — task 1003487.
|
|
3
|
-
"
|
|
2
|
+
"_doc": "The evergreen newcomer chores, grouped BY DISCIPLINE. Each discipline maps to a LIST of chore templates (was a single object before task 1488 — a discipline can now offer several first-builder chores so newcomers are not funnelled into one). The deterministic replenisher (scripts/bongos/newcomer-floor.js) and the on-claim restock (modules/onboarding/newcomer-restock.js) stamp disposable claimable COPIES from each template; the TEMPLATE is never claimed and never ships. Each copy's source_ref is tmpl:<discipline>:<template_id>:<seq> (monotonic), so identical-title copies never collide with the dedup. Per-template `floor` sets that chore’s standing pool depth; if a template omits it, the discipline default in newcomer-floor.config.json applies. credits_reward, est_minutes and floor all live here (per chore). version_id is per-template so each chore lands on the correct track; it MUST point at an open (non-shipped) version — update it at version close, or the replenisher's open-version guard refuses to stamp and the floor silently stops refilling (task 1002958: the engineer pins still read the long-shipped GDS-V4 and artist+ideator read V2, a version this ladder never had, so NO discipline could restock). touches[] is intentionally empty so multiple same-discipline newcomers can claim copies concurrently; git-merge is the real collision detector. To ADD a chore: append an entry to the discipline's array. To CHANGE one: edit it here — no code change needed. UI offers two browser-free chores (a token-drift fix measured by validate-design.js, and an accessible-name fix) so a newcomer whose only craft is ui is never starved for work on a machine with no Chrome/Edge for the kit — task 1003487. Artisan offers a SAFE coding chore (add a unit test — touches only tests/, which auto-ships) and the docs-hygiene sweep (constrained to non-protected docs so a sub-Metic ship does not stall on the authority core).",
|
|
3
|
+
"artisan": [
|
|
4
4
|
{
|
|
5
5
|
"template_id": "unit-test-pass",
|
|
6
6
|
"version_id": "BONGOS-V1",
|
|
7
|
-
"discipline": "
|
|
7
|
+
"discipline": "artisan",
|
|
8
8
|
"kind": "feature",
|
|
9
9
|
"est_minutes": 45,
|
|
10
10
|
"credits_reward": 15,
|
|
11
11
|
"floor": 2,
|
|
12
12
|
"title": "Add a unit test for an untested pure exported helper (assert its real behaviour)",
|
|
13
|
-
"description": "## What you're doing\n\nThe repo's safety net is its **unit suite** — every `tests/*.mjs` file, run by `scripts/bongos/run-unit-tests.js` in CI on every PR. New pure tests are **auto-discovered** (there is no allowlist to edit), so the moment you add one it joins the gate. Your first ship as an
|
|
13
|
+
"description": "## What you're doing\n\nThe repo's safety net is its **unit suite** — every `tests/*.mjs` file, run by `scripts/bongos/run-unit-tests.js` in CI on every PR. New pure tests are **auto-discovered** (there is no allowlist to edit), so the moment you add one it joins the gate. Your first ship as an artisan: pick ONE exported helper that has thin or no test coverage and add a focused, DB-free unit test for it.\n\nIt is identical for every newcomer and **always valuable** — coverage is never \"done\", the codebase has hundreds of small pure helpers, and a good test both guards behaviour and teaches you how a corner of the system works. It is also **safe to auto-ship**: you add ONE file under `tests/`, which is not a protected surface, so a clean test merges on green with no owner approval.\n\n## How to pick a target (a PURE helper)\n\nPick a **pure** function — deterministic, with no database, network, filesystem, or live-server dependency — so the test runs standalone. Do not hunt by hand: run the finder, which reads the live tree every time (so this task never goes stale the way a written list does):\n\n```\nnode scripts/bongos/find-untested.js\n```\n\nIt lists modules that **no test file names**, best candidates first — ones no test reaches at all, then ones that look pure, then the smallest. Its purity column is a heuristic: open the module and confirm the helper you pick really needs no database, network or files. If the list is empty, run it with `--all` and pick a module that still exports a helper you can test DB-free.\n\n## How to write it (mirror an existing test — there is no framework)\n\nThese tests are plain Node scripts using `node:assert`; there is no test runner. Open a small existing one as your model — for example `tests/criterion_ref_parse.mjs` or `tests/task_classifier_vocab.mjs` — and copy the shape:\n1. `import` / `require` the helper from its module.\n2. Assert **several** cases, including at least one **edge / boundary** case (empty input, null, a tricky value).\n3. Print a one-line success message at the end (an assert that throws exits non-zero, which is how the gate sees a failure).\n\nName the file `tests/<helper-or-module>.mjs`.\n\n## Verify + ship\n\nRun BOTH and confirm both are green:\n- `node tests/<your-file>.mjs` — your test alone, exits 0.\n- `node scripts/bongos/run-unit-tests.js` — the whole gate, with your test auto-included.\n\nThen `/builder-ship`. The ship notes must name the helper and file you tested.\n\n## Stay safe (off-limits)\n\nAdd exactly ONE new file under `tests/`. Do NOT edit `scripts/bongos/run-unit-tests.js` (it auto-discovers your test, and it is a protected file you may not touch), and do NOT change the helper you are testing. Your test must be **DB-free**: anything needing Postgres, the network, or a live server belongs in the integration gate and will fail standalone here. Touch no permission, pipeline, migration, or infra file.",
|
|
14
14
|
"done_when": [
|
|
15
15
|
"A NEW tests/<name>.mjs file was added (it did not exist before) that unit-tests a PURE exported helper — no Postgres, network, filesystem, or live server.",
|
|
16
16
|
"`node tests/<name>.mjs` exits 0 and the test makes real assertions about the helper's behaviour — at least two cases including one edge/boundary case — not a trivial always-true assertion.",
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
{
|
|
24
24
|
"template_id": "docs-hygiene-pass",
|
|
25
25
|
"version_id": "BONGOS-V1",
|
|
26
|
-
"discipline": "
|
|
26
|
+
"discipline": "artisan",
|
|
27
27
|
"kind": "cleanup",
|
|
28
28
|
"est_minutes": 40,
|
|
29
29
|
"credits_reward": 15,
|
|
30
30
|
"title": "Docs-hygiene sweep: fix one piece of real drift (broken link, dead anchor, or bare #NNN ref) in a Markdown doc",
|
|
31
|
-
"description": "## What you're doing\n\nThe project's docs drift constantly — links break when files are renamed, heading anchors go dead, formatting breaks, and bare `#NNN` refs creep back (a bare `#123` autolinks to GitHub's issue space and 404s — see CLAUDE.md §8). Your job: find **one** genuine piece of drift in **one** SAFE doc and ship the fix.\n\n## First — do NOT open these (they will stall your ship)\n\nA newcomer (Xenos / Thetes, sub-Metic) may not modify the authority / pipeline core. Picking one of these as your target will **fail your grade or block your ship**, so don't even go looking for drift in them: `CLAUDE.md`, `docs/canonical-permissions.md`, the authority ADRs (`docs/adr/0016-*`, `docs/adr/0043-*`), and anything under `migrations/`, `infra/`, `.github/`, `.husky/`, `.claude/hooks/`, plus `package.json` and the ship / grade / deploy scripts. (The authoritative list is the glob set in `src/bongos/permission-path-check.js`.) The most *visible* drift often lives in `CLAUDE.md` itself — resist it; it is off-limits to you.\n\n## Pick from the SAFE docs only\n\nAll fair game: a recipe under `docs/recipes/`, a session log under `docs/session-logs/`, `docs/architecture.md`, `docs/file-map.md`, `docs/TRACKS.md`, a **non-authority** ADR, `README.md`, or `art/README.md`.\n\n## How to find a real target (don't ship a no-op)\n\n1. **Bare `#NNN` refs (easiest):** run `node scripts/bongos/linkify-refs.js` (read-only) — it lists docs with upgradeable refs. Pick one from the SAFE list above and fix it with `node scripts/bongos/linkify-refs.js --apply <safe-file.md>`.\n2. **Broken / renamed links:** open a safe doc and check its `](path)` links resolve to a real file (cross-check `docs/file-map.md`).\n3. **Dead anchors:** `](#heading)` where that heading no longer exists in the target doc.\n4. **Formatting drift:** a broken list nest, an unclosed code fence, a mangled table.\n\n## Verify + ship\n\nRun `node scripts/bongos/linkify-refs.js --check <your-file.md>` (scoped to YOUR one safe file — exit 0) and confirm any link you touched resolves, then `/builder-ship`. The ship notes must name the specific drift you fixed and the one file.\n\n_Prefer writing code to prose? Claim the \"add a unit test\"
|
|
31
|
+
"description": "## What you're doing\n\nThe project's docs drift constantly — links break when files are renamed, heading anchors go dead, formatting breaks, and bare `#NNN` refs creep back (a bare `#123` autolinks to GitHub's issue space and 404s — see CLAUDE.md §8). Your job: find **one** genuine piece of drift in **one** SAFE doc and ship the fix.\n\n## First — do NOT open these (they will stall your ship)\n\nA newcomer (Xenos / Thetes, sub-Metic) may not modify the authority / pipeline core. Picking one of these as your target will **fail your grade or block your ship**, so don't even go looking for drift in them: `CLAUDE.md`, `docs/canonical-permissions.md`, the authority ADRs (`docs/adr/0016-*`, `docs/adr/0043-*`), and anything under `migrations/`, `infra/`, `.github/`, `.husky/`, `.claude/hooks/`, plus `package.json` and the ship / grade / deploy scripts. (The authoritative list is the glob set in `src/bongos/permission-path-check.js`.) The most *visible* drift often lives in `CLAUDE.md` itself — resist it; it is off-limits to you.\n\n## Pick from the SAFE docs only\n\nAll fair game: a recipe under `docs/recipes/`, a session log under `docs/session-logs/`, `docs/architecture.md`, `docs/file-map.md`, `docs/TRACKS.md`, a **non-authority** ADR, `README.md`, or `art/README.md`.\n\n## How to find a real target (don't ship a no-op)\n\n1. **Bare `#NNN` refs (easiest):** run `node scripts/bongos/linkify-refs.js` (read-only) — it lists docs with upgradeable refs. Pick one from the SAFE list above and fix it with `node scripts/bongos/linkify-refs.js --apply <safe-file.md>`.\n2. **Broken / renamed links:** open a safe doc and check its `](path)` links resolve to a real file (cross-check `docs/file-map.md`).\n3. **Dead anchors:** `](#heading)` where that heading no longer exists in the target doc.\n4. **Formatting drift:** a broken list nest, an unclosed code fence, a mangled table.\n\n## Verify + ship\n\nRun `node scripts/bongos/linkify-refs.js --check <your-file.md>` (scoped to YOUR one safe file — exit 0) and confirm any link you touched resolves, then `/builder-ship`. The ship notes must name the specific drift you fixed and the one file.\n\n_Prefer writing code to prose? Claim the \"add a unit test\" artisan chore instead — it's the other first-builder artisan task._",
|
|
32
32
|
"done_when": [
|
|
33
33
|
"Exactly one non-protected Markdown file was changed (NOT CLAUDE.md, canonical-permissions.md, an authority ADR, or anything under migrations/ infra/ .github/ .husky/ .claude/hooks/ — see src/bongos/permission-path-check.js), and the change is a genuine hygiene fix (a broken/renamed link path, a dead heading anchor, a bare `#NNN` ref linkified, or formatting drift) — not a content/meaning edit and not a cosmetic no-op.",
|
|
34
34
|
"`node scripts/bongos/linkify-refs.js --check <your-file.md>` (scoped to the one file you changed) exits 0.",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"est_minutes": 55,
|
|
50
50
|
"credits_reward": 15,
|
|
51
51
|
"title": "Generate one new in-world art asset of your choice (a prop, plant, animal, or accessory) via the pixel-art pipeline",
|
|
52
|
-
"description": "## What you're doing\n\nThis instance's world always needs more to look at. Your first ship as an artist is to **generate ONE new in-world asset of your own choosing** and add it to the world. You pick the subject — that's the fun part. Good candidates, all in-world (Mediterranean coast just south of Athens, mythic 'discovered secret' tone):\n- **Props**: amphora, fishing net, basket, crate, urn, torch, market awning, oar, clay pot.\n- **Plants**: olive tree, cypress, reeds, grapevine, shrub, wild herbs.\n- **Animals**: goat, chicken, duck, donkey, fish, gull, lizard.\n- **Accessories**: a sandal, a sun hat, a length of rope, a string of beads.\n\nIt is identical for every newcomer and **always valuable**, because a growing world has effectively unbounded demand for new props, flora, fauna, and detail — every newcomer picks a different subject, so two runs never collide.\n\n## How to find your way in\n\n1. Read `art/README.md` and `docs/recipes/pixel-art-pipeline.md`, plus `modules/art-pipeline/template/style_guide.md`, `modules/art-pipeline/template/rubric.json`, and `modules/art-pipeline/template/palette.json` — that's the look you must hit: 16-bit pixel art, early-Pokémon (FireRed) touchstone, top-down, 32×32, locked 64-colour Mediterranean palette (olive greens, warm tans, turquoise sea, weathered marble — NOT spring green, NOT navy, NOT pine).\n2. **Pick ONE subject.** Keep it to a SINGLE 32×32 tile/sprite — not a whole spritesheet or family (that's a bigger task).\n3. **Check the budget first**: `python3 modules/art-pipeline/pipeline/cost_ledger.py status` (the pipeline hard-stops at $80; one asset is ~$0.05–0.45).\n4. **Generate via the pipeline — do not hand-draw or freelance.** Use the `/otb-tile-generate` skill: it appends your new entry to `modules/art-pipeline/template/vocabulary.json`, runs the generate → post-process-to-32×32 → score-against-rubric → retry orchestrator, saves the final PNG to `modules/art-pipeline/generated/tiles/<id>.png`, and logs cost. (This needs the art pipeline's `GOOGLE_AI_STUDIO_KEY`, which resolves from `~/.config/otb/env`; most builder setups have it. If yours doesn't, claim the
|
|
52
|
+
"description": "## What you're doing\n\nThis instance's world always needs more to look at. Your first ship as an artist is to **generate ONE new in-world asset of your own choosing** and add it to the world. You pick the subject — that's the fun part. Good candidates, all in-world (Mediterranean coast just south of Athens, mythic 'discovered secret' tone):\n- **Props**: amphora, fishing net, basket, crate, urn, torch, market awning, oar, clay pot.\n- **Plants**: olive tree, cypress, reeds, grapevine, shrub, wild herbs.\n- **Animals**: goat, chicken, duck, donkey, fish, gull, lizard.\n- **Accessories**: a sandal, a sun hat, a length of rope, a string of beads.\n\nIt is identical for every newcomer and **always valuable**, because a growing world has effectively unbounded demand for new props, flora, fauna, and detail — every newcomer picks a different subject, so two runs never collide.\n\n## How to find your way in\n\n1. Read `art/README.md` and `docs/recipes/pixel-art-pipeline.md`, plus `modules/art-pipeline/template/style_guide.md`, `modules/art-pipeline/template/rubric.json`, and `modules/art-pipeline/template/palette.json` — that's the look you must hit: 16-bit pixel art, early-Pokémon (FireRed) touchstone, top-down, 32×32, locked 64-colour Mediterranean palette (olive greens, warm tans, turquoise sea, weathered marble — NOT spring green, NOT navy, NOT pine).\n2. **Pick ONE subject.** Keep it to a SINGLE 32×32 tile/sprite — not a whole spritesheet or family (that's a bigger task).\n3. **Check the budget first**: `python3 modules/art-pipeline/pipeline/cost_ledger.py status` (the pipeline hard-stops at $80; one asset is ~$0.05–0.45).\n4. **Generate via the pipeline — do not hand-draw or freelance.** Use the `/otb-tile-generate` skill: it appends your new entry to `modules/art-pipeline/template/vocabulary.json`, runs the generate → post-process-to-32×32 → score-against-rubric → retry orchestrator, saves the final PNG to `modules/art-pipeline/generated/tiles/<id>.png`, and logs cost. (This needs the art pipeline's `GOOGLE_AI_STUDIO_KEY`, which resolves from `~/.config/otb/env`; most builder setups have it. If yours doesn't, claim the artisan newcomer chore or the artist idea-seed chore instead, or ask an Archon to enable it.)\n5. **Confirm it PASSED** the rubric (`modules/art-pipeline/iterations/run_summary.json` shows `status: \"pass\"`). If it only reached best-effort, regenerate with the fix_hint — never paper over a fail.\n6. **See it in-game**: `node scripts/bongos/art-stage.js` rebuilds the atlas and surfaces it on your sandbox preview (`sandbox-<login>.<instance-apex>`).\n7. Ship via `/builder-ship`; the ship notes name your asset and its rubric score.\n\n## Stay clear of (READ-ONLY)\n\nNever modify the locked palette (`modules/art-pipeline/template/palette.json`) or the rubric (`modules/art-pipeline/template/rubric.json`) — those are theme-defining and changing them needs an ADR (out of scope for a first ship). Touch only art assets/vocabulary under `art/` (and, only if your asset is a genuinely new base-tile TYPE, the `public/game/world/tilePalette.js` mapping). Do not touch any permission/pipeline-core/migration/infra file.",
|
|
53
53
|
"done_when": [
|
|
54
54
|
"Exactly one NEW in-world asset (your chosen subject) was generated via the pixel-art pipeline (`/otb-tile-generate` / orchestrator.py); the final PNG exists at modules/art-pipeline/generated/tiles/<id>.png.",
|
|
55
55
|
"It PASSED the rubric (modules/art-pipeline/iterations/run_summary.json status 'pass', or /otb-design-review numbers reported at/above the bar) and every pixel is on the locked palette (modules/art-pipeline/template/palette.json).",
|
|
@@ -59,18 +59,16 @@
|
|
|
59
59
|
],
|
|
60
60
|
"touches": [],
|
|
61
61
|
"floor": 3
|
|
62
|
-
}
|
|
63
|
-
],
|
|
64
|
-
"ideator": [
|
|
62
|
+
},
|
|
65
63
|
{
|
|
66
64
|
"template_id": "idea-seed-pass",
|
|
67
65
|
"version_id": "BONGOS-V1",
|
|
68
|
-
"discipline": "
|
|
66
|
+
"discipline": "artist",
|
|
69
67
|
"kind": "feature",
|
|
70
68
|
"est_minutes": 45,
|
|
71
69
|
"credits_reward": 12,
|
|
72
70
|
"title": "Idea-seed pass: file 5-8 fresh in-world ideas (names, lore, or builder-UX) into the idea inbox",
|
|
73
|
-
"description": "## What you'll do\n\nThe seed of almost everything built here is the **idea_inbox** — a queue anyone can drop ideas into, which trusted builders later triage into real tasks. Your first ship
|
|
71
|
+
"description": "## What you'll do\n\nThe seed of almost everything built here is the **idea_inbox** — a queue anyone can drop ideas into, which trusted builders later triage into real tasks. Your first ship here is simple and concrete: **add a small batch of fresh ideas to that inbox.** You write no code, touch no game, change no system — you think, write a few short well-shaped ideas, and file them with one command each.\n\nIt is identical for every newcomer and **always valuable**: the inbox is never \"done\" — every triage pass consumes ideas (promote/discard/merge), so it is permanently hungry for fresh input, and a growing world always wants more names, more lore, more texture. Your batch is brand-new content, so it can never duplicate \"the work\" — the supply IS the work.\n\n## Pick ONE branch\n\n- **Branch A — PRODUCT flavour (default).** Ideas that grow the world players see: **names + one line of lore** for a character, structure, landmark, district, ship, or festival; or a world-background snippet (a custom, a superstition, a line a named NPC says). Tone: mythic, mysterious, a \"discovered secret\"; Mediterranean ancient-Greek coast (just south of Athens); NOT modern, NOT tongue-in-cheek-modern. Ground it in CLAUDE.md §3/§6/§7.\n- **Branch B — INTERNAL / builder-UX.** Small user-experience improvement ideas for the GDS / builders' hall (the surfaces builders use, not the game): a clearer label, a confusing onboarding message, a quality-of-life tweak to a status page or a slash-command's output. See `docs/TRACKS.md` for product vs internal.\n\n## How to find your way in\n\n1. (Branch A) Read CLAUDE.md §3/§6/§7 for tone. (Branch B) Skim `docs/TRACKS.md` and click around your instance's /builders hall for the rough edge you'll write up.\n2. See what's already filed so you don't repeat it: `node scripts/bongos/api.js GET /api/bongos/inbox`.\n3. File each idea with `node scripts/bongos/capture.js \"<title>\" --body-file <path>` (filing an idea is the open-to-all path — a Xenos/Thetes session is allowed to do it; you just can't triage them). Put one line of detail + a `track: product` or `track: internal` line in each body file.",
|
|
74
72
|
"done_when": [
|
|
75
73
|
"You filed 5–8 fresh ideas into the idea_inbox via `node scripts/bongos/capture.js` (each prints a `Captured idea #N` confirmation), all in ONE branch (A product, or B internal/UX).",
|
|
76
74
|
"Each idea has a clear title and a body with at least one sentence of detail — for Branch A, one line of in-world lore (who/what it is); for Branch B, what the rough edge is and what 'better' looks like.",
|
|
@@ -24,13 +24,13 @@ function test(name, fn) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// per-discipline DEFAULT floors (the fallback when a template omits its own).
|
|
27
|
-
const FLOOR_DEFAULTS = {
|
|
27
|
+
const FLOOR_DEFAULTS = { artisan: 2, artist: 3 };
|
|
28
28
|
|
|
29
29
|
// templates-by-discipline fixture (each discipline -> LIST of chores).
|
|
30
30
|
const TEMPLATES = {
|
|
31
|
-
|
|
32
|
-
{ template_id: 'unit-test-pass', version_id: 'GDS-V4', discipline: '
|
|
33
|
-
{ template_id: 'docs-hygiene-pass', version_id: 'GDS-V4', discipline: '
|
|
31
|
+
artisan: [
|
|
32
|
+
{ template_id: 'unit-test-pass', version_id: 'GDS-V4', discipline: 'artisan', floor: 2, title: 'Add a unit test', done_when: ['a', 'b'], est_minutes: 45 },
|
|
33
|
+
{ template_id: 'docs-hygiene-pass', version_id: 'GDS-V4', discipline: 'artisan', floor: 1, title: 'Docs sweep', done_when: ['a', 'b'], est_minutes: 40 },
|
|
34
34
|
],
|
|
35
35
|
artist: [
|
|
36
36
|
{ template_id: 'asset-generate-pass', version_id: 'V2', discipline: 'artist', title: 'Asset', done_when: ['a', 'b'], est_minutes: 55 }, // no own floor -> default
|
|
@@ -40,7 +40,7 @@ const TEMPLATES = {
|
|
|
40
40
|
// ---- templateFloor ----
|
|
41
41
|
|
|
42
42
|
test('templateFloor: own floor wins over the discipline default', () => {
|
|
43
|
-
assert.strictEqual(nf.templateFloor({ floor: 5 }, '
|
|
43
|
+
assert.strictEqual(nf.templateFloor({ floor: 5 }, 'artisan', FLOOR_DEFAULTS), 5);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
test('templateFloor: falls back to the discipline default when omitted', () => {
|
|
@@ -48,7 +48,7 @@ test('templateFloor: falls back to the discipline default when omitted', () => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
test('templateFloor: 0 is a valid explicit floor (not treated as missing)', () => {
|
|
51
|
-
assert.strictEqual(nf.templateFloor({ floor: 0 }, '
|
|
51
|
+
assert.strictEqual(nf.templateFloor({ floor: 0 }, 'artisan', FLOOR_DEFAULTS), 0);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
test('templateFloor: unknown discipline with no default -> 0', () => {
|
|
@@ -69,7 +69,7 @@ test('computeTemplateDeficits: empty supply -> each chore needs its full floor',
|
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
test('computeTemplateDeficits: ready + backlog both count as supply (idempotency)', () => {
|
|
72
|
-
const supply = { '
|
|
72
|
+
const supply = { 'artisan:unit-test-pass': { ready: 1, backlog: 1 } }; // fills floor 2
|
|
73
73
|
const rows = nf.computeTemplateDeficits(supply, TEMPLATES, FLOOR_DEFAULTS);
|
|
74
74
|
assert.strictEqual(deficitFor(rows, 'unit-test-pass').needed, 0);
|
|
75
75
|
assert.strictEqual(deficitFor(rows, 'docs-hygiene-pass').needed, 1, 'sibling chore is independent');
|
|
@@ -77,7 +77,7 @@ test('computeTemplateDeficits: ready + backlog both count as supply (idempotency
|
|
|
77
77
|
|
|
78
78
|
test('computeTemplateDeficits: supply is per-template, not shared across a discipline', () => {
|
|
79
79
|
// over-supplying one engineer chore must NOT satisfy the other.
|
|
80
|
-
const supply = { '
|
|
80
|
+
const supply = { 'artisan:unit-test-pass': { ready: 9, backlog: 0 } };
|
|
81
81
|
const rows = nf.computeTemplateDeficits(supply, TEMPLATES, FLOOR_DEFAULTS);
|
|
82
82
|
assert.strictEqual(deficitFor(rows, 'unit-test-pass').needed, 0);
|
|
83
83
|
assert.strictEqual(deficitFor(rows, 'docs-hygiene-pass').needed, 1);
|
|
@@ -102,48 +102,48 @@ test('computeTemplateDeficits: ignores non-floor disciplines', () => {
|
|
|
102
102
|
// ---- instanceSourceRef + parseSeq + parseRef ----
|
|
103
103
|
|
|
104
104
|
test('instanceSourceRef: tmpl:<disc>:<template_id>:<seq>', () => {
|
|
105
|
-
assert.strictEqual(nf.instanceSourceRef('
|
|
105
|
+
assert.strictEqual(nf.instanceSourceRef('artisan', 'unit-test-pass', 7), 'tmpl:artisan:unit-test-pass:7');
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
test('parseSeq: returns seq only for the matching discipline+template', () => {
|
|
109
|
-
assert.strictEqual(nf.parseSeq('tmpl:
|
|
110
|
-
assert.strictEqual(nf.parseSeq('tmpl:
|
|
111
|
-
assert.strictEqual(nf.parseSeq('tmpl:
|
|
112
|
-
assert.strictEqual(nf.parseSeq('manual:
|
|
113
|
-
assert.strictEqual(nf.parseSeq('tmpl:
|
|
109
|
+
assert.strictEqual(nf.parseSeq('tmpl:artisan:unit-test-pass:4', 'artisan', 'unit-test-pass'), 4);
|
|
110
|
+
assert.strictEqual(nf.parseSeq('tmpl:artisan:unit-test-pass:4', 'artist', 'unit-test-pass'), null, 'wrong discipline');
|
|
111
|
+
assert.strictEqual(nf.parseSeq('tmpl:artisan:other:4', 'artisan', 'unit-test-pass'), null, 'wrong template');
|
|
112
|
+
assert.strictEqual(nf.parseSeq('manual:artisan:some-title', 'artisan', 'unit-test-pass'), null, 'foreign ref');
|
|
113
|
+
assert.strictEqual(nf.parseSeq('tmpl:artisan:unit-test-pass:abc', 'artisan', 'unit-test-pass'), null, 'malformed seq');
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
test('parseRef: decomposes a chore ref into discipline/template/seq', () => {
|
|
117
|
-
assert.deepStrictEqual(nf.parseRef('tmpl:
|
|
118
|
-
assert.strictEqual(nf.parseRef('manual:
|
|
119
|
-
assert.strictEqual(nf.parseRef('tmpl:
|
|
120
|
-
assert.strictEqual(nf.parseRef('tmpl:
|
|
117
|
+
assert.deepStrictEqual(nf.parseRef('tmpl:artisan:unit-test-pass:7'), { discipline: 'artisan', templateId: 'unit-test-pass', seq: 7 });
|
|
118
|
+
assert.strictEqual(nf.parseRef('manual:artisan:x'), null, 'not a chore ref');
|
|
119
|
+
assert.strictEqual(nf.parseRef('tmpl:artisan:unit-test-pass:0'), null, 'seq must be positive');
|
|
120
|
+
assert.strictEqual(nf.parseRef('tmpl:artisan:unit-test-pass'), null, 'too few parts');
|
|
121
121
|
assert.strictEqual(nf.parseRef('tmpl:a:b:c:d'), null, 'too many parts');
|
|
122
122
|
});
|
|
123
123
|
|
|
124
124
|
// ---- nextSeq (monotonic across all refs) ----
|
|
125
125
|
|
|
126
126
|
test('nextSeq: empty set -> 1', () => {
|
|
127
|
-
assert.strictEqual(nf.nextSeq(new Set(), '
|
|
127
|
+
assert.strictEqual(nf.nextSeq(new Set(), 'artisan', 'unit-test-pass'), 1);
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
test('nextSeq: 1 + max matching seq, ignoring other templates/disciplines', () => {
|
|
131
131
|
const refs = new Set([
|
|
132
|
-
'tmpl:
|
|
133
|
-
'tmpl:
|
|
134
|
-
'tmpl:
|
|
135
|
-
'tmpl:
|
|
132
|
+
'tmpl:artisan:unit-test-pass:1',
|
|
133
|
+
'tmpl:artisan:unit-test-pass:3', // gap is fine; max is 3
|
|
134
|
+
'tmpl:artisan:unit-test-pass:2',
|
|
135
|
+
'tmpl:artisan:docs-hygiene-pass:9', // sibling chore — ignored
|
|
136
136
|
'tmpl:artist:asset-generate-pass:5', // different discipline — ignored
|
|
137
|
-
'manual:
|
|
137
|
+
'manual:artisan:something', // foreign — ignored
|
|
138
138
|
]);
|
|
139
|
-
assert.strictEqual(nf.nextSeq(refs, '
|
|
140
|
-
assert.strictEqual(nf.nextSeq(refs, '
|
|
141
|
-
assert.strictEqual(nf.nextSeq(refs, '
|
|
139
|
+
assert.strictEqual(nf.nextSeq(refs, 'artisan', 'unit-test-pass'), 4);
|
|
140
|
+
assert.strictEqual(nf.nextSeq(refs, 'artisan', 'docs-hygiene-pass'), 10, 'sibling seq is independent');
|
|
141
|
+
assert.strictEqual(nf.nextSeq(refs, 'artist', 'idea-seed-pass'), 1);
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
test('nextSeq: never recycles a shipped/abandoned seq (max over ALL statuses)', () => {
|
|
145
|
-
const refs = new Set(['tmpl:
|
|
146
|
-
assert.strictEqual(nf.nextSeq(refs, '
|
|
145
|
+
const refs = new Set(['tmpl:artist:idea-seed-pass:1', 'tmpl:artist:idea-seed-pass:2']);
|
|
146
|
+
assert.strictEqual(nf.nextSeq(refs, 'artist', 'idea-seed-pass'), 3);
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
// ---- buildProposalFromTemplate ----
|
|
@@ -151,7 +151,7 @@ test('nextSeq: never recycles a shipped/abandoned seq (max over ALL statuses)',
|
|
|
151
151
|
const SAMPLE_TEMPLATE = {
|
|
152
152
|
template_id: 'docs-hygiene-pass',
|
|
153
153
|
version_id: 'GDS-V4',
|
|
154
|
-
discipline: '
|
|
154
|
+
discipline: 'artisan',
|
|
155
155
|
kind: 'cleanup',
|
|
156
156
|
est_minutes: 40,
|
|
157
157
|
credits_reward: 15,
|
|
@@ -163,14 +163,14 @@ const SAMPLE_TEMPLATE = {
|
|
|
163
163
|
};
|
|
164
164
|
|
|
165
165
|
test('buildProposalFromTemplate: carries fields, normalizes touches, sets seq ref', () => {
|
|
166
|
-
const p = nf.buildProposalFromTemplate(SAMPLE_TEMPLATE, 5, { discipline: '
|
|
166
|
+
const p = nf.buildProposalFromTemplate(SAMPLE_TEMPLATE, 5, { discipline: 'artisan' });
|
|
167
167
|
assert.strictEqual(p.title, 'Docs-hygiene sweep', 'trimmed');
|
|
168
168
|
assert.strictEqual(p.version_id, 'GDS-V4');
|
|
169
169
|
assert.strictEqual(p.kind, 'cleanup');
|
|
170
170
|
assert.strictEqual(p.credits_reward, 15);
|
|
171
171
|
assert.strictEqual(p.est_minutes, 40);
|
|
172
172
|
assert.deepStrictEqual(p.touches, ['docs/', '42'], 'empties dropped, coerced to strings');
|
|
173
|
-
assert.strictEqual(p.source_ref, 'tmpl:
|
|
173
|
+
assert.strictEqual(p.source_ref, 'tmpl:artisan:docs-hygiene-pass:5');
|
|
174
174
|
assert.deepStrictEqual(p.done_when, ['a', 'b']);
|
|
175
175
|
});
|
|
176
176
|
|
|
@@ -191,20 +191,22 @@ test('buildProposalFromTemplate: null on a titleless template', () => {
|
|
|
191
191
|
test('buildDescription: folds done-when + evergreen footer', () => {
|
|
192
192
|
const desc = nf.buildDescription(
|
|
193
193
|
{ title: 't', description: 'Body text.', done_when: ['x', 'y'], est_minutes: 30 },
|
|
194
|
-
'
|
|
194
|
+
'artisan'
|
|
195
195
|
);
|
|
196
196
|
assert.ok(desc.includes('Body text.'));
|
|
197
197
|
assert.ok(desc.includes('**Done when:**'));
|
|
198
198
|
assert.ok(desc.includes('- x') && desc.includes('- y'));
|
|
199
199
|
assert.ok(desc.includes('Evergreen newcomer chore'), 'footer present');
|
|
200
|
-
assert.ok(desc.includes('
|
|
200
|
+
assert.ok(desc.includes('artisan'), 'discipline in footer');
|
|
201
201
|
});
|
|
202
202
|
|
|
203
203
|
// ---- loadTemplates: the REAL templates.json is well-formed (multi-chore shape) ----
|
|
204
204
|
|
|
205
205
|
test('loadTemplates: real templates.json is a valid per-discipline list', () => {
|
|
206
206
|
const templates = nf.loadTemplates(path.join(__dirname, 'newcomer-floor.templates.json'));
|
|
207
|
-
|
|
207
|
+
// task 1005141: two crafts, not three — ideator folded into artist, engineer is artisan.
|
|
208
|
+
assert.ok(!('engineer' in templates) && !('ideator' in templates), 'no old-spelling keys remain');
|
|
209
|
+
for (const disc of ['artisan', 'artist']) {
|
|
208
210
|
const list = templates[disc];
|
|
209
211
|
assert.ok(Array.isArray(list) && list.length >= 1, `${disc} is a non-empty list`);
|
|
210
212
|
for (const t of list) {
|
|
@@ -219,15 +221,22 @@ test('loadTemplates: real templates.json is a valid per-discipline list', () =>
|
|
|
219
221
|
}
|
|
220
222
|
});
|
|
221
223
|
|
|
222
|
-
test('loadTemplates:
|
|
224
|
+
test('loadTemplates: artist holds BOTH the asset chore and the (former ideator) idea-seed chore', () => {
|
|
223
225
|
const templates = nf.loadTemplates(path.join(__dirname, 'newcomer-floor.templates.json'));
|
|
224
|
-
const ids = templates.
|
|
226
|
+
const ids = templates.artist.map((t) => t.template_id);
|
|
227
|
+
assert.ok(ids.includes('asset-generate-pass'), 'the asset chore is present');
|
|
228
|
+
assert.ok(ids.includes('idea-seed-pass'), 'the idea chore moved under artist');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('loadTemplates: artisan offers BOTH the unit-test chore and the docs sweep', () => {
|
|
232
|
+
const templates = nf.loadTemplates(path.join(__dirname, 'newcomer-floor.templates.json'));
|
|
233
|
+
const ids = templates.artisan.map((t) => t.template_id);
|
|
225
234
|
assert.ok(ids.includes('unit-test-pass'), 'the safe coding chore is present');
|
|
226
235
|
assert.ok(ids.includes('docs-hygiene-pass'), 'the docs sweep is retained');
|
|
227
236
|
// every engineer chore touches only safe (non-protected) surfaces by design:
|
|
228
237
|
// touches[] is empty (git-merge is the collision detector), so this asserts the
|
|
229
238
|
// contract that engineer chores carry no protected-path touches.
|
|
230
|
-
for (const t of templates.
|
|
239
|
+
for (const t of templates.artisan) {
|
|
231
240
|
assert.deepStrictEqual(t.touches || [], [], `${t.template_id} declares no touches[]`);
|
|
232
241
|
}
|
|
233
242
|
});
|
|
@@ -123,10 +123,11 @@ function checkRolePacks(fsImpl = fs) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// The split's own shape, asserted so a later edit cannot quietly drop a craft
|
|
126
|
-
// back into the kernel:
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
// back into the kernel: two core packs since the game conversion folded the
|
|
127
|
+
// ideator into the artist (task 1005141, BONGOS-V3) — Artisan and Artist; Patron
|
|
128
|
+
// is a character, not a task craft, and has no pack.
|
|
129
|
+
if (packCount < 2) {
|
|
130
|
+
violations.push(`only ${packCount} craft(s) name a pack — the split is one kernel plus two packs (Artisan, Artist). A craft with no pack has nowhere to keep what the role-neutral kernel does not.`);
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
return {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"title": "Spike: where the welcome checklist is stored",
|
|
11
11
|
"description": "Output an ADR naming the table and who writes it.",
|
|
12
12
|
"kind": "spike",
|
|
13
|
-
"discipline": "
|
|
13
|
+
"discipline": "artisan",
|
|
14
14
|
"priority": 4,
|
|
15
15
|
"est_minutes": 90,
|
|
16
16
|
"criterion_ids": ["newcomer-knows-what-to-do-next"]
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"title": "The welcome checklist API",
|
|
21
21
|
"description": "Build to R01's ADR. Done-when: a new builder's checklist is returned by one GET.",
|
|
22
22
|
"kind": "feature",
|
|
23
|
-
"discipline": "
|
|
23
|
+
"discipline": "artisan",
|
|
24
24
|
"priority": 4,
|
|
25
25
|
"est_minutes": 180,
|
|
26
26
|
"criterion_ids": ["newcomer-knows-what-to-do-next"],
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"title": "Proof: a newcomer finds their next step without asking",
|
|
43
43
|
"description": "A brand-new builder signs in and finds what to do next unaided.",
|
|
44
44
|
"kind": "verify",
|
|
45
|
-
"discipline": "
|
|
45
|
+
"discipline": "artisan",
|
|
46
46
|
"priority": 3,
|
|
47
47
|
"est_minutes": 60,
|
|
48
48
|
"criterion_ids": ["newcomer-knows-what-to-do-next"],
|