@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.
- package/.bongos-core.json +363 -312
- package/.claude/skills/blocker-review/SKILL.md +1 -1
- package/.claude/skills/builder-claim/SKILL.md +2 -0
- package/.claude/skills/builder-exit/SKILL.md +2 -2
- package/.claude/skills/builder-redteam/SKILL.md +3 -3
- package/.claude/skills/builder-setup/SKILL.md +2 -2
- package/.claude/skills/builder-start/SKILL.md +1 -1
- package/.claude/skills/dev/SKILL.md +1 -1
- package/.claude/skills/idea-triage/SKILL.md +2 -2
- package/.claude/skills/ideate/SKILL.md +1 -1
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/.claude/skills/new-project/SKILL.md +3 -0
- package/.claude/skills/paint/SKILL.md +1 -1
- package/CLAUDE.md +11 -238
- package/GOVERNANCE.md +1 -1
- package/README.md +1 -1
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +4 -4
- package/clients/bongos-client/bongos-client.global.js +14 -1
- package/clients/bongos-client/index.cjs +14 -1
- package/clients/bongos-client/index.d.ts +19 -2
- package/clients/bongos-client/index.mjs +11 -1
- package/clients/bongos-client/package.json +1 -1
- package/docs/adr/0001-stack-choice-single-vps.md +1 -1
- package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
- package/docs/adr/0019-red-team-bounty-payout-table.md +3 -3
- package/docs/adr/0020-security-model-public-stub.md +1 -1
- package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +1 -1
- package/docs/adr/0024-cloneable-repo-local-first-memory.md +1 -1
- package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +4 -4
- package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +3 -3
- package/docs/adr/0030-builders-hall-live-updates.md +1 -1
- package/docs/adr/0031-cloud-dev-environments-for-builders.md +4 -4
- package/docs/adr/0032-discord-mirror-of-gds.md +2 -2
- package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
- package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
- package/docs/adr/0044-per-box-live-game-preview.md +1 -1
- package/docs/adr/0054-session-token-reward-cost-plus.md +10 -10
- package/docs/adr/0057-container-cost-ledger.md +3 -3
- package/docs/adr/0062-medusa-instance-model-decoupling.md +4 -4
- package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +2 -2
- package/docs/adr/0085-remove-default-per-builder-spend-cap.md +1 -1
- package/docs/adr/0086-goal-scoped-work-hierarchy.md +1 -1
- package/docs/adr/0096-require-reward-before-workable.md +9 -9
- package/docs/adr/0097-retroactive-reward-backfill.md +3 -3
- package/docs/adr/0100-two-repo-instance-model.md +1 -1
- package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +3 -3
- package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +3 -3
- package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +1 -1
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +6 -6
- package/docs/adr/0133-github-app-manifest-onboarding-signin.md +2 -0
- package/docs/adr/0134-private-first-npm-distribution.md +20 -6
- package/docs/adr/0135-module-upstream-submission-interim-queue.md +34 -0
- package/docs/adr/0136-update-channel-subscription-policy.md +51 -0
- package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +40 -0
- package/docs/adr/0138-bundled-module-deps-declared-per-module.md +45 -0
- package/docs/adr/README.md +14 -10
- package/docs/api/openapi.json +315 -6
- package/docs/api-reference.md +10 -5
- package/docs/architecture.md +3 -3
- package/docs/branding-contract.md +5 -4
- package/docs/canonical-permissions.md +1 -1
- package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
- package/docs/file-map.md +20 -19
- package/docs/handoff-template.md +2 -2
- package/docs/modules-contract.md +8 -1
- package/docs/onboarding/diagrams/README.md +7 -7
- package/docs/onboarding/drachmae-and-karma.md +18 -18
- package/docs/onboarding/primer.md +9 -9
- package/docs/recipes/audit-replay.md +2 -2
- package/docs/recipes/builder-box-lifecycle.md +2 -2
- package/docs/recipes/claude-design-loop.md +1 -1
- package/docs/recipes/instance-deploy.md +4 -4
- package/docs/recipes/instance-first-run.md +51 -0
- package/docs/recipes/ops-gotchas.md +1 -1
- package/docs/recipes/packaging-the-core.md +5 -5
- package/docs/recipes/private-npm-distribution.md +39 -12
- package/docs/recipes/self-host.md +1 -1
- package/docs/recipes/staging-environment.md +1 -1
- package/docs/recipes/upgrading-the-core.md +63 -8
- package/docs/research/cursor-competitive-analysis.md +1 -1
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
- package/migrations/core_191_module_submissions.sql +45 -0
- package/modules/autonomy/CLAUDE.md +11 -45
- package/modules/builder-settings/CLAUDE.md +11 -54
- package/modules/dev-box/CLAUDE.md +11 -34
- package/modules/discord/CLAUDE.md +11 -46
- package/modules/discord/module.json +2 -1
- package/modules/economy/CLAUDE.md +11 -44
- package/modules/economy/achievements.js +9 -1
- package/modules/economy/credits.js +10 -1
- package/modules/grading/CLAUDE.md +11 -38
- package/modules/hall-ui/CLAUDE.md +11 -57
- package/modules/hall-ui/public/builders.js +71 -4
- package/modules/hall-ui/public/goal-inbox.js +191 -0
- package/modules/hall-ui/public/goals-page.js +227 -22
- package/modules/hall-ui/public/goals.html +16 -0
- package/modules/hall-ui/public/hall-widgets.js +14 -1
- package/modules/hall-ui/public/index.html +3 -0
- package/modules/hall-ui/public/new-project-wizard.js +6 -4
- package/modules/hall-ui/public/style.css +43 -0
- package/modules/ideas/CLAUDE.md +11 -33
- package/modules/lifecycle/CLAUDE.md +11 -59
- package/modules/lifecycle/routes/goals.js +80 -0
- package/modules/memory/CLAUDE.md +11 -36
- package/modules/onboarding/CLAUDE.md +11 -51
- package/modules/provisioning/CLAUDE.md +15 -0
- package/modules/provisioning/migrations/provisioning_001_tables.sql +160 -0
- package/modules/provisioning/migrations/provisioning_002_standalone_shape.sql +11 -0
- package/modules/provisioning/migrations/provisioning_003_github_app_manifest.sql +95 -0
- package/modules/provisioning/module.json +17 -0
- package/modules/provisioning/onboard-plan.js +37 -0
- package/modules/provisioning/provisioning.js +539 -0
- package/modules/provisioning/rate-limit.js +35 -0
- package/modules/provisioning/routes/provisioning.js +607 -0
- package/modules/provisioning/tests/provisioning.mjs +232 -0
- package/modules/provisioning/ui/.gitkeep +0 -0
- package/modules/security/CLAUDE.md +11 -40
- package/modules/security/secret-scrub.js +10 -0
- package/modules/sessions/CLAUDE.md +11 -35
- package/modules/status-ui/CLAUDE.md +11 -43
- package/package-lock.json +2 -2
- package/package.json +7 -2
- package/public-landing/create.html +58 -12
- package/public-landing/index.html +40 -21
- package/scripts/discord/README.md +1 -1
- package/scripts/gds/CLAUDE.md +11 -140
- package/scripts/gds/claim.js +98 -48
- package/scripts/gds/claude-materialize.js +174 -20
- package/scripts/gds/claude-md-template.js +84 -0
- package/scripts/gds/cli-lib.js +112 -2
- package/scripts/gds/cutover-codemod.js +241 -0
- package/scripts/gds/fitness.js +54 -0
- package/scripts/gds/gen-api-client.js +10 -10
- package/scripts/gds/init.js +112 -16
- package/scripts/gds/lint-infra-exec.js +70 -0
- package/scripts/gds/lint-workflow-exec.js +63 -0
- package/scripts/gds/migration-namespace.js +1 -1
- package/scripts/gds/mirror-lineage-check.js +14 -5
- package/scripts/gds/mirror-redact.js +8 -0
- package/scripts/gds/module.js +118 -4
- package/scripts/gds/npmrc.js +93 -0
- package/scripts/gds/onboard.js +17 -24
- package/scripts/gds/package-core.js +74 -17
- package/scripts/gds/provision.js +102 -17
- package/scripts/gds/publish-manifest.js +60 -2
- package/scripts/gds/run-routine.js +51 -13
- package/scripts/gds/run-unit-tests.js +4 -0
- package/scripts/gds/seed-adopt-tasks.js +1 -1
- package/scripts/gds/status.js +6 -1
- package/scripts/gds/task.js +166 -0
- package/scripts/gds/update-channel.js +186 -0
- package/scripts/gds/upgrade.js +172 -25
- package/src/bongos/CLAUDE.md +11 -60
- package/src/bongos/auth.js +24 -0
- package/src/bongos/module-submissions.js +56 -0
- package/src/bongos/route-rank-check.js +6 -0
- package/src/bongos/routes/CLAUDE.md +11 -39
- package/src/bongos/routes/auth.js +6 -1
- package/src/bongos/routes/modules.js +123 -21
- package/src/bongos/serve-internal.js +10 -1
- package/src/branding.js +1 -0
- package/src/instance-config.js +1 -1
- package/src/module-api.js +24 -1
- package/src/module-loader/loader.js +36 -4
- package/src/module-loader/manifest-schema.js +16 -1
- package/tests/branding.mjs +11 -0
- package/tests/claim_error_surface.mjs +107 -0
- package/tests/claude_materialize.mjs +125 -0
- package/tests/consumer_layout_boot.mjs +302 -0
- package/tests/cutover_codemod.mjs +184 -0
- package/tests/device_flow_start.mjs +96 -0
- package/tests/first_run_engine_check.mjs +79 -0
- package/tests/goal_membership_requests.mjs +33 -3
- package/tests/hook_core_resolve.mjs +125 -0
- package/tests/hook_user_agents.mjs +62 -0
- package/tests/infra_exec_paths.mjs +104 -0
- package/tests/init.mjs +106 -6
- package/tests/instance_mismatch_guard.mjs +101 -0
- package/tests/mirror_lineage_proof.mjs +14 -0
- package/tests/module_cli.mjs +130 -2
- package/tests/module_loader.mjs +42 -0
- package/tests/module_manifest.mjs +19 -0
- package/tests/module_submissions.mjs +135 -0
- package/tests/npmrc.mjs +105 -0
- package/tests/onboard.mjs +62 -0
- package/tests/package_core.mjs +76 -22
- package/tests/platform_boot.mjs +5 -2
- package/tests/provision.mjs +107 -8
- package/tests/provisioning_github_app_routes.mjs +44 -2
- package/tests/publish_manifest.mjs +52 -1
- package/tests/run_routine.mjs +26 -1
- package/tests/run_routine_two_root.mjs +77 -0
- package/tests/scrubber_corpus.mjs +17 -0
- package/tests/served_paths_shipped.mjs +2 -2
- package/tests/task_cli.mjs +99 -0
- package/tests/update_channel.mjs +186 -0
- package/tests/upgrade.mjs +220 -25
- package/tests/workflow_exec_paths.mjs +98 -0
- package/docs/design/container-exterior-tiles.md +0 -49
- package/docs/design/container-exterior.md +0 -81
- package/docs/design/container-interior.md +0 -77
- package/docs/design/world-map-scroll.md +0 -41
- package/docs/recipes/claude-gemini-bongos-doors.md +0 -155
- package/docs/recipes/cloudbongos-staging-gate.md +0 -139
- package/docs/recipes/cloudbongos-standup.md +0 -111
- package/docs/recipes/parallel-load-results-2026-05-30.md +0 -63
- package/docs/recipes/parallel-load-test.md +0 -32
- package/docs/recipes/pixel-art-pipeline.md +0 -411
- package/docs/recipes/staging-data.md +0 -50
- package/migrations/instance/018_pms_v3_planning_seed.sql +0 -131
- package/migrations/instance/080_gds_v4_planning_seed.sql +0 -75
- package/migrations/instance/110_medusa_planning_seed.sql +0 -70
- package/migrations/instance/120_bongos_v1_planning_seed.sql +0 -69
- package/migrations/instance/144_bongos_v1_module_system_criterion.sql +0 -26
- package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +0 -29
- package/migrations/instance/172_v2_world_planning_seed.sql +0 -163
- package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +0 -33
- package/migrations/instance/182_gdsv4_bongos_consolidation.sql +0 -132
- package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +0 -116
- package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +0 -69
- package/scripts/gds/art-stage.js +0 -152
- package/server.js +0 -212
- package/tests/art_key.mjs +0 -121
- package/tests/day_cycle.mjs +0 -197
- package/tests/snapshot_picker.mjs +0 -233
- package/tests/sprite_facing.mjs +0 -35
- package/tests/terrain_picker.mjs +0 -298
- package/tests/turn_in_place.mjs +0 -280
- 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
|
-
#
|
|
1
|
+
# @bongos/core — installed platform dependency (not your project's CLAUDE.md)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
1
|
+
# @bongos/core — installed platform dependency (not your project's CLAUDE.md)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
1
|
+
# @bongos/core — installed platform dependency (not your project's CLAUDE.md)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|