@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
@@ -0,0 +1,232 @@
1
+ // modules/provisioning/tests/provisioning.mjs
2
+ //
3
+ // DB-free unit smoke for the provisioning module's domain slice (ADR 0111). Focus:
4
+ // the P4 (#1945) Caddy on-demand-TLS ALLOW-LIST helpers — normalizeHostname (pure)
5
+ // and isProvisionedHostname (a fake { query }) — plus a sanity check of the P1 pure
6
+ // helpers. No DB, no network: `db` is a recording fake. Auto-discovered + run by
7
+ // scripts/gds/run-unit-tests.js when the `provisioning` module is enabled (it is on
8
+ // OTB). Run standalone: node modules/provisioning/tests/provisioning.mjs
9
+
10
+ import { strict as assert } from 'node:assert';
11
+ import { createRequire } from 'node:module';
12
+
13
+ const require = createRequire(import.meta.url);
14
+ const P = require('../provisioning.js');
15
+ const RL = require('../rate-limit.js');
16
+
17
+ let passed = 0, failed = 0;
18
+ function t(name, fn) {
19
+ try { fn(); passed++; console.log(` PASS ${name}`); }
20
+ catch (e) { failed++; console.log(` FAIL ${name}\n ${e.message}`); }
21
+ }
22
+ async function ta(name, fn) {
23
+ try { await fn(); passed++; console.log(` PASS ${name}`); }
24
+ catch (e) { failed++; console.log(` FAIL ${name}\n ${e.message}`); }
25
+ }
26
+
27
+ console.log('normalizeHostname (on-demand-TLS allow-list, P4):');
28
+
29
+ t('lowercases, trims, drops a trailing dot + a :port', () => {
30
+ assert.equal(P.normalizeHostname(' Mercury.Example.COM. '), 'mercury.example.com');
31
+ assert.equal(P.normalizeHostname('mercury.example.com:443'), 'mercury.example.com');
32
+ });
33
+
34
+ t('rejects a bare TLD / empty / malformed names (fail-closed → the caller denies)', () => {
35
+ assert.equal(P.normalizeHostname('com'), '');
36
+ assert.equal(P.normalizeHostname(''), '');
37
+ assert.equal(P.normalizeHostname(null), '');
38
+ assert.equal(P.normalizeHostname('-bad.example.com'), '');
39
+ assert.equal(P.normalizeHostname('bad-.example.com'), '');
40
+ assert.equal(P.normalizeHostname('has space.example.com'), '');
41
+ assert.equal(P.normalizeHostname('a..b.com'), '');
42
+ });
43
+
44
+ t('accepts a normal multi-label host', () => {
45
+ assert.equal(P.normalizeHostname('a.b.example.com'), 'a.b.example.com');
46
+ });
47
+
48
+ console.log('\nisProvisionedHostname (the Caddy `ask` allow-list, P4):');
49
+
50
+ // A fake db whose query returns the status row a lookup fn decides, keyed on $1.
51
+ function fakeDb(statusFor) {
52
+ return { query: async (_sql, params) => {
53
+ const s = statusFor(String(params[0]));
54
+ return { rows: s ? [{ status: s }] : [] };
55
+ } };
56
+ }
57
+
58
+ await ta('true ONLY for a provisioning|active instance; false for every other state or unknown', async () => {
59
+ const byHost = {
60
+ 'live.example.com': 'active',
61
+ 'mid.example.com': 'provisioning',
62
+ 'gone.example.com': 'torn_down',
63
+ 'req.example.com': 'requested',
64
+ 'err.example.com': 'error',
65
+ 'down.example.com': 'tearing_down',
66
+ };
67
+ const db = fakeDb((h) => byHost[h] || null);
68
+ assert.equal(await P.isProvisionedHostname(db, 'live.example.com'), true);
69
+ assert.equal(await P.isProvisionedHostname(db, 'mid.example.com'), true);
70
+ assert.equal(await P.isProvisionedHostname(db, 'gone.example.com'), false);
71
+ assert.equal(await P.isProvisionedHostname(db, 'req.example.com'), false);
72
+ assert.equal(await P.isProvisionedHostname(db, 'err.example.com'), false);
73
+ assert.equal(await P.isProvisionedHostname(db, 'down.example.com'), false);
74
+ assert.equal(await P.isProvisionedHostname(db, 'unknown.example.com'), false);
75
+ });
76
+
77
+ await ta('normalizes the SNI before matching, and never queries a malformed host (fail-closed, no DB hit)', async () => {
78
+ let queried = 0;
79
+ const db = { query: async (_sql, params) => {
80
+ queried++;
81
+ return { rows: params[0] === 'live.example.com' ? [{ status: 'active' }] : [] };
82
+ } };
83
+ assert.equal(await P.isProvisionedHostname(db, 'LIVE.example.com.:443'), true, 'normalized SNI matches');
84
+ const before = queried;
85
+ assert.equal(await P.isProvisionedHostname(db, 'com'), false);
86
+ assert.equal(await P.isProvisionedHostname(db, ''), false);
87
+ assert.equal(queried, before, 'no query issued for a malformed host');
88
+ });
89
+
90
+ console.log('\nmakeAskRateLimiter (tls-check ask rate limit, P4 SEC review of task 1945):');
91
+
92
+ function fakeRes() {
93
+ return {
94
+ _status: 200, _json: null, _headers: {},
95
+ set(k, v) { this._headers[k] = v; return this; },
96
+ status(c) { this._status = c; return this; },
97
+ json(b) { this._json = b; return this; },
98
+ };
99
+ }
100
+ function call(mw, ip) {
101
+ let nexted = false;
102
+ const res = fakeRes();
103
+ mw({ ip }, res, () => { nexted = true; });
104
+ return { nexted, res };
105
+ }
106
+
107
+ t('allows up to `max` per window, then 429s with Retry-After', () => {
108
+ let clock = 1_000_000;
109
+ const mw = RL.makeAskRateLimiter({ windowMs: 60_000, max: 3, now: () => clock });
110
+ for (let i = 0; i < 3; i++) {
111
+ const { nexted, res } = call(mw, '1.2.3.4');
112
+ assert.equal(nexted, true, `request ${i + 1} within cap passes`);
113
+ assert.equal(res._status, 200);
114
+ }
115
+ const over = call(mw, '1.2.3.4');
116
+ assert.equal(over.nexted, false, 'over-cap request is blocked (next not called)');
117
+ assert.equal(over.res._status, 429);
118
+ assert.ok(Number(over.res._headers['Retry-After']) >= 1, 'sets Retry-After');
119
+ assert.equal(over.res._json.error, 'rate_limited');
120
+ });
121
+
122
+ t('the window slides — after windowMs elapses, requests flow again', () => {
123
+ let clock = 0;
124
+ const mw = RL.makeAskRateLimiter({ windowMs: 1_000, max: 1, now: () => clock });
125
+ assert.equal(call(mw, 'a').nexted, true);
126
+ assert.equal(call(mw, 'a').nexted, false, 'second within the window is blocked');
127
+ clock += 1_001; // step past the window
128
+ assert.equal(call(mw, 'a').nexted, true, 'allowed again after the window slides');
129
+ });
130
+
131
+ t('per-IP buckets are independent', () => {
132
+ let clock = 5;
133
+ const mw = RL.makeAskRateLimiter({ windowMs: 60_000, max: 1, now: () => clock });
134
+ assert.equal(call(mw, 'ip-a').nexted, true);
135
+ assert.equal(call(mw, 'ip-a').nexted, false, 'ip-a is capped');
136
+ assert.equal(call(mw, 'ip-b').nexted, true, 'ip-b has its own bucket');
137
+ });
138
+
139
+ console.log('\nP1 pure helpers (sanity):');
140
+
141
+ t('isValidSlug basic contract', () => {
142
+ assert.equal(P.isValidSlug('mercury'), true);
143
+ assert.equal(P.isValidSlug('a'), false); // too short
144
+ assert.equal(P.isValidSlug('-x'), false); // leading dash
145
+ assert.equal(P.isValidSlug('a--b'), false); // double dash
146
+ });
147
+
148
+ console.log('\nGitHub App Manifest — pure builders (task 2080, ADR 0133):');
149
+
150
+ t('githubAppName prefers the DOMAIN (unique + reads on the consent screen)', () => {
151
+ // The domain is globally unique, so "demo" never collides once named after its host.
152
+ assert.equal(P.githubAppName({ domain: 'demo.cloudbongos.com', brand: 'Cloud Bongos', project: 'demo' }), 'demo.cloudbongos.com');
153
+ assert.equal(P.githubAppName({ domain: 'Mercury.Example.com.:443', brand: 'X', project: 'mercury' }), 'mercury.example.com'); // normalized (case/port/dot)
154
+ });
155
+
156
+ t('githubAppName falls back to brand-qualified project when there is no usable domain', () => {
157
+ // no domain → "<brand> - <project>", still collision-resistant vs a bare slug
158
+ assert.equal(P.githubAppName({ brand: 'Cloud Bongos', project: 'demo' }), 'Cloud Bongos - demo');
159
+ // a domain over GitHub's 34-char limit → fall back to brand-qualified project
160
+ assert.equal(P.githubAppName({ domain: 'a-really-long-subdomain.enterprise.example.com', brand: 'Cloud Bongos', project: 'demo' }), 'Cloud Bongos - demo');
161
+ assert.equal(P.githubAppName({ brand: '', project: 'mercury' }), 'mercury'); // no brand → project
162
+ assert.equal(P.githubAppName({ brand: 'Cloud Bongos', project: '' }), 'Cloud Bongos'); // no project → brand
163
+ assert.equal(P.githubAppName({}), 'Cloud Bongos'); // last-resort default
164
+ const long = P.githubAppName({ brand: 'Cloud Bongos', project: 'x'.repeat(50) });
165
+ assert.ok(long.length <= 34 && long.startsWith('Cloud Bongos - '), `capped + brand kept: ${long}`);
166
+ });
167
+
168
+ t('buildGithubAppManifest points redirect at the MOTHERSHIP, callbacks at the INSTANCE, minimal perms, domain-named', () => {
169
+ const m = P.buildGithubAppManifest({
170
+ instanceName: 'mercury', slug: 'mercury', brand: 'Cloud Bongos',
171
+ instanceDomain: 'mercury.example.com', mothershipOrigin: 'https://cloudbongos.com',
172
+ });
173
+ assert.equal(m.name, 'mercury.example.com'); // the App is named after its own domain
174
+ assert.equal(m.url, 'https://mercury.example.com');
175
+ // redirect_url = the control-plane callback (where GitHub returns the code)
176
+ assert.equal(m.redirect_url, 'https://cloudbongos.com/api/bongos/provisioning/github-app/callback');
177
+ // callback_urls = the INSTANCE's own sign-in callback, BOTH prefixes for robustness
178
+ assert.deepEqual(m.callback_urls, [
179
+ 'https://mercury.example.com/api/gds/auth/web/callback',
180
+ 'https://mercury.example.com/api/bongos/auth/web/callback',
181
+ ]);
182
+ // sign-in needs no repo/account permissions; no active webhook; not public
183
+ assert.deepEqual(m.default_permissions, {});
184
+ assert.equal(m.hook_attributes.active, false);
185
+ assert.equal(m.public, false);
186
+ });
187
+
188
+ t('buildGithubAppManifest tolerates a trailing slash on the mothership origin', () => {
189
+ const m = P.buildGithubAppManifest({ instanceName: 'M', slug: 'm', instanceDomain: 'm.example.com', mothershipOrigin: 'https://cloudbongos.com/' });
190
+ assert.equal(m.redirect_url, 'https://cloudbongos.com/api/bongos/provisioning/github-app/callback');
191
+ });
192
+
193
+ t('manifestPostUrl: personal vs org, state url-encoded', () => {
194
+ assert.equal(P.manifestPostUrl({ account: 'personal', state: 'abc' }), 'https://github.com/settings/apps/new?state=abc');
195
+ assert.equal(P.manifestPostUrl({ account: 'org', orgLogin: 'acme', state: 'a b' }), 'https://github.com/organizations/acme/settings/apps/new?state=a%20b');
196
+ // org requested but no org login → falls back to personal (never a broken URL)
197
+ assert.equal(P.manifestPostUrl({ account: 'org', state: 'x' }), 'https://github.com/settings/apps/new?state=x');
198
+ });
199
+
200
+ console.log('\nGitHub App Manifest — data access (fake { query }):');
201
+
202
+ await ta('attachManifestCode is single-use + TTL-guarded (only a pending, un-expired row advances)', async () => {
203
+ const seen = [];
204
+ const db = { query: async (sql, params) => {
205
+ seen.push({ sql, params });
206
+ // Emulate the guarded UPDATE ... WHERE status='pending' AND expires_at > now():
207
+ // the fake returns a row only when the caller's id is the "fresh" one.
208
+ if (/UPDATE provisioning_oauth_manifests/.test(sql) && /status = 'code_received'/.test(sql)) {
209
+ return { rows: params[0] === 1 ? [{ id: 1, status: 'code_received', github_code: params[1] }] : [] };
210
+ }
211
+ return { rows: [] };
212
+ } };
213
+ const fresh = await P.attachManifestCode(db, 1, 'CODE123');
214
+ assert.ok(fresh && fresh.status === 'code_received' && fresh.github_code === 'CODE123');
215
+ const stale = await P.attachManifestCode(db, 2, 'CODE456'); // wrong/expired → no row
216
+ assert.equal(stale, null);
217
+ // the guard (status + expiry) lives in the SQL, not JS — assert it's actually there
218
+ assert.match(seen[0].sql, /status = 'pending' AND expires_at > now\(\)/);
219
+ });
220
+
221
+ await ta('claimNextOAuthManifest flips code_received→converting, gated on an ACTIVE instance, SKIP LOCKED', async () => {
222
+ let sql = '';
223
+ const db = { query: async (q) => { sql = q; return { rows: [{ id: 7, status: 'converting' }] }; } };
224
+ const row = await P.claimNextOAuthManifest(db);
225
+ assert.equal(row.id, 7);
226
+ assert.match(sql, /SET status = 'converting'/);
227
+ assert.match(sql, /FOR UPDATE OF m SKIP LOCKED/);
228
+ assert.match(sql, /m\.status = 'code_received' AND i\.status = 'active'/);
229
+ });
230
+
231
+ console.log(`\nprovisioning module: ${passed} passed, ${failed} failed`);
232
+ process.exit(failed ? 1 : 0);
File without changes
@@ -1,44 +1,15 @@
1
- # `security` module — red-team reports + bounty + the `security` kernel port
1
+ # @bongos/core — installed platform dependency (not your project's CLAUDE.md)
2
2
 
3
- > The **fifth CORE-domain** carved into a module (BV1.R79, [ADR 0093](../../docs/adr/<redacted>.md) §1) — after memory (R72), grading (R73), economy (R77), and ideas (R78). This is **<redacted>**, not <redacted> ([02-module-map §5](../../docs/design/modular-architecture/02-module-map.md)): the optional, rank-gated red-team-reports + bounty surface. A clean slice — its SQL already lived in a dedicated `security.js` (not the `db.js` monolith), so the carve is a git-move + a route split + a doorway re-point, OTB byte-identical. `default: true` in `module.json` — every instance can take red-team reports.
3
+ This file is a placeholder shipped inside the **@bongos/core** package. The core's own
4
+ internal agent notes are intentionally omitted from the published artifact so they cannot
5
+ load into your instance's context.
4
6
 
5
- Owns the **red-team / vulnerability-report** lifecycle (file → adjudicate → pay) and the **bounty payout table**: the `/builder-redteam` skill POSTs a report (Metic+), the Archon dashboard reads + adjudicates it (confirm / dispute / fix / link-fix), and the env-aware per-severity drachmae schedule (the bounty table) is the single source of truth for "how much for a confirmed report."
7
+ **It does not govern this repository.** Your own root `CLAUDE.md`, your `.claude/`
8
+ directory, and the live Bongos database are the source of truth for how work happens here
9
+ and who may do what.
6
10
 
7
- ## The boundary (the one rule)
11
+ You are seeing this only because an agent opened a file under `node_modules/@bongos/core/`.
12
+ Nothing in here needs to be read to use the platform — run `bongos help` for commands.
8
13
 
9
- This module reaches core **only** through the published doorway [`../../src/module-api.js`](../../src/module-api.js) (`coreVersion ^1.8.0`). It must **never** `require()` a core internal directly, and never another module. Core, in turn, must never `require()` a file in here — it reaches the bounty schedule only through the `security` port (below). Both directions are enforced by the BV1.R44/R45 fitness checks.
10
-
11
- ## <redacted> stays KERNEL (the headline boundary call)
12
-
13
- [02-module-map §5](../../docs/design/modular-architecture/02-module-map.md) splits "security" in two, and only one half is here:
14
-
15
- - **PROPERTY → kernel (NOT moved):** the authz / trust-boundary machinery — `permission-path-check.js`, `route-rank-check.js`, `requireRank`/`requireBuilder` (`auth.js`). Imported everywhere, enforced by composition + fitness functions, never "called." Untouched by this carve.
16
- - **PROPERTY → kernel (NOT moved, the subtle part):** the **threat-model + engagement-doc plane** also stayed in core `src/bongos/routes/security.js`: `GET /security/adr` (reads the real ADR 0020 threat model off the droplet, Metic+) and `GET/POST /security/docs` (the Archon-only exploit-detail engagement-report store, backed by the core `db.js` `security_docs` queries). These document **defenses + exploits** — security-the-property's operational surface, not the bounty feature. They keep their `EXPECTED_RANKS` pins in `route-rank-check.js`.
17
- - **FEATURE → this module:** red-team / vulnerability **reports** + **bounty** (`/security/reports*`) and the payout table. Self-contained, optional, rank-gated.
18
-
19
- The core `src/bongos/routes/security.js` therefore did **not** disappear — it was *split*: the report/bounty routes moved here; the adr/docs (property) routes stayed.
20
-
21
- ## The `security` kernel PORT
22
-
23
- - **Provides — port `security`** → `{ bountyTableDetailed, bountyTable }`. Registered in `routes/reports.js`'s factory (the loader runs it at boot, and again whenever a test rebuilds the GDS router — `hasProvider`-guarded so re-instantiation never trips the single-provider guard). Mirrors `modules/economy/routes/reward.js`.
24
- - **Why:** the **one** core consumer of the security feature is `src/bongos/routes/public.js`'s `GET /public/bounty-table` (the public payout schedule the hall renders). Core can't `require()` a module, so it resolves `seams.resolveOptional('security').bountyTableDetailed()` — the economy `reward`-port precedent for a core read route reaching module data.
25
-
26
- ## Files
27
-
28
- | File | What |
29
- |---|---|
30
- | `security.js` | the bounty payout table (`bountyTable`/`bountyTableDetailed`, env-overridable per severity via `GDS_BOUNTY_*`) + the `vulnerability_reports` domain: `createVulnerabilityReport` (the `/builder-redteam` writer), `listVulnerabilityReports({scope})`, and the Archon adjudication transitions (`confirm`/`dispute`/`markFixed`/`linkFix`). Reaches the shared pool through the doorway. |
31
- | `routes/reports.js` | `POST /security/reports` (Metic+ file), `GET /security/reports` (Archon list), `POST /security/reports/:id/{confirm,dispute,fix,link-fix}` (Archon adjudicate). Its factory **registers the `security` port**. |
32
-
33
- ## NOT in this module (deliberately left in core)
34
-
35
- - **The threat-model + engagement-doc routes** — `src/bongos/routes/security.js` keeps `GET /security/adr` + `GET/POST /security/docs` (security-the-property; see the boundary call above). They use the core `db.js` `security_docs` queries and `auth`/`_helpers` directly — core code, not this module.
36
- - **The `/builder-redteam` skill** — `.claude/skills/builder-redteam/` travels with the feature but stays under `.claude/skills/` (it just POSTs to `/api/gds/security/reports`, which this module now mounts).
37
-
38
- ## Tables
39
-
40
- None owned. `vulnerability_reports` (migration 030) and `security_docs` (the property-side store) were created before the module system and stay in core `migrations/` (the `schema_migrations` stem-key makes a later relocation a no-op). This module owns the *report/bounty queries*; any **new** security-feature migration belongs in `modules/security/migrations/` namespaced `security_*` (the additive rule).
41
-
42
- ## Tests
43
-
44
- `tests/security_reports.mjs` exercises the report/bounty domain (the `security.js` helpers + the bounty table). The route-rank fitness check (`tests/module_route_rank.mjs`) asserts every write route in `routes/reports.js` is rank-gated. The doorway/boundary checks (`tests/fitness.mjs`, `tests/module_loader.mjs`) discover `security` as a module. See [ADR 0093](../../docs/adr/<redacted>.md).
14
+ _To work ON the platform core itself, use the standalone @bongos/core source repository,
15
+ not this vendored copy._
@@ -146,10 +146,19 @@ function redactHighEntropy(text) {
146
146
  const ENV_SECRET_KEY_RE =
147
147
  /(?:api[_-]?key|secret|token|password|passwd|pwd|passphrase|access[_-]?key|private[_-]?key|client[_-]?secret|credential|cookie)/i;
148
148
  const ENV_SECRET_PREFIX_RE = /^(?:gds_|pms_|mac_|apple|anthropic|stripe|cf_|cloudflare|do_token|digitalocean)/i;
149
+ // Benign shell/runtime vars whose NAME trips a secret-word substring but whose VALUE is
150
+ // never a credential. PWD and OLDPWD contain "pwd" (the password alternative in
151
+ // ENV_SECRET_KEY_RE), yet hold a working-directory PATH — so without this exclusion a
152
+ // legitimate publish fails closed whenever that path appears verbatim in a functional file
153
+ // (e.g. a /Users/<name> anti-pattern comment), passing or failing purely on the operator's
154
+ // last `cd` (task 2130). Exact-match only, so real password vars (MYSQL_PWD, DB_PWD, …)
155
+ // still match below.
156
+ const ENV_BENIGN_KEY_RE = /^(?:PWD|OLDPWD)$/;
149
157
  function collectEnvSecrets(env = process.env) {
150
158
  const out = [];
151
159
  for (const [k, v] of Object.entries(env || {})) {
152
160
  if (typeof v !== 'string' || v.length < 8) continue;
161
+ if (ENV_BENIGN_KEY_RE.test(k)) continue;
153
162
  if (ENV_SECRET_KEY_RE.test(k) || ENV_SECRET_PREFIX_RE.test(k)) out.push(v);
154
163
  }
155
164
  return out;
@@ -196,6 +205,7 @@ module.exports = {
196
205
  redactHighEntropy,
197
206
  ENV_SECRET_KEY_RE,
198
207
  ENV_SECRET_PREFIX_RE,
208
+ ENV_BENIGN_KEY_RE,
199
209
  collectEnvSecrets,
200
210
  scrubSecrets,
201
211
  };
@@ -1,39 +1,15 @@
1
- # `sessions` module — session-records corpus, BFG, session-pulse
1
+ # @bongos/core — installed platform dependency (not your project's CLAUDE.md)
2
2
 
3
- > A CORE-domain carve (BV1.R82, [ADR 0093](../../docs/adr/<redacted>.md) §1) — the seventh tranche-2 domain extracted from the GDS core, after onboarding (R81). `default: true` — every instance records its builders' sessions for the BFG, so it mounts unconditionally unless an instance disables it.
3
+ This file is a placeholder shipped inside the **@bongos/core** package. The core's own
4
+ internal agent notes are intentionally omitted from the published artifact so they cannot
5
+ load into your instance's context.
4
6
 
5
- Owns the **session_records corpus** + its data plane (the `/sessions` upload + the Archon-gated cross-builder search/deep-dive), the **BFG** cross-builder "read-the-room" scorer ([ADR 0026](../../docs/adr/<redacted>.md) §6B), and the **session-pulse / stale-claim** heartbeat helpers.
7
+ **It does not govern this repository.** Your own root `CLAUDE.md`, your `.claude/`
8
+ directory, and the live Bongos database are the source of truth for how work happens here
9
+ and who may do what.
6
10
 
7
- ## The boundary (the one rule)
11
+ You are seeing this only because an agent opened a file under `node_modules/@bongos/core/`.
12
+ Nothing in here needs to be read to use the platform — run `bongos help` for commands.
8
13
 
9
- This module reaches core **only** through the published doorway `../../src/module-api.js` (`coreVersion ^1.10.0`). It must **never** `require()` a core internal (`../../src/bongos/db`, `../../src/bongos/auth`, `../../src/bongos/pool`, …) directly, and **never** another module. Core, in turn, must never `require()` a file in here — the route mount left `src/bongos/routes.js`; `moduleLoader.mountModuleRoutes` discovers + mounts it now. Both directions are enforced by the BV1.R44/R45 fitness checks.
10
-
11
- ## No port (the memory shape, not the economy shape)
12
-
13
- Unlike economy / security / builder-settings / onboarding, sessions registers **no kernel port** (`provides: []`) — exactly like `memory`. **Core never calls back into sessions:** the ship-time session-digest write is an **HTTP POST** to this module's own `/sessions` route (`scripts/gds/ship.js` → `POST /api/gds/sessions`), not a `require`. So there is nothing for a port to expose. The session-records db slice moved here cleanly with its sole consumer (the route).
14
-
15
- The **one outward edge** is the economy **`reward`** kernel port this module *consumes* (resolves, never imports): `upsertSessionRecordWithReward` (in `db.js`) and `POST /sessions` (in `routes/sessions.js`) both `seams.resolveOptional('reward')` — to award the cost-plus session token reward inside the upsert transaction (passing the tx `client`) and to sum the drachmae credited for the owned task(s). That is the kernel-mediated module↔module seam (ADR 0083 / 0093 §2).
16
-
17
- ## Files
18
-
19
- | File | What |
20
- |---|---|
21
- | `db.js` | the `session_records` db slice — `upsertSessionRecord[WithReward]` (the token-reward txn resolves the `reward` port), `searchSessions` / `summarizeSessions` / `getSessionById` / `listSessionsByBuilder`, `filterTaskIdsClaimedBy`, the ORDER-BY whitelist + limit/offset clamps + the shared filter clause. Reaches Postgres via the doorway `pool` + `withTx`. |
22
- | `bfg.js` | the BFG's PURE scorers (`scoreInteractionQuality` / `scoreMemoryQuality`) + extractors + the thin `gatherInteractionSignals` DB gatherer (ADR 0026 §6B). Reads via the doorway (`pool`, `getBuilderById`, `builderRollingAvgGrade`). |
23
- | `pulse.js` | session-pulse + stale-claim heartbeat helpers (`recordPulse` / `sweepStaleClaims` / `getStaleClaimsForBuilder`). The CLI shell stays at `scripts/gds/pulse.js` and requires this lib directly. |
24
- | `routes/sessions.js` | `/sessions` (upload — carries its own 4 MB `express.json`; core `routes.js` still skips the global parser for that path), `/sessions/search` + `/sessions/:id` (Archon-gated cross-builder, fail-closed audited), `/sessions/mine`. |
25
-
26
- ## Tables (owned, not moved)
27
-
28
- `session_records` (+ its migrations) was created before the module system and stays in core `migrations/` (the schema-migrations stem-key makes a later relocation a no-op). The module owns the *queries*; any **new** session migration belongs in `modules/sessions/migrations/` namespaced `sessions_*` (the additive rule). The `session_pulses` table (migration 006) is likewise shared-schema; pulse.js owns its queries.
29
-
30
- ## NOT in this module
31
-
32
- - **`session_logs`** — the per-claim session LOG (the ship-time handoff record, `sealSessionLog`) stays in core `db.js` (lifecycle): it's written inside the claim/ship transaction and read by `routes/claims.js`/`builders.js`. Distinct from `session_records` (the BFG transcript corpus).
33
- - **`builder_sessions`** — the AUTH token table (`createSession`/`lookupSession`/…) is kernel (auth), not this domain.
34
- - **cost / reconcileSessionCosts** — the cost ledger + the session-cost reconciler graduated to [`modules/economy/`](../economy/CLAUDE.md) at BV1.R77. `scripts/gds/session-cost.js` (the SessionEnd cost hook) stays a CLI and posts to `/cost`.
35
- - **the session-domain CLIs** — `session.js`, `session-eval.js` (the 6D evaluator), `session-digest.js`, `session-cost.js`, `backfill-sessions.js`, `bfg-principal-token.js`, `repo-metrics.js`, `pulse.js` travel with the domain but stay under `scripts/` (a command travels with its domain — design 02§1). They're outside the kernel boundary, so `session-eval.js`'s sibling read of `modules/builder-settings/wandering-prefs` is fine (not a cross-module import — fitness scans `src/` + `modules/`, not `scripts/`).
36
-
37
- ## Tests
38
-
39
- `tests/session_search.mjs` (ORDER-BY/clamp guards), `tests/session_caps.mjs` (SR-17 disk-fill bounds — route + db, with the `reward` port registered), `tests/session_eval.mjs` + `tests/session_506_regression.mjs` (the evaluator), `tests/session_digest.mjs` + `tests/session_records.mjs` (the ship-time digest), `tests/bfg*.mjs` (the scorers), `tests/backfill_sessions.mjs` import from `modules/sessions/…` or exercise it. `bfg`/`bfg_write`/`session_records`/`backfill_sessions` are DB-dependent (the `INTEGRATION` set in `run-unit-tests.js`).
14
+ _To work ON the platform core itself, use the standalone @bongos/core source repository,
15
+ not this vendored copy._
@@ -1,47 +1,15 @@
1
- # `status-ui` module — the public status dashboard ("The Watch")
1
+ # @bongos/core — installed platform dependency (not your project's CLAUDE.md)
2
2
 
3
- > A CORE-domain carve (BV1.R84, [ADR 0093](../../docs/adr/<redacted>.md) §1) — the **first WEB-SURFACE module** ([02-module-map §6](../../docs/design/modular-architecture/02-module-map.md): "a web surface is first-class"). `default: true` — a Cloud Bongos instance ships its status page like it ships its hall, so it mounts unconditionally unless an instance disables it.
3
+ This file is a placeholder shipped inside the **@bongos/core** package. The core's own
4
+ internal agent notes are intentionally omitted from the published artifact so they cannot
5
+ load into your instance's context.
4
6
 
5
- Owns the public build-status dashboard served at **`status.<apex>`** (`status.example.com`) — the live up/down banner, version-progress rollups, cost summary, recent ships, the leaderboard, and the kinds-of-work ecosystem fingerprint. A no-framework page (`status.js`) that polls the read-only `/api/gds/public/*` surface every 60s.
7
+ **It does not govern this repository.** Your own root `CLAUDE.md`, your `.claude/`
8
+ directory, and the live Bongos database are the source of truth for how work happens here
9
+ and who may do what.
6
10
 
7
- ## The shape: a static surface, not a backend domain
11
+ You are seeing this only because an agent opened a file under `node_modules/@bongos/core/`.
12
+ Nothing in here needs to be read to use the platform — run `bongos help` for commands.
8
13
 
9
- Unlike every prior tranche-2 carve (economy/ideas/security/…), this module has **no `.js`, no `db.js` slice, no kernel port, and no routes**. It is purely:
10
- - `public/` — the static dashboard (HTML/CSS/JS/cursors/snapshots), moved verbatim from the old repo-root `public-status/`.
11
- - `module.json` — declares one **`contributes.webSurfaces`** entry `{ host: "status.", dir: "public" }`.
12
-
13
- ## The serving seam (how a web surface reaches its host with no core edit)
14
-
15
- The loader exposes `moduleWebSurfaces({ isEnabled })` ([`src/module-loader/loader.js`](../../src/module-loader/loader.js)) — the Host-routed twin of `hallWidgetScripts`/`mountModuleRoutes`. It returns `[{ key, host, dir(abs) }]` for each enabled module that declares `webSurfaces` **and** ships the directory on disk. `src/bongos/serve-internal.js` iterates that set and serves each surface on its `Host:` prefix with the **same** machinery the status page always used:
16
- - the in-memory **content-hash-stamped** index (`buildStampedHtml`, #521 — defeats Cloudflare's 4h TTL),
17
- - the per-instance **branding/theme injection** (`injectBrandingInto`),
18
- - fall-through to `express.static` for assets.
19
-
20
- `serve-internal.js` names **no module key** (kernel stays module-agnostic, [ADR 0083](../../docs/adr/<redacted>.md) §Decision #4); it routes purely off the manifest-declared `host` prefix. The `isStatusHost` export stays the literal `status.` predicate the entrypoints (`server.js`/`src/platform-server.js`) use to keep their apex tail off the status host; **inside** `mountInternalSurfaces` the generic `isModuleSurfaceHost` (any enabled web-surface host) is used, so the next carved surface (hall-UI, R85) needs no new guard.
21
-
22
- ## The boundary
23
-
24
- This module reaches **nothing** in core (no doorway require — it has no JS). Core, in turn, must never `require()` a file in here — `serve-internal.js` resolves the surface through `loader.moduleWebSurfaces`, never an import of `modules/status-ui/*`. Both directions are enforced by the BV1.R44/R45 fitness checks.
25
-
26
- ## The data it renders STAYS shared core (the judgment call)
27
-
28
- The `/api/gds/public/*` read endpoints the dashboard polls (`/public/uptime`, `/public/progress`, `/public/cost-summary`, `/public/grades`, `/public/recent-shipped`, `/public/leaderboard`, `/public/analytics/ecosystem`, `/public/tasks|ideas|blockers/:id`, plus `/versions/:id/done-when`) **did not move** — they live in `src/bongos/routes/public.js` and are consumed by the hall and the **off-box GitHub-Pages mirror** too, not just this surface. A public read shared by multiple surfaces is correctly core/shared, not status-exclusive (the same call the economy/ideas carves made for `routes/public.js`'s inline reads). So the carve moves the *surface*, not the reads it happens to render.
29
-
30
- ## Off-box mirror (operational)
31
-
32
- The LIVE page is served **off the droplet** for outage resilience ([ADR 0029](../../docs/adr/<redacted>.md)) — GitHub Pages from `example-owner/example-status`, fed by `scripts/status-mirror-sync.js`. That script (status-UI domain tooling that travels with its domain but stays a CLI under `scripts/`, the sessions/autonomy precedent) was re-pointed at `modules/status-ui/public/`. **Follow-up:** `.github/workflows/sync-status-mirror.yml` still triggers on `public-status/**` and must be re-pointed at `modules/status-ui/public/**` by an owner/Archon (the GitHub App can't push workflow files — learning 107).
33
-
34
- ## Files & tooling
35
-
36
- | Path | What |
37
- |---|---|
38
- | `public/index.html` · `public/status.js` · `public/style.css` | the dashboard (no build step, no framework) |
39
- | `public/cursors/` · `public/snapshots/` | bronze cursors + the world-render snapshots the page shows |
40
- | `public/README.md` | the surface's own notes (off-box mirror, asset cache-busting) |
41
- | `module.json` | `contributes.webSurfaces: [{ host: "status.", dir: "public" }]`; no port, no routes |
42
-
43
- Generators that WRITE into this surface live under `scripts/` and were re-pointed: `scripts/art/gen-cursors.py` (cursors), `scripts/snapshots/render-*.mjs` (snapshots), `scripts/status-mirror-sync.js` (off-box publish).
44
-
45
- ## Tests
46
-
47
- The loader seam is covered in `tests/module_loader.mjs` (`moduleWebSurfaces` — enabled lists, disabled vanishes, declared-but-not-shipped skipped, plus the live status-ui surface); the manifest `webSurfaces` validation in `tests/module_manifest.mjs`; the carve discovery-key membership in `tests/fitness.mjs` / `tests/module_loader.mjs` / `tests/modules.mjs`; and the carve proof in `scripts/gds/coreB-carve-proof.sh` (leg 4h).
14
+ _To work ON the platform core itself, use the standalone @bongos/core source repository,
15
+ not this vendored copy._
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.17.0",
3
+ "version": "1.17.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.17.0",
9
+ "version": "1.17.2",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.17.0",
3
+ "version": "1.17.2",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -10,7 +10,12 @@
10
10
  "exports": {
11
11
  ".": "./src/platform-server.js",
12
12
  "./serve-internal": "./src/bongos/serve-internal.js",
13
- "./module-api": "./src/module-api.js"
13
+ "./module-api": "./src/module-api.js",
14
+ "./bongos/api-path-404": "./src/bongos/api-path-404.js",
15
+ "./bongos/uptime-poller": "./src/bongos/uptime-poller.js",
16
+ "./module-loader/loader": "./src/module-loader/loader.js",
17
+ "./modules": "./src/modules.js",
18
+ "./module-seams": "./src/module-seams.js"
14
19
  },
15
20
  "engines": {
16
21
  "node": ">=22"