@bongos/core 1.17.0 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/.bongos-core.json +363 -312
  2. package/.claude/skills/blocker-review/SKILL.md +1 -1
  3. package/.claude/skills/builder-claim/SKILL.md +2 -0
  4. package/.claude/skills/builder-exit/SKILL.md +2 -2
  5. package/.claude/skills/builder-redteam/SKILL.md +3 -3
  6. package/.claude/skills/builder-setup/SKILL.md +2 -2
  7. package/.claude/skills/builder-start/SKILL.md +1 -1
  8. package/.claude/skills/dev/SKILL.md +1 -1
  9. package/.claude/skills/idea-triage/SKILL.md +2 -2
  10. package/.claude/skills/ideate/SKILL.md +1 -1
  11. package/.claude/skills/merge-mode/SKILL.md +1 -1
  12. package/.claude/skills/new-project/SKILL.md +3 -0
  13. package/.claude/skills/paint/SKILL.md +1 -1
  14. package/CLAUDE.md +11 -238
  15. package/GOVERNANCE.md +1 -1
  16. package/README.md +1 -1
  17. package/bin/bongos.js +1 -0
  18. package/clients/bongos-client/README.md +4 -4
  19. package/clients/bongos-client/bongos-client.global.js +14 -1
  20. package/clients/bongos-client/index.cjs +14 -1
  21. package/clients/bongos-client/index.d.ts +19 -2
  22. package/clients/bongos-client/index.mjs +11 -1
  23. package/clients/bongos-client/package.json +1 -1
  24. package/docs/adr/0001-stack-choice-single-vps.md +1 -1
  25. package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
  26. package/docs/adr/0019-red-team-bounty-payout-table.md +3 -3
  27. package/docs/adr/0020-security-model-public-stub.md +1 -1
  28. package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +1 -1
  29. package/docs/adr/0024-cloneable-repo-local-first-memory.md +1 -1
  30. package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +4 -4
  31. package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +3 -3
  32. package/docs/adr/0030-builders-hall-live-updates.md +1 -1
  33. package/docs/adr/0031-cloud-dev-environments-for-builders.md +4 -4
  34. package/docs/adr/0032-discord-mirror-of-gds.md +2 -2
  35. package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
  36. package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
  37. package/docs/adr/0044-per-box-live-game-preview.md +1 -1
  38. package/docs/adr/0054-session-token-reward-cost-plus.md +10 -10
  39. package/docs/adr/0057-container-cost-ledger.md +3 -3
  40. package/docs/adr/0062-medusa-instance-model-decoupling.md +4 -4
  41. package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +2 -2
  42. package/docs/adr/0085-remove-default-per-builder-spend-cap.md +1 -1
  43. package/docs/adr/0086-goal-scoped-work-hierarchy.md +1 -1
  44. package/docs/adr/0096-require-reward-before-workable.md +9 -9
  45. package/docs/adr/0097-retroactive-reward-backfill.md +3 -3
  46. package/docs/adr/0100-two-repo-instance-model.md +1 -1
  47. package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +3 -3
  48. package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +3 -3
  49. package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +1 -1
  50. package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +6 -6
  51. package/docs/adr/0133-github-app-manifest-onboarding-signin.md +2 -0
  52. package/docs/adr/0134-private-first-npm-distribution.md +20 -6
  53. package/docs/adr/0135-module-upstream-submission-interim-queue.md +34 -0
  54. package/docs/adr/0136-update-channel-subscription-policy.md +51 -0
  55. package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +40 -0
  56. package/docs/adr/0138-bundled-module-deps-declared-per-module.md +45 -0
  57. package/docs/adr/README.md +14 -10
  58. package/docs/api/openapi.json +315 -6
  59. package/docs/api-reference.md +10 -5
  60. package/docs/architecture.md +3 -3
  61. package/docs/branding-contract.md +5 -4
  62. package/docs/canonical-permissions.md +1 -1
  63. package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
  64. package/docs/file-map.md +20 -19
  65. package/docs/handoff-template.md +2 -2
  66. package/docs/modules-contract.md +8 -1
  67. package/docs/onboarding/diagrams/README.md +7 -7
  68. package/docs/onboarding/drachmae-and-karma.md +18 -18
  69. package/docs/onboarding/primer.md +9 -9
  70. package/docs/recipes/audit-replay.md +2 -2
  71. package/docs/recipes/builder-box-lifecycle.md +2 -2
  72. package/docs/recipes/claude-design-loop.md +1 -1
  73. package/docs/recipes/instance-deploy.md +4 -4
  74. package/docs/recipes/instance-first-run.md +51 -0
  75. package/docs/recipes/ops-gotchas.md +1 -1
  76. package/docs/recipes/packaging-the-core.md +5 -5
  77. package/docs/recipes/private-npm-distribution.md +39 -12
  78. package/docs/recipes/self-host.md +1 -1
  79. package/docs/recipes/staging-environment.md +1 -1
  80. package/docs/recipes/upgrading-the-core.md +63 -8
  81. package/docs/research/cursor-competitive-analysis.md +1 -1
  82. package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
  83. package/migrations/core_191_module_submissions.sql +45 -0
  84. package/modules/autonomy/CLAUDE.md +11 -45
  85. package/modules/builder-settings/CLAUDE.md +11 -54
  86. package/modules/dev-box/CLAUDE.md +11 -34
  87. package/modules/discord/CLAUDE.md +11 -46
  88. package/modules/discord/module.json +2 -1
  89. package/modules/economy/CLAUDE.md +11 -44
  90. package/modules/economy/achievements.js +9 -1
  91. package/modules/economy/credits.js +10 -1
  92. package/modules/grading/CLAUDE.md +11 -38
  93. package/modules/hall-ui/CLAUDE.md +11 -57
  94. package/modules/hall-ui/public/builders.js +71 -4
  95. package/modules/hall-ui/public/goal-inbox.js +191 -0
  96. package/modules/hall-ui/public/goals-page.js +227 -22
  97. package/modules/hall-ui/public/goals.html +16 -0
  98. package/modules/hall-ui/public/hall-widgets.js +14 -1
  99. package/modules/hall-ui/public/index.html +3 -0
  100. package/modules/hall-ui/public/new-project-wizard.js +6 -4
  101. package/modules/hall-ui/public/style.css +43 -0
  102. package/modules/ideas/CLAUDE.md +11 -33
  103. package/modules/lifecycle/CLAUDE.md +11 -59
  104. package/modules/lifecycle/routes/goals.js +80 -0
  105. package/modules/memory/CLAUDE.md +11 -36
  106. package/modules/onboarding/CLAUDE.md +11 -51
  107. package/modules/provisioning/CLAUDE.md +15 -0
  108. package/modules/provisioning/migrations/provisioning_001_tables.sql +160 -0
  109. package/modules/provisioning/migrations/provisioning_002_standalone_shape.sql +11 -0
  110. package/modules/provisioning/migrations/provisioning_003_github_app_manifest.sql +95 -0
  111. package/modules/provisioning/module.json +17 -0
  112. package/modules/provisioning/onboard-plan.js +37 -0
  113. package/modules/provisioning/provisioning.js +539 -0
  114. package/modules/provisioning/rate-limit.js +35 -0
  115. package/modules/provisioning/routes/provisioning.js +607 -0
  116. package/modules/provisioning/tests/provisioning.mjs +232 -0
  117. package/modules/provisioning/ui/.gitkeep +0 -0
  118. package/modules/security/CLAUDE.md +11 -40
  119. package/modules/security/secret-scrub.js +10 -0
  120. package/modules/sessions/CLAUDE.md +11 -35
  121. package/modules/status-ui/CLAUDE.md +11 -43
  122. package/package-lock.json +2 -2
  123. package/package.json +7 -2
  124. package/public-landing/create.html +58 -12
  125. package/public-landing/index.html +40 -21
  126. package/scripts/discord/README.md +1 -1
  127. package/scripts/gds/CLAUDE.md +11 -140
  128. package/scripts/gds/claim.js +98 -48
  129. package/scripts/gds/claude-materialize.js +174 -20
  130. package/scripts/gds/claude-md-template.js +84 -0
  131. package/scripts/gds/cli-lib.js +112 -2
  132. package/scripts/gds/cutover-codemod.js +241 -0
  133. package/scripts/gds/fitness.js +54 -0
  134. package/scripts/gds/gen-api-client.js +10 -10
  135. package/scripts/gds/init.js +112 -16
  136. package/scripts/gds/lint-infra-exec.js +70 -0
  137. package/scripts/gds/lint-workflow-exec.js +63 -0
  138. package/scripts/gds/migration-namespace.js +1 -1
  139. package/scripts/gds/mirror-lineage-check.js +14 -5
  140. package/scripts/gds/mirror-redact.js +8 -0
  141. package/scripts/gds/module.js +118 -4
  142. package/scripts/gds/npmrc.js +93 -0
  143. package/scripts/gds/onboard.js +17 -24
  144. package/scripts/gds/package-core.js +74 -17
  145. package/scripts/gds/provision.js +102 -17
  146. package/scripts/gds/publish-manifest.js +60 -2
  147. package/scripts/gds/run-routine.js +51 -13
  148. package/scripts/gds/run-unit-tests.js +4 -0
  149. package/scripts/gds/seed-adopt-tasks.js +1 -1
  150. package/scripts/gds/status.js +6 -1
  151. package/scripts/gds/task.js +166 -0
  152. package/scripts/gds/update-channel.js +186 -0
  153. package/scripts/gds/upgrade.js +172 -25
  154. package/src/bongos/CLAUDE.md +11 -60
  155. package/src/bongos/auth.js +24 -0
  156. package/src/bongos/module-submissions.js +56 -0
  157. package/src/bongos/route-rank-check.js +6 -0
  158. package/src/bongos/routes/CLAUDE.md +11 -39
  159. package/src/bongos/routes/auth.js +6 -1
  160. package/src/bongos/routes/modules.js +123 -21
  161. package/src/bongos/serve-internal.js +10 -1
  162. package/src/branding.js +1 -0
  163. package/src/instance-config.js +1 -1
  164. package/src/module-api.js +24 -1
  165. package/src/module-loader/loader.js +36 -4
  166. package/src/module-loader/manifest-schema.js +16 -1
  167. package/tests/branding.mjs +11 -0
  168. package/tests/claim_error_surface.mjs +107 -0
  169. package/tests/claude_materialize.mjs +125 -0
  170. package/tests/consumer_layout_boot.mjs +302 -0
  171. package/tests/cutover_codemod.mjs +184 -0
  172. package/tests/device_flow_start.mjs +96 -0
  173. package/tests/first_run_engine_check.mjs +79 -0
  174. package/tests/goal_membership_requests.mjs +33 -3
  175. package/tests/hook_core_resolve.mjs +125 -0
  176. package/tests/hook_user_agents.mjs +62 -0
  177. package/tests/infra_exec_paths.mjs +104 -0
  178. package/tests/init.mjs +106 -6
  179. package/tests/instance_mismatch_guard.mjs +101 -0
  180. package/tests/mirror_lineage_proof.mjs +14 -0
  181. package/tests/module_cli.mjs +130 -2
  182. package/tests/module_loader.mjs +42 -0
  183. package/tests/module_manifest.mjs +19 -0
  184. package/tests/module_submissions.mjs +135 -0
  185. package/tests/npmrc.mjs +105 -0
  186. package/tests/onboard.mjs +62 -0
  187. package/tests/package_core.mjs +76 -22
  188. package/tests/platform_boot.mjs +5 -2
  189. package/tests/provision.mjs +107 -8
  190. package/tests/provisioning_github_app_routes.mjs +44 -2
  191. package/tests/publish_manifest.mjs +52 -1
  192. package/tests/run_routine.mjs +26 -1
  193. package/tests/run_routine_two_root.mjs +77 -0
  194. package/tests/scrubber_corpus.mjs +17 -0
  195. package/tests/served_paths_shipped.mjs +2 -2
  196. package/tests/task_cli.mjs +99 -0
  197. package/tests/update_channel.mjs +186 -0
  198. package/tests/upgrade.mjs +220 -25
  199. package/tests/workflow_exec_paths.mjs +98 -0
  200. package/docs/design/container-exterior-tiles.md +0 -49
  201. package/docs/design/container-exterior.md +0 -81
  202. package/docs/design/container-interior.md +0 -77
  203. package/docs/design/world-map-scroll.md +0 -41
  204. package/docs/recipes/claude-gemini-bongos-doors.md +0 -155
  205. package/docs/recipes/cloudbongos-staging-gate.md +0 -139
  206. package/docs/recipes/cloudbongos-standup.md +0 -111
  207. package/docs/recipes/parallel-load-results-2026-05-30.md +0 -63
  208. package/docs/recipes/parallel-load-test.md +0 -32
  209. package/docs/recipes/pixel-art-pipeline.md +0 -411
  210. package/docs/recipes/staging-data.md +0 -50
  211. package/migrations/instance/018_pms_v3_planning_seed.sql +0 -131
  212. package/migrations/instance/080_gds_v4_planning_seed.sql +0 -75
  213. package/migrations/instance/110_medusa_planning_seed.sql +0 -70
  214. package/migrations/instance/120_bongos_v1_planning_seed.sql +0 -69
  215. package/migrations/instance/144_bongos_v1_module_system_criterion.sql +0 -26
  216. package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +0 -29
  217. package/migrations/instance/172_v2_world_planning_seed.sql +0 -163
  218. package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +0 -33
  219. package/migrations/instance/182_gdsv4_bongos_consolidation.sql +0 -132
  220. package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +0 -116
  221. package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +0 -69
  222. package/scripts/gds/art-stage.js +0 -152
  223. package/server.js +0 -212
  224. package/tests/art_key.mjs +0 -121
  225. package/tests/day_cycle.mjs +0 -197
  226. package/tests/snapshot_picker.mjs +0 -233
  227. package/tests/sprite_facing.mjs +0 -35
  228. package/tests/terrain_picker.mjs +0 -298
  229. package/tests/turn_in_place.mjs +0 -280
  230. package/tests/walkability.mjs +0 -188
@@ -9,7 +9,7 @@
9
9
 
10
10
  ## Context
11
11
 
12
- [ADR 0062](<redacted>.md) decoupled the GDS into **Cloud Bongos** — a configurable, self-hostable build platform where AI agents claim tasks and ship code, with a human owner setting scope and the hard gates. Example (the Off the Boats game) becomes **customer 1** running on it; Cloud Bongos runs on its own plain vanilla brand as **customer 0** and builds itself.
12
+ [ADR 0062](<redacted>.md) decoupled the GDS into **Cloud Bongos** — a configurable, self-hostable build platform where AI agents claim tasks and ship code, with a human owner setting scope and the hard gates. Example (the Example game) becomes **customer 1** running on it; Cloud Bongos runs on its own plain vanilla brand as **customer 0** and builds itself.
13
13
 
14
14
  Criterion **C9 (`medusa-open-and-protected`)** commits Cloud Bongos to being *open, free, and protected from capture*: released to the public as open source on a deliberate, delayed cadence. Three questions had to be answered before any of R69–R73 could be built, because each is downstream of the answer:
15
15
 
@@ -34,7 +34,7 @@ The license covers the **publishable subset only** — the portable methodology
34
34
  The project is run as a **non-profit**: it is **not monetized from its contributors**, and it is structured so no single party can capture it.
35
35
 
36
36
  - No contributor pays to contribute, and no contribution is resold as a proprietary add-on by the project.
37
- - The drachmae/credit economy ([ADR 0054](<redacted>.md)) is an **instance-internal** mechanism — it rewards builders on a given instance out of that instance's own budget; it is not the project monetizing the commons. It ships as a feature an instance configures, not as a revenue stream flowing to a central owner.
37
+ - The example/credit economy ([ADR 0054](<redacted>.md)) is an **instance-internal** mechanism — it rewards builders on a given instance out of that instance's own budget; it is not the project monetizing the commons. It ships as a feature an instance configures, not as a revenue stream flowing to a central owner.
38
38
  - The AGPL is the legal backstop; the non-profit posture is the social one. Together they are the "protected from capture" half of C9.
39
39
 
40
40
  Optional **managed-hosting** (operating instances for owners) was captured as a post-C8 idea in the planning session. If it is ever built, it is a service offered *on top of* the same AGPL artifact — it does not change the artifact's license or the non-profit posture of the platform itself.
@@ -36,4 +36,4 @@ No schema change (`monthly_budget_usd` stays; `NULL` now means "uncapped" instea
36
36
  - **The brake survives.** An Archon can still cap a specific runaway builder by setting a positive `monthly_budget_usd`; clearing it (`null`) returns them to uncapped.
37
37
  - **Cost stays honest.** Spend attribution, the savings ledger, and the public dashboard are untouched — we still see exactly where the $5K goes; we just don't gate work on it.
38
38
  - **Project-wide guards are unaffected.** The separate autonomous-fleet spend brake (`autonomy-gate.js`, `OTB_AUTONOMY_CEILING_USD`) and the art-pipeline `HARD_STOP_USD` ledger cap are independent mechanisms and remain in force.
39
- - **C3's intent narrows** from "every builder has a default ceiling" to "a ceiling is an opt-in, per-builder Archon tool." The onboarding drachmae/karma diagram note is updated to match.
39
+ - **C3's intent narrows** from "every builder has a default ceiling" to "a ceiling is an opt-in, per-builder Archon tool." The onboarding example/karma diagram note is updated to match.
@@ -82,7 +82,7 @@ The create-time gate sees only *declared* touches (advisory — git merge is the
82
82
 
83
83
  ### Deferred (explicit non-decisions)
84
84
 
85
- - **Rewards** — whether *achieving a goal* earns anything beyond the per-task cost-plus already paid ([ADR 0054](<redacted>.md)), and how any reward splits among a goal's members. The leaning is karma by default (budget-safe, since drachmae are real money) with contribution-weighted splits, but this is **not yet decided** and rides a follow-on.
85
+ - **Rewards** — whether *achieving a goal* earns anything beyond the per-task cost-plus already paid ([ADR 0054](<redacted>.md)), and how any reward splits among a goal's members. The leaning is karma by default (budget-safe, since example are real money) with contribution-weighted splits, but this is **not yet decided** and rides a follow-on.
86
86
  - **A sub-Metic builder joining a protected-scope goal** — letting a newcomer work *inside* the dangerous core under a tightly-scoped, Archon-granted goal amends the [ADR 0043](<redacted>.md) hard floor (and needs the ship-time gates to become goal-aware). Deliberately **last**, behind its own security-reviewed ADR. Until then, protected-scope goals require a Metic+ holder.
87
87
  - **An AI relevance / anti-hack judge** at create/claim time (reusing the grader subagent harness; advisory-first, flag-for-Metic, never hard-blocking a newcomer) — a later phase; the deterministic scope + protected-path gates ship first.
88
88
 
@@ -1,4 +1,4 @@
1
- # ADR 0096 — Require a `credits_reward` (drachmae) value before a task is workable
1
+ # ADR 0096 — Require a `credits_reward` (example) value before a task is workable
2
2
 
3
3
  **Date:** 2026-06-28
4
4
  **Status:** Accepted
@@ -7,12 +7,12 @@
7
7
 
8
8
  ## Context — the problem
9
9
 
10
- Every task carries a `credits_reward` integer — the in-world drachmae payout when it
10
+ Every task carries a `credits_reward` integer — the in-world example payout when it
11
11
  ships ([ADR 0023](<redacted>.md), [ADR 0054](<redacted>.md)).
12
12
  That column was created with `NOT NULL DEFAULT 0` (migration `003_pms.sql`). Nothing in
13
13
  the lifecycle ever required it to be set, so the default path was:
14
14
 
15
- > create a task (reward = 0) → promote it to `ready` → claim → ship → **pay 0 drachmae**.
15
+ > create a task (reward = 0) → promote it to `ready` → claim → ship → **pay 0 example**.
16
16
 
17
17
  A task could travel the whole claim→ship→credit pipeline paying nothing. The credit
18
18
  ledger then records a real shipped task with a zero payout — the reward economy is
@@ -96,7 +96,7 @@ complete and no zero-reward `ready` tasks remain.
96
96
 
97
97
  ## Consequences
98
98
 
99
- - No task created/promoted/claimed after this change can be worked while paying 0 drachmae.
99
+ - No task created/promoted/claimed after this change can be worked while paying 0 example.
100
100
  - Existing `ready` + in-flight zero-reward tasks stay fully workable (grandfathered).
101
101
  - The promote/claim refusals name the way forward (suggested value + the `PATCH` call),
102
102
  matching the `TASK_NOT_READY` actionable-hint precedent ([#952](https://example.com/builders#/task/952)).
@@ -112,7 +112,7 @@ The original decision above made a missing reward **block** the two workable tra
112
112
  that turned a missing-value into a hard wall: a perfectly good task with a deps-met,
113
113
  ready-to-work footprint could not be promoted or claimed until someone hand-set a value
114
114
  first. That is friction the methodology doesn't want — the goal is "no task is worked for
115
- 0 drachmae", not "stop work until a human types a number."
115
+ 0 example", not "stop work until a human types a number."
116
116
 
117
117
  **The shift: the gate now AUTO-ASSIGNS a fair, capped value and PROCEEDS.**
118
118
 
@@ -143,7 +143,7 @@ author-set precisely because auto-deriving it from `est_minutes` is gameable (in
143
143
  estimate → inflate the pay). Auto-assigning at the gate crosses that line — so two guards
144
144
  contain the blast radius:
145
145
 
146
- 1. **CAP.** `suggestCreditsReward` is capped at `REWARD_SUGGESTION_CAP = 60` drachmae. The
146
+ 1. **CAP.** `suggestCreditsReward` is capped at `REWARD_SUGGESTION_CAP = 60` example. The
147
147
  formula stays modest (~1 drachma/estimated-minute, weighted by a coarse per-kind factor,
148
148
  floored at 5), and the cap bounds how far an inflated estimate can push the *auto-assigned*
149
149
  value. Anyone who genuinely needs more sets `credits_reward` by hand via `PATCH /tasks/:id`
@@ -161,7 +161,7 @@ contain the blast radius:
161
161
 
162
162
  ### Net effect
163
163
 
164
- No task is ever worked for 0 drachmae **and** no promote/claim is ever blocked for a missing
164
+ No task is ever worked for 0 example **and** no promote/claim is ever blocked for a missing
165
165
  value. The friction is gone; the economy stays funded; the gameable surface is a capped,
166
166
  audited floor sitting under the ungameable cost-plus primary reward.
167
167
 
@@ -170,7 +170,7 @@ audited floor sitting under the ungameable cost-plus primary reward.
170
170
  The first amendment moved assignment to the two *workable* transitions (promote, claim). That
171
171
  still left a window: a freshly **created** `backlog` task read `credits_reward = 0` until it was
172
172
  later promoted or claimed. The owner's intent is stronger — **every task should carry a fair
173
- drachmae value from the moment it is born**, so anyone reading the backlog sees real numbers, not
173
+ example value from the moment it is born**, so anyone reading the backlog sees real numbers, not
174
174
  zeros waiting to be filled in.
175
175
 
176
176
  **The shift: creation is now the PRIMARY assignment point; promote/claim becomes the backstop.**
@@ -204,6 +204,6 @@ just now lands at birth instead of at first-work.
204
204
 
205
205
  ### Net effect (post-1677)
206
206
 
207
- Every task is born with a fair, capped drachmae value. The backlog never shows a placeholder zero;
207
+ Every task is born with a fair, capped example value. The backlog never shows a placeholder zero;
208
208
  promote/claim no longer carries the assignment weight (it's a backstop); the economy stays funded
209
209
  end-to-end with the same anti-gaming ceiling.
@@ -1,4 +1,4 @@
1
- # ADR 0097 — Retroactive drachmae backfill for shipped 0-reward tasks
1
+ # ADR 0097 — Retroactive example backfill for shipped 0-reward tasks
2
2
 
3
3
  - **Date:** 2026-06-28
4
4
  - **Status:** Accepted
@@ -11,7 +11,7 @@
11
11
  A task's `credits_reward` column **defaults to 0**. The per-task ship credit is
12
12
  minted by `modules/economy/credits.js insertMultipliedConfirmCredit`, which
13
13
  computes `applyKindMultiplier(credits_reward, kind)` — so a task that shipped
14
- with `credits_reward = 0` (or NULL) paid `0 × multiplier = 0` drachmae. Many
14
+ with `credits_reward = 0` (or NULL) paid `0 × multiplier = 0` example. Many
15
15
  early ships, and most dev-box / CI agent ships, never had a fair per-task reward
16
16
  set, so the builders who did real, shipped work earned nothing for it on the
17
17
  per-task stream.
@@ -28,7 +28,7 @@ There are **two reward streams** a builder accrues to `builders.total_credits`:
28
28
  `priceModelUsage` returns ~$0 and the reward rounds to 0. So for exactly the
29
29
  ships that missed the per-task reward, the cost-plus stream *also* paid nothing.
30
30
 
31
- The net effect: a real backlog of shipped work that minted no drachmae for its
31
+ The net effect: a real backlog of shipped work that minted no example for its
32
32
  builders. This ADR records the decision (and the tool) to remediate that **once,
33
33
  fairly, and safely** — with an Archon authorizing the amounts.
34
34
 
@@ -16,7 +16,7 @@ ADR 0062 drew the **logical** cut-line — portable core / host instance / gated
16
16
 
17
17
  Two owner priorities now push past the single-repo model:
18
18
 
19
- - **Mercury** — Example' internal platform — should be "its own Cloud Bongos project": not code living inside the Off the Boats repo, and not merely another reskin of one checkout.
19
+ - **Mercury** — Example' internal platform — should be "its own Cloud Bongos project": not code living inside the Example repo, and not merely another reskin of one checkout.
20
20
  - **Core improvements should flow to future projects** on each project's own schedule — an explicit "update to the latest platform" capability.
21
21
 
22
22
  The 2026-06-26 gap analysis (planning [#1604](https://example.com/builders#/task/1604)) found the logical boundary ~70% built and CI-enforced. Since then the load-bearing pieces landed: the bounded kernel ([#1570](https://example.com/builders#/task/1570)), the completed Config-B core-carve ([#1578](https://example.com/builders#/task/1578) + ADR 0093 — BONGOS-V1 C7 now satisfied), the hall-widget registry ([#1612](https://example.com/builders#/task/1612)), and core de-branding ([#1613](https://example.com/builders#/task/1613)). The boundary now holds, so a split is **mechanical, not a leap** — which is exactly the precondition this gate ([#1607](https://example.com/builders#/task/1607)) was told to wait for.
@@ -57,11 +57,11 @@ We adopt a three-part design. Each part is an independent, revertible build task
57
57
 
58
58
  **Leaning (recorded; the money is owner-gated).**
59
59
 
60
- - **Karma by default, not drachmae.** Drachmae are real money (1 = $1) against a tight $5k budget, and a goal is a *coordination construct*, not a unit of billable work — the per-task cost-plus already pays for the actual shipping. A goal-achievement **karma** bonus recognizes the collaboration with **no new monetary spend**.
60
+ - **Karma by default, not example.** Example are real money (1 = $1) against a tight $5k budget, and a goal is a *coordination construct*, not a unit of billable work — the per-task cost-plus already pays for the actual shipping. A goal-achievement **karma** bonus recognizes the collaboration with **no new monetary spend**.
61
61
  - **Contribution-weighted, not membership-flat.** The bonus tracks **who actually shipped tasks under the goal** (reuse the shipped-task/credit attribution + the BFG contribution signals, [ADR 0027](<redacted>.md)), never mere membership — a flat per-member reward would incentivize goal-squatting. A `lead` may take a small coordination share.
62
62
  - **On achievement, not creation.** The reward attaches when a goal is **achieved** (all criteria satisfied + confirmed via `/goal-review`), never on creation or joining — which, with contribution-weighting and the Metic confirmation, closes the free-rider and squatting surfaces.
63
63
 
64
- **Owner-gated.** Whether *any* monetary (drachmae) component attaches, and the exact karma amount/curve, is **Lars's call at build time** — money is owner authority. This ADR records the budget-safe default (karma-only, contribution-weighted); the build task (P5.3) surfaces the money decision explicitly rather than presuming it.
64
+ **Owner-gated.** Whether *any* monetary (example) component attaches, and the exact karma amount/curve, is **Lars's call at build time** — money is owner authority. This ADR records the budget-safe default (karma-only, contribution-weighted); the build task (P5.3) surfaces the money decision explicitly rather than presuming it.
65
65
 
66
66
  ### The child task list (filed backlog under BONGOS-V1, `source_ref` = this ADR)
67
67
 
@@ -76,7 +76,7 @@ We adopt a three-part design. Each part is an independent, revertible build task
76
76
  - **A hard-blocking relevance judge — rejected.** A fallible LLM as a hard gate re-introduces the bottleneck ADR 0086 removed and creates a gaming/DoS surface; the deterministic gates are the hard wall, the judge is a soft signal.
77
77
  - **Keep protected-scope Metic+-only forever — rejected as the long-term posture** (it caps the maneuverability grant's reach), but it **is** the safe default that stays in force until P5.2 ships with its security review.
78
78
  - **Flat per-member goal reward — rejected.** It incentivizes goal-squatting; contribution-weighting is fairer and anti-gaming.
79
- - **A drachmae goal bonus by default — rejected as the default** (real money, tight budget); left as an explicit owner-gated option, not presumed.
79
+ - **A example goal bonus by default — rejected as the default** (real money, tight budget); left as an explicit owner-gated option, not presumed.
80
80
  - **Fold Phase 5 into Phase 4 rather than its own ADR — rejected** (ADR 0086 already decided this): the boundary-widening warrants a standalone security review, which is this document.
81
81
 
82
82
  ---
@@ -17,7 +17,7 @@ A next-day owner review (2026-07-02, task [#1726](https://example.com/builders#/
17
17
 
18
18
  - **Cloud Bongos is the real product** — the thing of durable value — and should live as a clean standalone artifact, with Example as *one consumer of it*, not its host.
19
19
  - **The coupling actively hurts.** One repo carrying both the game and the platform, with a convoluted shared history, is hard to reason about and hand off.
20
- - **The boundary stays theoretical while Example is coupled.** OTB specifics keep leaking into the "portable" core. Evidence: a walk of the ~105 accumulated memory-file learnings on 2026-07-02 found **~85% are platform knowledge wearing OTB-specific framing** (example.com links, the droplet, `drachmae`) — the substance is core, the *wording* is coupled. Only 7 items are genuinely instance-bound.
20
+ - **The boundary stays theoretical while Example is coupled.** OTB specifics keep leaking into the "portable" core. Evidence: a walk of the ~105 accumulated memory-file learnings on 2026-07-02 found **~85% are platform knowledge wearing OTB-specific framing** (example.com links, the droplet, `example`) — the substance is core, the *wording* is coupled. Only 7 items are genuinely instance-bound.
21
21
  - **Mercury is imminent** — the separation should be proven on something that matters to the business now.
22
22
 
23
23
  "Leave Example coupled until later" reads, under these priorities, as "never." The precondition ADR 0100 waited for — a mechanical, CI-enforced boundary — already holds (ADR 0093 core-carve, the module system, `isPublishable()`), so the more ambitious cut is now buildable rather than a leap.
@@ -65,6 +65,6 @@ Each repo runs its own GDS (own DB, tasks, builders, credits) — a shared GDS i
65
65
  §1 is done. The core was extracted into **[github.com/example-owner/cloud-bongos](https://github.com/example-owner/cloud-bongos)** — **private**, `main`, **AGPL-3.0**, **fresh single-commit history**.
66
66
 
67
67
  - **What was extracted:** the `isPublishable()` subtree via R84's `scripts/gds/package-core.js` — **989 core files** (of 2203), redacted in-memory through the fail-closed no-leak gate (0 residual leaks). The pin travels in-repo as `.cloudbongos-core.json`: `core_version 1.14.0`, `source_commit 2783a396…`, `tree_sha256 2bf9f8ee9a97…`. R85 ([#1689](https://example.com/builders#/task/1689)) and R89 ([#1728](https://example.com/builders#/task/1728)) can now depend on this pin.
68
- - **Genericized on the way in:** identity surface branded to Cloud Bongos (README, package.json, CLAUDE.md 298→258 lines, GOVERNANCE); instance identity re-pointed to `docs/project-context.template.md` + `config/branding.neutral.json`; `drachmae→credits`, owner-name and Off-the-Boats/ancient-Greece framing removed.
68
+ - **Genericized on the way in:** identity surface branded to Cloud Bongos (README, package.json, CLAUDE.md 298→258 lines, GOVERNANCE); instance identity re-pointed to `docs/project-context.template.md` + `config/branding.neutral.json`; `example→credits`, owner-name and Off-the-Boats/ancient-Greece framing removed.
69
69
  - **§4 mirror re-evaluation (ADR 0099):** the physical-extraction trigger has now fired, and `cloud-bongos` is the **designated canonical public artifact** — but it is **PRIVATE for now**. So the ADR 0099 generated mirror is **NOT retired yet**; it stays the public surface until the owner flips `cloud-bongos` public, at which point the mirror is retired (or repointed). Retirement is bound to the public-flip, not to this extraction.
70
- - **Deferred to follow-ups (tracked in the GDS):** the exhaustive `Off the Boats`→neutral sweep across the ~32 remaining ADR/test/recipe files; standalone vanilla-boot wiring (`server.js`/entrypoints still reference the reference instance's product layer) — naturally proven by R85/R89; core `package.json` dependency pruning (game deps carried in); dangling in-repo links to topology/instance paths the scaffold populates on `init`; seeding the CORE-bucketed learnings; and tagging R88/R89 to goal 26 (their `goal_id` is null).
70
+ - **Deferred to follow-ups (tracked in the GDS):** the exhaustive `Example`→neutral sweep across the ~32 remaining ADR/test/recipe files; standalone vanilla-boot wiring (`server.js`/entrypoints still reference the reference instance's product layer) — naturally proven by R85/R89; core `package.json` dependency pruning (game deps carried in); dangling in-repo links to topology/instance paths the scaffold populates on `init`; seeding the CORE-bucketed learnings; and tagging R88/R89 to goal 26 (their `goal_id` is null).
@@ -32,7 +32,7 @@ Each repo runs its own GDS with its own builder identities ([ADR 0100](<redacted
32
32
 
33
33
  ### 3. Reward: credit now, money later.
34
34
 
35
- Accepted contributions earn **visible credit** (author + origin in the catalog) immediately. Any monetary reward (drachmae) — and the cross-instance identity/payout mechanics it would require — is **explicitly deferred** to a separate economics decision (owner call, 2026-07-03). This keeps the build unblocked and avoids prematurely solving cross-GDS identity.
35
+ Accepted contributions earn **visible credit** (author + origin in the catalog) immediately. Any monetary reward (example) — and the cross-instance identity/payout mechanics it would require — is **explicitly deferred** to a separate economics decision (owner call, 2026-07-03). This keeps the build unblocked and avoids prematurely solving cross-GDS identity.
36
36
 
37
37
  ### 4. Licensing: AGPL-in, sign-off, no CLA.
38
38
 
@@ -1,4 +1,4 @@
1
- # ADR 0120 — Pay drachmae on land (not on confirm) + a builder-owned rebase gate
1
+ # ADR 0120 — Pay example on land (not on confirm) + a builder-owned rebase gate
2
2
 
3
3
  **Date:** 2026-07-04
4
4
  **Status:** Accepted
@@ -7,7 +7,7 @@
7
7
 
8
8
  ## Context — the problem
9
9
 
10
- The per-task drachmae reward is awarded in **`confirmTask`** (`modules/lifecycle/db.js`,
10
+ The per-task example reward is awarded in **`confirmTask`** (`modules/lifecycle/db.js`,
11
11
  `reward.insertMultipliedConfirmCredit`, `credit_log.reason = 'task.confirmed'`) — at the
12
12
  `completed → confirmed` transition. Confirmed means *verified + credited*, but the code is
13
13
  **not yet on `main`**: the merge to `main` + deploy is the later `confirmed → shipped`
@@ -16,7 +16,7 @@ transition, run by the auto-lander / `publish-reconciler`.
16
16
  So a builder is paid the moment they declare done and pass verification — **before their
17
17
  code lands.** When the branch then can't merge (stale after a big codebase change, a real
18
18
  conflict, or never pushed at all), the task strands at `confirmed` indefinitely with **no
19
- consequence to the builder**: they already have the drachmae, and nothing stops them
19
+ consequence to the builder**: they already have the example, and nothing stops them
20
20
  claiming new work while the stranded branch rots.
21
21
 
22
22
  This is not hypothetical. On 2026-07-04 the confirmed queue held ~17 tasks across three
@@ -38,7 +38,7 @@ Re-couple reward and queue-access to **landed code**, in four parts.
38
38
  The award becomes idempotent on ship (`credit_log.reason = 'task.shipped'`, guarded by a
39
39
  uniqueness check per task, mirroring today's confirm-time guard). **The happy path barely
40
40
  changes:** a clean PR auto-merges within a minute or two of confirm, so the credit lands
41
- almost immediately. The teeth only bite when a task strands — no landed code, no drachmae.
41
+ almost immediately. The teeth only bite when a task strands — no landed code, no example.
42
42
 
43
43
  2. **A stalled land becomes the builder's own chore (`needs_rebase`).** The
44
44
  `publish-reconciler` already detects why a confirmed task won't land — `merge_conflict`,
@@ -73,11 +73,11 @@ can do it.
73
73
 
74
74
  - **Good behavior is unaffected.** Clean work still auto-lands in minutes and pays out then;
75
75
  most builders never see `needs_rebase` or the gate.
76
- - **Stranding now has teeth.** No landed code → no drachmae, and a stalled land blocks new
76
+ - **Stranding now has teeth.** No landed code → no example, and a stalled land blocks new
77
77
  claims until the builder rebases (or releases the task, forfeiting its reward).
78
78
  - **The credit ledger tells the truth.** `credit_log` credits now correspond to code that is
79
79
  actually on `main`, not to verified-but-unlanded branches.
80
- - **Retroactive sting.** The clawback reverses already-shown drachmae for currently-stranded
80
+ - **Retroactive sting.** The clawback reverses already-shown example for currently-stranded
81
81
  tasks. This is intentional (it creates the pressure) and is applied uniformly, but it will
82
82
  feel punitive to builders who were paid under the old rule. The reversal is fully re-earnable:
83
83
  land the task and the `task.shipped` credit books.
@@ -33,6 +33,8 @@ The conversion endpoint is unauthenticated (the code is the auth), so the web ti
33
33
 
34
34
  `redirect_url` = the **mothership** callback (the instance can't do the exchange). `callback_urls` = the **instance's** own `/api/gds/auth/web/callback` (what `auth.js` actually builds) **and** the `/api/bongos/...` alias, so sign-in works regardless of which prefix a build emits. `default_permissions: {}` (sign-in needs none), `hook_attributes.active:false`, `public:false`. Personal vs org differs only in the POST URL (derived from the repo owner); the App works for sign-in either way.
35
35
 
36
+ **App name (tasks 2093 → 2095).** GitHub App names are globally unique across all of GitHub, so a bare project slug (e.g. `demo`) is almost always already taken. The auto name is the **instance domain** (e.g. `demo.cloudbongos.com`): a domain is inherently unique (so it never collides) and it reads perfectly on GitHub's sign-in consent screen, where the name is shown as *"`<name>` by `<owner>` … will redirect to `https://<domain>`"* — the name IS the destination. GitHub keeps the dots in the display name (only the internal slug is transformed) and caps it at 34 chars. **Fallback** (no domain, or a domain over 34 chars): brand-qualify the project — `<mothership brand> - <project>`, e.g. `Cloud Bongos - demo`, from the mothership's own `branding().identity.productName` (portable, not hardcoded) — still far less collision-prone than a bare slug. Either way it is only a default the owner can rename on GitHub's confirmation screen.
37
+
36
38
  ## Consequences
37
39
 
38
40
  - **Back-compat preserved.** An instance already on an OAuth App keeps working untouched — `loadGithubCreds()` reads the same `<PREFIX>_GITHUB_CLIENT_ID/SECRET`. The manifest path is the new *default* for fresh instances; nothing about `auth.js` changed.
@@ -3,12 +3,12 @@
3
3
  - **Status:** Accepted — publish path + private-by-default config built in task 2089; the real publish is a gated owner step.
4
4
  - **Date:** 2026-07-07
5
5
  - **Deciders:** example-owner (owner/Archon), Claude. Owner asked to have the npm/CLI onboarding commands ready but kept private pre-launch, choosing paid private npm over the free GitHub Packages option.
6
- - **Task:** [#2089](https://example.com/builders#/task/2089) · **Goal:** 35 (project-creation flow), criterion 165.
6
+ - **Task:** [#2089](https://example.com/builders#/task/2089) (publish path + private config) · [#2092](https://example.com/builders#/task/2092) (the `@cloudbongos` → `@bongos` scope rename + first real publish) · **Goal:** 35 (project-creation flow), criterion 165.
7
7
  - **Refines:** [#2025](https://example.com/builders#/task/2025) (the 2026-07-04 *public* unscoped `cloudbongos` decision — now the launch-time go-public step, not superseded) · **Builds on:** [ADR 0108](<redacted>.md) (core-as-npm-dep), [#2053](https://example.com/builders#/task/2053) (vendored-tarball install).
8
8
 
9
9
  ## Context
10
10
 
11
- A new owner stands up a standalone instance that depends on `@cloudbongos/core` (ADR 0108), and external programs call the API through `@cloudbongos/client` (ADR 0118). Today neither is on a registry: `package-core.js` produces a real, installable `.tgz`, and provisioning **vendors that tarball** into each instance repo (a `file:` pin `npm ci` resolves — task 2053). That works, but the onboarding experience isn't the clean `npm install @cloudbongos/core` an owner expects, and the client's on-ramp doc referenced a nonexistent "internal registry."
11
+ A new owner stands up a standalone instance that depends on `@bongos/core` (ADR 0108), and external programs call the API through `@bongos/client` (ADR 0118). Today neither is on a registry: `package-core.js` produces a real, installable `.tgz`, and provisioning **vendors that tarball** into each instance repo (a `file:` pin `npm ci` resolves — task 2053). That works, but the onboarding experience isn't the clean `npm install @bongos/core` an owner expects, and the client's on-ramp doc referenced a nonexistent "internal registry."
12
12
 
13
13
  The owner wants the npm/CLI commands **ready and usable pre-launch, but not public** — Cloud Bongos shouldn't be installable by the general public before launch. Two prior decisions bear on this: the *public* install name was set to the unscoped **`cloudbongos`** (task 2025, deferred), and the `bongos` + ~10 defensive npm orgs were reserved under the owner's login.
14
14
 
@@ -16,15 +16,17 @@ One npm rule forces the shape: **unscoped packages are always public — only *s
16
16
 
17
17
  ## Decision
18
18
 
19
- Publish **`@cloudbongos/core`** and **`@cloudbongos/client`** as **scoped, private** npm packages during pre-launch, on a **paid** `cloudbongos` npm org. Both already use the `@cloudbongos` scope (an org the owner holds), so **no rename is needed now**.
19
+ Publish **`@bongos/core`** and **`@bongos/client`** as **scoped, private** npm packages during pre-launch, on the owner's **paid** `bongos` npm org — the **canonical** brand org.
20
+
21
+ > **Scope correction (task 2092).** The publish path first shipped (task 2089) under the `@cloudbongos` scope, matching the name the repo already used. But when publishing, the paid org turned out to be **`bongos`** (the canonical brand the owner put on Teams), while `cloudbongos` — one of the ~10 defensive orgs — was left on the free tier. Since a private scoped publish requires the scope to match a *paid* org, and `@bongos` is the intended canonical brand anyway, we **renamed the core + client package identity `@cloudbongos/*` → `@bongos/*`** (a consistent rename across the build + vendored-install flow, verified by the test suite) rather than pay for a second org. Existing vendored consumers keep their pinned `@cloudbongos/core` tarball; only new scaffolds use `@bongos`.
20
22
 
21
23
  - **Private by default in code, not just by flag.** The synthesized core `package.json` (`buildPackageJson`) and the generated client `package.json` (`gen-api-client.js`) both carry `publishConfig: { access: "restricted" }`. `package-core.js --publish` additionally passes `--access restricted` on the CLI — belt-and-suspenders so a stray config edit can't silently make the pre-launch artifact public.
22
24
  - **The publish is a gated owner step; the tooling holds no token.** `package-core.js --publish` **dry-runs** by default; the real, permanent publish needs `--publish --live` plus the owner's `npm login` and the org on a paid plan. npm owns auth — this repo never stores a token.
23
25
  - **Consumers authenticate with an env-fed `.npmrc`:** `//registry.npmjs.org/:_authToken=<redacted> The token expands from the environment, so it never lands in a committed file. Only members of the paid org can install.
24
26
  - **The vendored-tarball path stays as the working default/fallback.** Rewiring provision/init to auto-pull the pinned core from the registry (env-fed token on the control plane) is a follow-up — it touches a proven, protected control-plane flow and isn't required for "commands ready + private." Publishing privately + documented auth already delivers a real `npm install`.
25
- - **Going public at launch is one step**, chosen then: either `npm access public @cloudbongos/core` (keeps the scoped name), or execute task 2025 to *also* publish the unscoped vanity `cloudbongos` for `npm install -g cloudbongos`. Task 2025 is therefore the launch-time go-public leg, not replaced by this.
27
+ - **Going public at launch is one step**, chosen then: either `npm access public @bongos/core` (keeps the scoped name), or execute task 2025 to *also* publish the unscoped vanity `cloudbongos` for `npm install -g cloudbongos`. Task 2025 is therefore the launch-time go-public leg, not replaced by this.
26
28
 
27
- The daily command stays `bongos` regardless of package name (the `bin` mapping in the synthesized package.json is name-independent), so `npm install -g @cloudbongos/core` → `bongos init` works today, and the eventual public `cloudbongos` name changes nothing for users.
29
+ The daily command stays `bongos` regardless of package name (the `bin` mapping in the synthesized package.json is name-independent), so `npm install -g @bongos/core` → `bongos init` works today, and the eventual public `cloudbongos` name changes nothing for users.
28
30
 
29
31
  ## Why paid private npm over the free GitHub Packages alternative
30
32
 
@@ -32,7 +34,19 @@ The owner chose it deliberately, knowing the trade-off. GitHub Packages is free
32
34
 
33
35
  ## Consequences
34
36
 
35
- - Cost: a paid `cloudbongos` npm org seat per person who needs to install during pre-launch. Gone at launch.
37
+ - Cost: a paid `bongos` npm org seat per person who needs to install during pre-launch. Gone at launch.
36
38
  - Owner-only steps (out of the build session; capture as a blocker if they gate verification): put the org on a paid plan; generate an npm access token (automation — read-write to publish, read for CI/provision installs); run `package-core.js --publish --live` (permanent; login + 2FA).
37
39
  - Follow-up: wire provision/init to install the pinned core from the private registry (env-fed `NPM_TOKEN`), keeping the vendored tarball as fallback.
38
40
  - Full operator runbook — org, plan, token, publish, consumer `.npmrc`, and the go-public flip — is [`docs/recipes/private-npm-distribution.md`](../recipes/private-npm-distribution.md).
41
+
42
+ ## Update — the provision/init registry path is wired (task 2090)
43
+
44
+ The follow-up above shipped. The wiring is **opt-in and fail-soft**, so the proven vendored-tarball path stays the default until the registry path is proven end-to-end on a real greenfield standup:
45
+
46
+ - **One source of truth**: `scripts/gds/npmrc.js` owns the env-fed `.npmrc` body (`@bongos:registry=…` + `_authToken=<redacted> and the gates (`hasNpmToken`, `registryCoreRequested`). The token expands from the environment at install time, so nothing secret is committed.
47
+ - **init** (`--registry-core`): keeps the caret pin and writes the `.npmrc`; with `NPM_TOKEN` set it also generates `package-lock.json` from the registry. Mutually exclusive with `--vendor-core`.
48
+ - **provision** (standalone scaffold): opt-in via `PROVISION_CORE_FROM_REGISTRY=1`; when a token is present it scaffolds via `init --registry-core` (no tarball build), and when the flag is set but no token is present it **falls back to the vendored tarball** — a tokenless standup still works.
49
+ - **upgrade** (`--to <v> --registry`): pins the exact registry version + ensures the `.npmrc` instead of vendoring; mutually exclusive with `--from`.
50
+ - **Verified — both legs green (task 2118, 2026-07-08).**
51
+ - *Tokenless:* a scaffolded instance with the caret pin + env-fed `.npmrc` and **no** `NPM_TOKEN` 404s cleanly on `npm install` (`E404 … @bongos/core … could not be found or you do not have permission to access it`) — it fails closed.
52
+ - *With token (the leg blocker 71 tracked):* a real `bongos init --registry-core` scaffold with a valid **read-only** token (paid `bongos` org) resolved `@bongos/core@1.17.0` from `registry.npmjs.org` and generated a reproducible `package-lock.json` (`resolved: https://registry.npmjs.org/@bongos/core/-/core-1.17.0.tgz`). The registry pull works with a token and fails closed without one — so the opt-in registry path is proven end-to-end, and blocker 71 is resolved. The vendored tarball remains the default until an owner chooses to flip a live provisioner to `PROVISION_CORE_FROM_REGISTRY=1`.
@@ -0,0 +1,34 @@
1
+ # 0135 — Module upstream submission: the review queue lives in the submitting instance's own DB (interim)
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-07-08
5
+ - **Task:** [#1770](https://example.com/builders#/task/1770) (BONGOS-V1) — `bongos module submit` + `POST /modules/:key/submit`.
6
+ - **Builds on:** [ADR 0107](<redacted>.md) (the upstreaming decision — submit → review → accept), [ADR 0103](<redacted>.md) (the core extracted to `github.com/example-owner/cloud-bongos`, private), [ADR 0100](<redacted>.md) §3 (per-repo GDS — each project runs its own DB).
7
+
8
+ ## Context
9
+
10
+ ADR 0107 §1 describes `bongos module submit <key>` as packaging a module + attribution + a DCO sign-off and filing it "into a rank-gated review queue **on the core**" — without granting the submitting instance write access to core. Task 1770 depends on R88 (the core extraction, [#1727](https://example.com/builders#/task/1727), shipped) and originally also listed R85 (Mercury, the forcing-function second consumer). R85 was abandoned ([#1689](https://example.com/builders#/task/1689)): the two-repo model it was meant to prove is already proven end-to-end by a different standalone instance, so it dropped as a dependency.
11
+
12
+ That leaves a gap task 1770 has to resolve to be buildable at all: **what does "on the core" concretely mean today?** The extracted core repo (`github.com/example-owner/cloud-bongos`) is a **private git repository, not a running service** — it has no HTTP API, no reachable review-queue endpoint, and (per ADR 0100 §3) each Cloud Bongos project runs its **own** GDS database; there is no shared cross-repo GDS a submission could land in. Building a real cross-repo network submission would mean inventing a core-side API that doesn't exist yet, authenticating an instance against it, and defining a wire format — none of which is this task's job (that's ADR 0107 §8's *pipeline* stage, gated on task 1771, "core-side review & accept flow," which is still `backlog`).
13
+
14
+ ## Decision
15
+
16
+ **The review queue is a table (`module_submissions`, migration `<redacted>.sql`) in THIS instance's own Bongos DB** — the same database the module was built against — not a network call to a remote core.
17
+
18
+ - `bongos module submit <key>` packages the module (manifest + a full base64 file snapshot + the recorded DCO sign-off), re-runs the ADR 0107 §4 AGPL/publish pre-check, and **refuses (exit 1)** if the pre-check fails or no sign-off is on file — exactly the ADR's "done when" bar.
19
+ - The packaging + submission both flow through `POST /api/gds/modules/:key/submit` (rank: metic+archon) — the SAME route the hall "Modules" tab's submit button calls (ADR 0107 §9), so there is one authoritative implementation, not a CLI copy and a UI copy. The route re-runs the pre-check against **its own** on-disk copy of the module (the server's deployed checkout), which is the real gate — a builder's local pre-check pass is a courtesy/fail-fast, not proof, if their changes aren't committed + shipped + deployed yet.
20
+ - `GET /api/gds/modules/submissions` (rank: metic+archon) lists the queue — the surface task 1771's core-side review UI reads from.
21
+
22
+ This satisfies the letter of ADR 0107 §1 ("rank-gated review queue," "refuses if the pre-check fails," "captures attribution + sign-off") using infrastructure that actually exists today, and is forward-compatible: when a real core-side receiving endpoint exists (task 1771 or later), filing becomes a network hop to that endpoint instead of a local `INSERT`, with **no change to the CLI's or the hall UI's caller-facing contract** (both already only see `{ signed_by }` in, `{ submission }` out).
23
+
24
+ ## Alternatives considered (rejected)
25
+
26
+ - **A live HTTP call to a core-hosted endpoint.** Rejected for now — no such endpoint exists; inventing one is task 1771's job (core-side review & accept), not the submit side's. Building it here would mean guessing at a contract 1771 might not honor.
27
+ - **A GitHub Issue/PR against `github.com/example-owner/cloud-bongos` via the GitHub API.** Considered (the ADR's "fork-and-PR remains a fallback" framing), but rejected as the *native* path: it would silently degrade every submission to the explicitly-non-canonical fallback, loses the structured queue (status/reviewed_by/reviewed_at) task 1771 needs, and the repo is private — the CLI/server have no standing GitHub credential scoped for that repo today.
28
+ - **A dry-run-only client (mirroring `do-api.js`'s DigitalOcean/Cloudflare pattern) that just logs "would submit" and writes a local file.** Rejected — a local orphan file is a worse "review queue" than a real, queryable DB table the instance already operates and backs up; it would need its own bespoke listing/status mechanism that duplicates what Postgres already gives us for free.
29
+
30
+ ## Consequences
31
+
32
+ - **The queue is scoped per-instance, not global**, until a real cross-repo channel exists. Two instances submitting modules with the same key don't collide (each queue is local), which is fine for now (there is exactly one non-abandoned instance, this one) but is a real gap once a second live instance exists — revisit when task 1771 is scoped.
33
+ - **Task 1771** ("core-side review & accept flow") inherits a concrete shape to review against: read `module_submissions` (`status='pending'`), accept/reject with `reviewed_by`/`reviewed_at`/`review_notes`, and the accept action still has to do the ADR 0107 §6 checklist (land in `modules/<key>/`, `default:false`, allowlist, record attribution) — this ADR does not change any of that.
34
+ - **`scripts/gds/module.js` is required directly by `src/bongos/routes/modules.js`** (both resolve under `resolveCoreRoot()` — see `scripts/gds/CLAUDE.md`) to reuse `checkModulePublishability`/`recordSignOff`/`readSignOff`/`<redacted>` rather than re-implementing the pre-check server-side. `module.js` never statically requires the CLI session/fetch layer (`cli-lib.js`) — only `cmdSubmit`'s network leg lazy-requires it, and only when a human/agent actually runs the CLI — so this stays safe to load into the always-on server process (the same discipline `llm-cache.js` documents for the same reason).
@@ -0,0 +1,51 @@
1
+ # ADR 0136 — Update-channel subscription policy (patch-only, health-gated, opt-in, autonomy-gated)
2
+
3
+ **Date:** 2026-07-10
4
+ **Status:** Accepted.
5
+ **Context:** [task 2150](https://example.com/builders#/task/2150) (BONGOS-V1, goal 26). The last piece of the update-consumption workstream: [ADR 0100](<redacted>.md) §2 / [task 1690](https://example.com/builders#/task/1690) gave a consumer the manual `bongos upgrade` channel; [ADR 0134](<redacted>.md) / [task 2090](https://example.com/builders#/task/2090) added the private-registry source; [task 2149](https://example.com/builders#/task/2149) added auto-rollback; [task 2157](https://example.com/builders#/task/2157) proved rollback live on the demo instance. What was missing is the **subscription** — how a consumer instance takes new releases *on a cadence, unattended*, rather than a human running `bongos upgrade` each time. Builds on the scheduled-routines portability contract ([ADR 0115](<redacted>.md)).
6
+
7
+ ## Problem
8
+
9
+ "How do projects subscribe to regular updates?" needs a policy, not just a mechanism. Left unspecified, an auto-updater is a footgun: it could take a breaking major unattended, upgrade an instance nobody meant to enroll, or leave an instance down after a bad release. Two questions had to be answered together: **(1)** how far may an unattended upgrade jump, and **(2)** what has to be true before anything moves on its own. A third question — who *publishes* the releases the subscription consumes — is related but separable, and conflating it would have blocked (1) and (2) behind a publishing-automation decision.
10
+
11
+ ## Decision
12
+
13
+ Adopt the owner-chosen policy (2026-07-09): **patch-only by default, health-gated with auto-rollback, opt-in, and autonomy-gated.**
14
+
15
+ **1. Update channels bound the blast radius.** Each subscribed instance carries an `update_channel`:
16
+
17
+ - `pinned` — never auto-upgrade (manual `bongos upgrade` only).
18
+ - `patch` — take a higher **patch** within the same `major.minor` (`1.17.1 → 1.17.4`, never `1.18.0`). **The default.**
19
+ - `minor` — take a higher **minor** within the same major (`1.17.1 → 1.18.0`, never `2.0.0`).
20
+
21
+ A **major** bump is never automatic under any channel — a breaking change wants a human. Prereleases (`1.17.2-rc.1`) are never auto-targeted. This math is the pure, unit-tested core [`scripts/gds/update-channel.js`](../../scripts/gds/update-channel.js) (`resolveChannelTarget` / `channelAllows`), so the "what should this instance move to?" decision is testable in isolation from any I/O.
22
+
23
+ **2. The registry is the source of truth for "what's newest."** A subscription needs a live feed of published versions — `npm view @bongos/core versions` against the private registry (ADR 0134), not a vendored tarball. The vendored path stays the manual-bump fallback.
24
+
25
+ **3. Every auto-upgrade is health-gated with auto-rollback.** The routine drives `bongos upgrade --to <v> --registry` with auto-rollback **ON** (task 2149): a patch that fails install/migrate/health reverts its pin, reinstalls the previous core, restarts, and records a `rolled_back` ledger row. The DB is left intact (core migrations are additive/forward-safe). So "unattended" never means "unsafe."
26
+
27
+ **4. Triple-gated before anything moves.** The scheduled routine [`core-update-subscription`](../../.claude/scheduled-tasks/core-update-subscription/) is:
28
+
29
+ - **Autonomy-gated** — `requiresAutonomy: true` in [`config/scheduled-routines.json`](../../config/scheduled-routines.json), a no-op until the instance arms `<PREFIX>_AUTONOMY_ENABLED`. Default **OFF** (ADR 0115's rule for autonomous-action routines).
30
+ - **Opt-in** — the roster [`config/update-subscriptions.json`](../../config/update-subscriptions.json) ships **empty**; an instance is touched only after an operator lists it (with its dir, systemd unit, health URL, channel, and an optional `env` map so `migrate` hits the right DB).
31
+ - **Health-gated** — per (3).
32
+
33
+ The routine is `mode: deterministic` (System 4, [ADR 0078](<redacted>.md)) — it runs as plain `node` with no model session — and **instance-wide** (ADR 0115): one run per instance iterating the roster, not one cron per instance-being-watched.
34
+
35
+ **5. Upstream auto-publish is explicitly split out, not decided here.** For a subscription to deliver anything, new patches must be *published*. Today that is a gated owner step (`package-core.js --publish --live`, ADR 0134). This ADR deliberately scopes the subscription to **consume whatever the owner has published** and does **not** decide whether merges to `main` auto-publish a patch. That is its own decision (publishing cadence, provenance, who holds the token) and is filed as [task 2158](https://example.com/builders#/task/2158). Splitting it lets goal 26 close on the consumption half, which is what the switchover needs.
36
+
37
+ **6. Scope: local, same-host instances.** The routine upgrades instances reachable from the control plane on the same host (the co-hosted shape — e.g. the demo instance beside the control plane). Multi-host / SSH orchestration across a real fleet is the deferred managed-fleet-control-plane epic ([task 1948](https://example.com/builders#/task/1948) / idea 303, "no fleet registry exists"); this routine is the per-instance consumption half that epic will call.
38
+
39
+ ## Alternatives considered
40
+
41
+ - **A `provisioning_instances` DB column for the roster + channel.** Rejected for now: the provisioning module models a *request→standup* lifecycle, not a subscription; coupling the subscription to it presumes the fleet-registry schema that is still being designed. A config manifest is migration-free (matches the task's `needs_migration:false`), matches the ADR-0115 "operator configures via a manifest" pattern, and is trivially superseded later by a `loadSubscriptions()` seam swap.
42
+ - **Auto-take minors by default.** Rejected: minors can carry behavior changes; patch-only is the conservative default an operator can widen per instance.
43
+ - **No dry-run / apply-always.** Rejected: `subscribe.js` is dry-run by default (the box-sweep convention); the scheduled step passes `--apply`. A hand run previews safely.
44
+ - **Deciding auto-publish here.** Rejected — see (5); it would have blocked the consumption half behind an orthogonal publishing decision.
45
+
46
+ ## Consequences
47
+
48
+ - **`config/update-subscriptions.json` is the portability surface** for subscriptions — the one place an operator opts an instance in and sets its channel. A vanilla instance boots subscribed to nothing.
49
+ - **The policy is enforced by a pure, tested core**, not prose: [`tests/update_channel.mjs`](../../tests/update_channel.mjs) pins the channel matrix (majors/prereleases never auto, patch stays on its minor, pinned/unknown → no-op).
50
+ - **A follow-up task owns upstream auto-publish** — [task 2158](https://example.com/builders#/task/2158) (whether/how `main` merges publish a patch: cadence, version-bump provenance, token custody, the pre-publish no-leak gate). Until it lands, the subscription simply picks up whatever the owner publishes, which is safe and useful on day one.
51
+ - **No behavior change until armed.** OTB (and any fresh instance) ships this OFF with an empty roster; nothing auto-upgrades until an operator both arms autonomy and enrolls an instance.
@@ -0,0 +1,40 @@
1
+ # 0137 — Upstream publish policy: `@bongos/core` patches are published manually (owner-gated), not auto-published on merge
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-07-10
5
+ - **Deciders:** example-owner (owner/Archon), Claude. Presented the manual-vs-auto fork; the owner chose to keep publishing a manual, owner-triggered step.
6
+ - **Task:** [#2158](https://example.com/builders#/task/2158) (BONGOS-V1, goal 26) — the **supply** half split out of [ADR 0136](<redacted>.md) §5 / [#2150](https://example.com/builders#/task/2150).
7
+ - **Builds on:** [ADR 0134](<redacted>.md) (private-first npm, tokenless tooling), [ADR 0136](<redacted>.md) (subscription consumption policy). **Relates to:** the deferred managed-fleet-control-plane epic ([#1948](https://example.com/builders#/task/1948)).
8
+
9
+ ## Context
10
+
11
+ [ADR 0136](<redacted>.md) (task 2150) built the **consumption** half of the `@bongos/core` update subscription — the channel math (`pinned`/`patch`/`minor`, never `major`/prerelease unattended) and the auto-upgrade routine that consumes published releases on a cadence. It explicitly deferred (§5) the **supply** half: *"does not decide whether merges to `main` auto-publish a patch. That is its own decision (publishing cadence, provenance, who holds the token)."* This ADR is that decision.
12
+
13
+ Today, per [ADR 0134](<redacted>.md), publishing is a **gated owner step**: `node scripts/gds/package-core.js --publish --live` requires the owner's `npm login` + 2FA, and *the repo/tooling holds no token* ("npm owns auth — this repo never stores a token"). Consumers install with a **read-only** token via an env-fed `.npmrc`. For a subscription to deliver anything, new patches must be published — so the open question was whether to **automate** that publish (on-merge, using a CI-stored write token) or keep it **manual**.
14
+
15
+ ## Decision
16
+
17
+ **Publishing `@bongos/core` stays a manual, owner-gated step. Merges to `main` do NOT auto-publish.**
18
+
19
+ 1. **Trigger / cadence — owner-initiated.** The owner runs `package-core.js --publish --live` when they decide to cut a release. Recommended convention: tag the released commit `core-v<version>` as the deliberate, auditable release marker. The tag *documents* the release; it does **not** trigger CI.
20
+ 2. **Version-bump provenance — deliberate, in-task.** `CORE_VERSION` (`src/module-api.js`) is bumped by hand as part of a task, with a changelog line in the same file (the existing discipline — e.g. task 2148 bumped `1.17.1 → 1.17.2` with a changelog entry). It is **not** auto-bumped. A release publishes whatever `CORE_VERSION` currently is; a stale (un-bumped) version collides with the already-published npm version and npm rejects the duplicate — a natural guard against an accidental no-op republish.
21
+ 3. **Token custody — no write-token at rest.** No npm **write** token is stored in CI or the repo. The owner authenticates at publish time (a Classic Automation token bypasses the interactive 2FA-OTP prompt when scripting locally; interactive `npm login` + 2FA otherwise). This preserves ADR 0134's tokenless-tooling posture. Only the **read** token (for consumer / CI installs, env-fed `NPM_TOKEN`) exists.
22
+ 4. **No-leak / mirror-redact gate — already fail-closed.** `package-core.js` builds the artifact *through* the no-leak gate (docs genericized + secret-scrubbed; functional files scanned for a live-credential leak) and **produces nothing** if anything leaks, so `--publish` structurally cannot upload a leaking artifact. No extra gate is needed for the manual path — and any future automated path inherits the same fail-closed gate because it runs the same build.
23
+ 5. **The subscription is unchanged.** The [ADR 0136](<redacted>.md) routine consumes whatever the owner has published. Its freshness is bounded by the owner's release cadence — intended and acceptable at current scale.
24
+
25
+ ## Why manual over auto (the trade-off)
26
+
27
+ - **Security surface.** <redacted> requires a long-lived npm **automation write** token as a standing CI secret — a powerful credential that could publish to the canonical brand org from any merge. That reverses ADR 0134's deliberate tokenless-tooling posture for little near-term gain.
28
+ - **Low, deliberate volume.** Core patches are infrequent and intentional (`1.17.0 → .1 → .2` over days, each driven by a specific task). The manual publish is not the bottleneck; the consumption side already flows.
29
+ - **Bootstrapped posture** ([CLAUDE.md §4](../../CLAUDE.md)). Don't build and secure CI-publishing machinery before a real fleet justifies it.
30
+
31
+ ## When to revisit
32
+
33
+ When the **managed-fleet-control-plane epic** ([#1948](https://example.com/builders#/task/1948)) stands up a real multi-instance fleet and the manual publish becomes a genuine bottleneck, re-evaluate a **tag-triggered** CI publish (a human still cuts the `core-v<version>` tag; CI runs the already-fail-closed gated publish with a narrowly-scoped token). That is deliberately out of scope here — this ADR keeps the supply side simple so goal 26 closes on the consumption half.
34
+
35
+ ## Consequences
36
+
37
+ - **Owner-only step at each release** (out of a build session; capture as a blocker if it ever gates verification): bump `CORE_VERSION` + changelog in a task, then run `package-core.js --publish --live`.
38
+ - The **release ritual** is documented in [`docs/recipes/private-npm-distribution.md`](../recipes/private-npm-distribution.md) → "Cutting a core release".
39
+ - **No CI secret, no new automation** to maintain.
40
+ - A consumer on the `patch`/`minor` channel only receives an update **after** the owner publishes — the subscription's freshness is owner-paced by design.
@@ -0,0 +1,45 @@
1
+ # 0138 — A core-bundled module's external npm deps are declared per-module; the consumer composes them (core stays lean)
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-07-10
5
+ - **Deciders:** example-owner (owner/Archon), Claude. Presented the fork (keep core lean + declare per-module vs. hoist the dep into `@bongos/core`); the owner chose to keep core lean.
6
+ - **Task:** [#2160](https://example.com/builders#/task/2160) (BONGOS-V1, goal 26) — rehearsal finding **F2** from the R89 cutover rehearsal ([`docs/reviews/<redacted>.md`](../reviews/<redacted>.md)).
7
+ - **Builds on:** [ADR 0108](<redacted>.md) (core consumed as a pinned npm dep; core deps = express+pg), [ADR 0083](<redacted>.md) (the module manifest contract).
8
+
9
+ ## Context
10
+
11
+ The R89 cutover rehearsal converted the example monolith into a `@bongos/core` consumer and surfaced **F2**: with `dependencies: { "@bongos/core" }` alone the consumer boots and then crashes on `Cannot find module 'colyseus'` — and, more subtly, on `Cannot find module 'discord.js'`. The game/art deps (`colyseus`, `phaser`, `sharp`) are obviously the host's. But `discord.js` is different: the **discord module ships INSIDE `@bongos/core`** (it's one of the ~15 bundled core modules), yet `package-core.js` synthesizes only `express` + `pg` as the core's `dependencies` ([ADR 0108](<redacted>.md) §Context). So the core ships the discord module's *code* but declares none of its *runtime dep* — the consumer is silently expected to provide `discord.js`, with a boot crash as the only feedback.
12
+
13
+ Two facts frame the decision:
14
+
15
+ - **`discord.js` is the only such case today.** A scan of every bundled core module found exactly one external npm require beyond `express`/node built-ins: `discord.js`, in the discord module.
16
+ - **The discord module is opt-in** (`module.json` `default: false`). Most instances never enable it.
17
+
18
+ So the open question (rehearsal F2, "separately decide"): should `@bongos/core` declare its bundled-module deps (e.g. `discord.js`) itself — as `optionalDependencies`/`peerDependencies` — or should the consumer provide them?
19
+
20
+ ## Decision
21
+
22
+ **`@bongos/core` stays lean (`express` + `pg`). A bundled module that needs an external npm dependency declares it in its own `module.json` `dependencies`, and the consumer composes the deps of the modules it ENABLES.**
23
+
24
+ 1. **Core deps unchanged.** `package-core.js` continues to synthesize `dependencies: { express, pg }` (+ the undici pin). We do **not** hoist `discord.js` (or any future module dep) into the core package.
25
+ 2. **Manifest field.** `module.json` gains an optional `dependencies: { "<npm-name>": "<semver-range>" }` block ([`src/module-loader/manifest-schema.js`](../../src/module-loader/manifest-schema.js) validates it — a typo'd range is caught, not silently ignored). The discord manifest declares `{ "discord.js": "^14.26.4" }` — the honest home for the fact.
26
+ 3. **Consumer composition.** Greenfield `bongos init` composes the ENABLED modules' declared deps into the instance `package.json` ([`scripts/gds/init.js`](../../scripts/gds/init.js) `enabledModuleDeps` → `buildInstancePackageJson`), so turning a module on brings its npm deps. Precedence: module deps fill gaps, an explicit pre-existing instance pin wins, `@bongos/core` is always set.
27
+ 4. **The cutover preserves.** The example R89 cutover re-authors the consumer `package.json` from the monolith's **existing** dependency closure ∪ `@bongos/core` (runbook §2) — the monolith already carries `discord.js` + the game/art deps, so preserving is sufficient there. `enabledModuleDeps` is the greenfield/adopt path, not the conversion path.
28
+
29
+ ## Why lean-core over hoisting (the trade-off)
30
+
31
+ - **Hoisting bloats the common case.** `optionalDependencies` still install by default, so declaring `discord.js` in core makes **every** consumer download it (and its transitive `undici`, WebSocket stack, …) for a module that is **off by default**. That directly cuts against [ADR 0108](<redacted>.md)'s deliberate lean-core choice.
32
+ - **`peerDependencies` don't fix the crash.** An *optional* peer dep is not auto-installed by npm, so the boot crash would remain; a *required* peer dep is just hoisting with extra warnings.
33
+ - **Per-module declaration is honest and scales.** The dep lives with the code that needs it, the manifest stays the single source of truth for "what this module is," and the pattern generalizes to any future module without re-touching core.
34
+ - **`--omit=optional` is opt-out, not opt-in.** Relying on consumers to prune an unwanted dep is the wrong default; composing only enabled modules' deps is opt-in by construction.
35
+
36
+ ## Consequences
37
+
38
+ - **`@bongos/core`'s dependency surface stays minimal + auditable** — the property [ADR 0108](<redacted>.md) and `package_core.mjs` (`dependencies == { express, pg }`) already lock in.
39
+ - **A bundled module with an external dep MUST declare it** in `module.json` `dependencies`, or a consumer that enables it crashes at boot. Documented in [`docs/modules-contract.md`](../modules-contract.md).
40
+ - **Greenfield/adopt** instances that enable a module get its deps automatically; the **R89 cutover** stays a preserve-existing-closure step.
41
+ - **No new standing credential or CI machinery** — this is authoring-time composition, not an install-time core change.
42
+
43
+ ## When to revisit
44
+
45
+ If a bundled module ever becomes `default: true` AND carries a heavy external dep, revisit whether that module belongs in the core bundle at all (vs. an opt-in add-on) — the dep-posture question would then be downstream of a module-bundling question.