@bongos/core 1.19.998 → 1.19.1000
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 +105 -90
- package/.claude/skills/collab-review/SKILL.md +24 -13
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +7 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/adr/0330-an-ask-is-answered-where-it-was-asked.md +155 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +154 -4
- package/docs/api-reference.md +5 -3
- package/docs/copy-inventory.md +18 -13
- package/docs/copy-registry.json +70 -15
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/collab-lib.js +79 -0
- package/modules/hall-ui/public/collab.css +60 -0
- package/modules/hall-ui/public/collab.js +134 -5
- package/modules/lifecycle/help-requests.js +118 -8
- package/modules/lifecycle/migrations/lifecycle_011_help_request_replies.sql +113 -0
- package/modules/lifecycle/routes/help-requests.js +93 -13
- package/modules/provisioning/capacity.js +3 -3
- package/modules/provisioning/control-plane-guard.js +1 -1
- package/modules/provisioning/core-upgrade.js +7 -7
- package/modules/provisioning/migrations/provisioning_027_cloud_host_shape_rename.sql +58 -0
- package/modules/provisioning/onboard-plan.js +1 -1
- package/modules/provisioning/paid-shape-gate.js +28 -15
- package/modules/provisioning/provisioning.js +18 -14
- package/modules/provisioning/routes/repo-private.js +1 -1
- package/modules/provisioning/tests/liveness.mjs +16 -8
- package/modules/provisioning/tests/provisioning.mjs +1 -1
- package/modules/public-landing/public/projects.html +26 -27
- package/modules/ui-design/kit/fixtures/provisioning-instances.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/init.js +12 -4
- package/scripts/gds/onboard.js +1 -1
- package/scripts/gds/provision-capacity.js +6 -1
- package/scripts/gds/provision-config.js +6 -3
- package/scripts/gds/provision-core-upgrade.js +8 -8
- package/scripts/gds/provision-disconnect.js +2 -2
- package/scripts/gds/provision-process.js +2 -2
- package/scripts/gds/provision-repo-private.js +1 -1
- package/scripts/gds/provision-repo.js +13 -3
- package/scripts/gds/provision-teardown.js +2 -2
- package/scripts/gds/provision-units.js +2 -2
- package/scripts/gds/provision.js +6 -6
- package/src/module-api.js +1 -1
- package/tests/collab_page.mjs +140 -7
- package/tests/core_upgrade_door.mjs +1 -1
- package/tests/core_upgrade_runner.mjs +20 -20
- package/tests/deploy_page_projects.mjs +5 -5
- package/tests/hall_collab_world.mjs +6 -2
- package/tests/help_requests.mjs +201 -6
- package/tests/hub_disconnect_ui.mjs +4 -1
- package/tests/init.mjs +6 -6
- package/tests/onboard.mjs +5 -4
- package/tests/project_catalog_backfill.mjs +1 -1
- package/tests/project_catalog_bridge.mjs +2 -2
- package/tests/project_catalog_routes.mjs +4 -4
- package/tests/projects_hub.mjs +1 -1
- package/tests/projects_hub_pre_uat.mjs +22 -5
- package/tests/projects_hub_repo_private.mjs +1 -1
- package/tests/provision.mjs +32 -32
- package/tests/provision_byo_host.mjs +1 -1
- package/tests/provision_caddy_wiring.mjs +1 -1
- package/tests/provision_disconnect.mjs +3 -3
- package/tests/provision_repo_private.mjs +1 -1
- package/tests/provision_restart.mjs +5 -5
- package/tests/provision_settings_apply.mjs +4 -4
- package/tests/provision_teardown_address.mjs +2 -2
- package/tests/provisioning_capacity.mjs +40 -34
- package/tests/provisioning_capacity_gate.mjs +5 -12
- package/tests/provisioning_default_domain.mjs +1 -1
- package/tests/provisioning_domain_attach.mjs +4 -4
- package/tests/provisioning_domain_unique.mjs +2 -2
- package/tests/provisioning_error_note_scope.mjs +1 -1
- package/tests/provisioning_module_selection.mjs +5 -5
- package/tests/provisioning_onboard_mode.mjs +2 -2
- package/tests/provisioning_paid_shape_gate.mjs +57 -44
- package/tests/provisioning_repo_private_route.mjs +1 -1
- package/tests/provisioning_restart.mjs +11 -4
- package/tests/provisioning_settings_apply.mjs +12 -5
- package/tests/provisioning_settings_env.mjs +1 -1
- package/tests/provisioning_teardown_intent.mjs +3 -3
- package/tests/public_landing_projects.mjs +13 -6
- package/tests/upgrade.mjs +2 -2
|
@@ -89,10 +89,17 @@ t('RESTART_FROM is exactly {active} — every other state is already served by t
|
|
|
89
89
|
}
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
-
t('RESTART_SHAPES is the runner-reachable pair
|
|
93
|
-
assert.deepEqual([...provisioning.RESTART_SHAPES].sort(), ['
|
|
94
|
-
assert.ok(!provisioning.RESTART_SHAPES.has('dedicated'));
|
|
92
|
+
t('RESTART_SHAPES is the runner-reachable pair, and the set is a subset of HOSTING_SHAPES', () => {
|
|
93
|
+
assert.deepEqual([...provisioning.RESTART_SHAPES].sort(), ['cloud-host', 'co-tenant']);
|
|
95
94
|
for (const s of provisioning.RESTART_SHAPES) assert.ok(provisioning.HOSTING_SHAPES.includes(s), `${s} is a real shape`);
|
|
95
|
+
// Was `!has('dedicated')`. Task 1004182 removed that value from HOSTING_SHAPES, which made
|
|
96
|
+
// the assertion vacuously true — it would now pass against any set at all. Name the shapes
|
|
97
|
+
// that still EXIST and are still unreachable, and assert they are real first, so this
|
|
98
|
+
// cannot rot the same way twice.
|
|
99
|
+
for (const unreachable of ['byo-host', 'control-plane']) {
|
|
100
|
+
assert.ok(provisioning.HOSTING_SHAPES.includes(unreachable), `${unreachable} must be a real shape or this assertion is vacuous`);
|
|
101
|
+
assert.ok(!provisioning.RESTART_SHAPES.has(unreachable), unreachable);
|
|
102
|
+
}
|
|
96
103
|
});
|
|
97
104
|
|
|
98
105
|
await ta('enqueueRestart: a free slot creates the intent', async () => {
|
|
@@ -190,7 +197,7 @@ function post(path) {
|
|
|
190
197
|
});
|
|
191
198
|
}
|
|
192
199
|
|
|
193
|
-
const ACTIVE = { owner_builder_id: 7, slug: 'spike', status: 'active', hosting_shape: '
|
|
200
|
+
const ACTIVE = { owner_builder_id: 7, slug: 'spike', status: 'active', hosting_shape: 'cloud-host', domain: 'spike.cloudbongos.com', settings: {} };
|
|
194
201
|
function reset(patch = {}) {
|
|
195
202
|
instance = { ...ACTIVE, ...patch };
|
|
196
203
|
enqueueCalls = []; enqueueResult = null; events = [];
|
|
@@ -72,10 +72,17 @@ t('SETTINGS_APPLY_FROM is exactly {active} — the provision intent cannot stand
|
|
|
72
72
|
assert.ok(!provisioning.PROVISION_ENQUEUE_FROM.has('active'), 'the premise the gate rests on');
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
t('SETTINGS_APPLY_SHAPES is the reachable pair
|
|
76
|
-
assert.deepEqual([...provisioning.SETTINGS_APPLY_SHAPES].sort(), ['
|
|
75
|
+
t('SETTINGS_APPLY_SHAPES is the reachable pair, and the set is a subset of HOSTING_SHAPES', () => {
|
|
76
|
+
assert.deepEqual([...provisioning.SETTINGS_APPLY_SHAPES].sort(), ['cloud-host', 'co-tenant']);
|
|
77
77
|
for (const s of provisioning.SETTINGS_APPLY_SHAPES) assert.ok(provisioning.HOSTING_SHAPES.includes(s), s);
|
|
78
|
-
assert
|
|
78
|
+
// The exclusion this used to assert was 'dedicated', which task 1004182 removed from
|
|
79
|
+
// HOSTING_SHAPES entirely — so that assertion became vacuously true and stopped testing
|
|
80
|
+
// anything. Name the shapes that DO exist and are still deliberately unreachable: nothing
|
|
81
|
+
// of ours runs a byo-host, and the control plane is not pushed settings by this path.
|
|
82
|
+
for (const unreachable of ['byo-host', 'control-plane']) {
|
|
83
|
+
assert.ok(provisioning.HOSTING_SHAPES.includes(unreachable), `${unreachable} must be a real shape or this assertion is vacuous`);
|
|
84
|
+
assert.ok(!provisioning.SETTINGS_APPLY_SHAPES.has(unreachable), unreachable);
|
|
85
|
+
}
|
|
79
86
|
});
|
|
80
87
|
|
|
81
88
|
await ta('enqueueSettingsApply: a fresh intent → created:true; the INSERT carries the settings-apply action', async () => {
|
|
@@ -208,7 +215,7 @@ function patch(path, body) {
|
|
|
208
215
|
});
|
|
209
216
|
}
|
|
210
217
|
|
|
211
|
-
const ACTIVE = { owner_builder_id: 7, slug: 'spike', status: 'active', hosting_shape: '
|
|
218
|
+
const ACTIVE = { owner_builder_id: 7, slug: 'spike', status: 'active', hosting_shape: 'cloud-host', domain: 'spike.cloudbongos.com', settings: {} };
|
|
212
219
|
function reset({ inst, as } = {}) {
|
|
213
220
|
instance = inst !== undefined ? inst : { ...ACTIVE };
|
|
214
221
|
builder = as || { id: 7, github_login: 'owner', rank: 'thetes' };
|
|
@@ -230,7 +237,7 @@ function receiptShape(push, { queued, restart, reachable }) {
|
|
|
230
237
|
|
|
231
238
|
console.log('\nPATCH /provisioning/instances/:id/settings — the enqueue:');
|
|
232
239
|
|
|
233
|
-
await ta('ACTIVE
|
|
240
|
+
await ta('ACTIVE cloud-host: the row is written FIRST, then a settings-apply intent queues; the receipt says a restart is coming', async () => {
|
|
234
241
|
reset();
|
|
235
242
|
const r = await patch('/provisioning/instances/18/settings', { platform_visibility: 'gated' });
|
|
236
243
|
assert.equal(r.status, 200, r.raw);
|
|
@@ -168,7 +168,7 @@ t('the runner writes them into web.env alongside PORT and PGDATABASE', () => {
|
|
|
168
168
|
const provision = require('../scripts/gds/provision.js');
|
|
169
169
|
const body = provision.webEnvBody(
|
|
170
170
|
{ slug: 'spike-harbor', port: 3010, db_name: 'spike-harbor', target_ref: 'jaxri/spike-harbor',
|
|
171
|
-
hosting_shape: '
|
|
171
|
+
hosting_shape: 'cloud-host', settings: { platform_visibility: 'gated' } }, {});
|
|
172
172
|
assert.match(body, /_PLATFORM_VISIBILITY=gated/);
|
|
173
173
|
assert.match(body, /_JOINABILITY=apply/);
|
|
174
174
|
assert.match(body, /PORT=3010/, 'the rest of the file is untouched');
|
|
@@ -250,7 +250,7 @@ console.log('\nPOST /provisioning/instances — no provision intent for an insta
|
|
|
250
250
|
await ta('re-requesting an ACTIVE instance does NOT enqueue a provision intent (the enabling misstep, fixed)', async () => {
|
|
251
251
|
createResult = { instance: { id: 42, owner_builder_id: 7, slug: 'spike', status: 'active' }, created: false };
|
|
252
252
|
enqueueIntentCalls = [];
|
|
253
|
-
const res = await post('/provisioning/instances', { slug: 'spike', hosting_shape: '
|
|
253
|
+
const res = await post('/provisioning/instances', { slug: 'spike', hosting_shape: 'cloud-host' });
|
|
254
254
|
assert.equal(res.status, 200, res.raw);
|
|
255
255
|
assert.equal(enqueueIntentCalls.length, 0, 'no provision intent was enqueued');
|
|
256
256
|
assert.equal(res.body.queued, false);
|
|
@@ -260,7 +260,7 @@ await ta('re-requesting an ACTIVE instance does NOT enqueue a provision intent (
|
|
|
260
260
|
await ta('re-requesting an ERRORED instance still enqueues (the retry path is preserved)', async () => {
|
|
261
261
|
createResult = { instance: { id: 42, owner_builder_id: 7, slug: 'spike', status: 'error' }, created: false };
|
|
262
262
|
enqueueIntentCalls = [];
|
|
263
|
-
const res = await post('/provisioning/instances', { slug: 'spike', hosting_shape: '
|
|
263
|
+
const res = await post('/provisioning/instances', { slug: 'spike', hosting_shape: 'cloud-host' });
|
|
264
264
|
assert.equal(res.status, 200, res.raw);
|
|
265
265
|
assert.equal(enqueueIntentCalls.length, 1, 'the provision retry was enqueued');
|
|
266
266
|
assert.equal(enqueueIntentCalls[0].action, 'provision');
|
|
@@ -269,7 +269,7 @@ await ta('re-requesting an ERRORED instance still enqueues (the retry path is pr
|
|
|
269
269
|
await ta('a FRESH instance always enqueues its provision intent (unchanged happy path)', async () => {
|
|
270
270
|
createResult = { instance: { id: 43, owner_builder_id: 7, slug: 'fresh', status: 'requested' }, created: true };
|
|
271
271
|
enqueueIntentCalls = [];
|
|
272
|
-
const res = await post('/provisioning/instances', { slug: 'fresh', hosting_shape: '
|
|
272
|
+
const res = await post('/provisioning/instances', { slug: 'fresh', hosting_shape: 'cloud-host' });
|
|
273
273
|
assert.equal(res.status, 201, res.raw);
|
|
274
274
|
assert.equal(enqueueIntentCalls.length, 1);
|
|
275
275
|
assert.equal(enqueueIntentCalls[0].action, 'provision');
|
|
@@ -193,14 +193,21 @@ test('the manual-repo step states placement and visibility — the owner never h
|
|
|
193
193
|
assert.match(body, /public or private/i, 'visibility must be stated in our copy, not first seen on GitHub');
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
-
test('teardown copy states what the runner actually does
|
|
196
|
+
test('teardown copy states what the runner actually does (F17)', async () => {
|
|
197
197
|
const { body } = await request('/projects', 'game.test.local');
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
// The DECIDED policy is unchanged — teardown keeps the database — but it is no longer a
|
|
199
|
+
// PER-SHAPE claim. This used to branch: every shared shape kept the database while a
|
|
200
|
+
// 'dedicated' machine took its database with it. ADR 0327 decision 4 removed that shape
|
|
201
|
+
// and task 1004182 folded its rows into 'cloud-host', so the branch could never be taken
|
|
202
|
+
// again and the page now states the surviving outcome flatly.
|
|
203
|
+
//
|
|
204
|
+
// The important part is the NEGATIVE: the copy must no longer offer the other outcome at
|
|
205
|
+
// all. A conditional that can't fire is not merely dead here — it is a promise to owners
|
|
206
|
+
// about a machine none of them have.
|
|
207
|
+
assert.match(body, /The database is kept/, 'the kept promise is present');
|
|
203
208
|
assert.match(body, /repository is untouched|repository stays yours|repository is still yours/i);
|
|
209
|
+
assert.doesNotMatch(body, /database goes with (it|the machine)/, 'the removed shape\'s copy is gone with it');
|
|
210
|
+
assert.doesNotMatch(body, /hosting_shape === 'dedicated'/, 'and so is the branch that selected it');
|
|
204
211
|
// the pre-walk over-claim can never come back
|
|
205
212
|
assert.doesNotMatch(body, /server and database go away/i);
|
|
206
213
|
assert.doesNotMatch(body, /database (is|will be) (deleted|dropped|destroyed|removed)/i);
|
package/tests/upgrade.mjs
CHANGED
|
@@ -1621,7 +1621,7 @@ t('pinnedDeployKey / remoteGithubRepo: read the ADR 0176 wiring off the checkout
|
|
|
1621
1621
|
});
|
|
1622
1622
|
|
|
1623
1623
|
t('resolvePinPushAuth: the owner token is spent only when one is stored AND the provisioning row names the remote repo', async () => {
|
|
1624
|
-
const inst = { slug: 'acme', owner_builder_id: 7, target_ref: 'acme/widgets', hosting_shape: '
|
|
1624
|
+
const inst = { slug: 'acme', owner_builder_id: 7, target_ref: 'acme/widgets', hosting_shape: 'cloud-host' };
|
|
1625
1625
|
const at = (url) => pinRun({ remote: { status: 0, stdout: `${url}\n` } });
|
|
1626
1626
|
const dir = { instanceDir: '/srv/bongos/acme' };
|
|
1627
1627
|
|
|
@@ -1683,7 +1683,7 @@ t('runUpgrade --commit-pin: on a wired private instance the pin lands via the ow
|
|
|
1683
1683
|
materialize: () => ({ skills: 35, settings: true, hooks: 3 }),
|
|
1684
1684
|
regenerateApiArtifacts: () => ({ ok: true, ran: ['gen-api-docs.js', 'gen-api-client.js'] }),
|
|
1685
1685
|
databaseUrl: '', pgDatabase: '',
|
|
1686
|
-
instance: { slug: 'acme', owner_builder_id: 7, target_ref: 'acme/widgets', hosting_shape: '
|
|
1686
|
+
instance: { slug: 'acme', owner_builder_id: 7, target_ref: 'acme/widgets', hosting_shape: 'cloud-host' },
|
|
1687
1687
|
getGithubToken: async () => ({ token, scope: 'read:user,repo' }),
|
|
1688
1688
|
},
|
|
1689
1689
|
);
|