@bongos/core 1.21.149 → 1.21.150
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 +12 -12
- package/docs/module-api-changelog.md +2 -0
- package/modules/lifecycle/ship-assurance.js +61 -3
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
- package/tests/ship_assurance.mjs +64 -0
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.150",
|
|
6
|
+
"core_contract": "1.21.150",
|
|
7
|
+
"source_commit": "0c869afc46eb5a0841a9406c43fa41f8031c01ad",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-
|
|
9
|
+
"built_at": "2026-10-19T22:01:23.493Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 608,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 3805,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "e7a8a5514591ba5b313a0cc54239367bcfb3f92d0dce344582653a125ecb5d21",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2927,7 +2927,7 @@
|
|
|
2927
2927
|
{
|
|
2928
2928
|
"path": "docs/module-api-changelog.md",
|
|
2929
2929
|
"mode": "0000644",
|
|
2930
|
-
"sha256": "
|
|
2930
|
+
"sha256": "42b00f22670c0c8aa194a2a7c3f22f3bcc9e4a444ff54c398c1254c06e6d5718"
|
|
2931
2931
|
},
|
|
2932
2932
|
{
|
|
2933
2933
|
"path": "docs/modules-contract.md",
|
|
@@ -7737,7 +7737,7 @@
|
|
|
7737
7737
|
{
|
|
7738
7738
|
"path": "modules/lifecycle/ship-assurance.js",
|
|
7739
7739
|
"mode": "0000644",
|
|
7740
|
-
"sha256": "
|
|
7740
|
+
"sha256": "c4dd044485e664528c01caea0500dd6fe9d8ee9d2319385000d1cee26e32102e"
|
|
7741
7741
|
},
|
|
7742
7742
|
{
|
|
7743
7743
|
"path": "modules/lifecycle/ship-card.js",
|
|
@@ -10552,12 +10552,12 @@
|
|
|
10552
10552
|
{
|
|
10553
10553
|
"path": "package-lock.json",
|
|
10554
10554
|
"mode": "0000644",
|
|
10555
|
-
"sha256": "
|
|
10555
|
+
"sha256": "d0e2fcb3573e72ebc1d3ba608eeac029763e7fb2f8fad0f459cca42759bb6c5b"
|
|
10556
10556
|
},
|
|
10557
10557
|
{
|
|
10558
10558
|
"path": "package.json",
|
|
10559
10559
|
"mode": "0000644",
|
|
10560
|
-
"sha256": "
|
|
10560
|
+
"sha256": "5348abba02d8b98849a7a6cf83a44f7d3138f00a806edb89ae2616618b15a559"
|
|
10561
10561
|
},
|
|
10562
10562
|
{
|
|
10563
10563
|
"path": "public-docs/index.html",
|
|
@@ -10577,7 +10577,7 @@
|
|
|
10577
10577
|
{
|
|
10578
10578
|
"path": "release-notes.json",
|
|
10579
10579
|
"mode": "0000644",
|
|
10580
|
-
"sha256": "
|
|
10580
|
+
"sha256": "eaf55ec677f00c2212a9fb00f680130dc60974d8e2a199a97337048c567d2706"
|
|
10581
10581
|
},
|
|
10582
10582
|
{
|
|
10583
10583
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -12957,7 +12957,7 @@
|
|
|
12957
12957
|
{
|
|
12958
12958
|
"path": "src/module-api.js",
|
|
12959
12959
|
"mode": "0000644",
|
|
12960
|
-
"sha256": "
|
|
12960
|
+
"sha256": "6ff0eaf959df5d39f838ae2b19a960f7f37552e9eedcb91b183b09b38ab6c840"
|
|
12961
12961
|
},
|
|
12962
12962
|
{
|
|
12963
12963
|
"path": "src/module-loader/catalog.js",
|
|
@@ -18102,7 +18102,7 @@
|
|
|
18102
18102
|
{
|
|
18103
18103
|
"path": "tests/ship_assurance.mjs",
|
|
18104
18104
|
"mode": "0000644",
|
|
18105
|
-
"sha256": "
|
|
18105
|
+
"sha256": "be68b8dbf514937f581dfa3a892958fd4f16e20dd319d0ed70bf08811a3451e3"
|
|
18106
18106
|
},
|
|
18107
18107
|
{
|
|
18108
18108
|
"path": "tests/ship_autolinkify.mjs",
|
|
@@ -3135,5 +3135,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3135
3135
|
landed since 1.21.147 with no explicit bump. run 39286989989. (task 1002620)
|
|
3136
3136
|
1.21.149 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3137
3137
|
landed since 1.21.148 with no explicit bump. run 39329687123. (task 1002620)
|
|
3138
|
+
1.21.150 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3139
|
+
landed since 1.21.149 with no explicit bump. run 39331860470. (task 1002620)
|
|
3138
3140
|
---------------------------------------------------------------------------
|
|
3139
3141
|
```
|
|
@@ -67,7 +67,13 @@ function normalizeShipAssurance(a) {
|
|
|
67
67
|
//
|
|
68
68
|
// `deps` is injected so this is unit-testable with no network and no DB.
|
|
69
69
|
async function deriveShipAssurance({ taskId, body = {}, db, githubPush, onProbeError }) {
|
|
70
|
-
|
|
70
|
+
// Every probe miss is kept, not just logged: when the ONLY reason there is no
|
|
71
|
+
// proof is that this server cannot read GitHub at all, the refusal must say so
|
|
72
|
+
// (task 1004760). The toast instance merged real work and was told only that the
|
|
73
|
+
// land was "not proven" — its GitHub App existed but was never installed on the
|
|
74
|
+
// repo, which is a one-click owner fix nobody was told about.
|
|
75
|
+
const probeErrors = [];
|
|
76
|
+
const report = (e) => { probeErrors.push(e); if (onProbeError) onProbeError(e); };
|
|
71
77
|
// The tip is the durable proof (task 1547); the branch/PR status is only a
|
|
72
78
|
// WITNESS for the out-of-band fallback (task 1002615). A claim with no branch
|
|
73
79
|
// makes getPublishStatus throw BAD_BRANCH — that used to abort the whole probe
|
|
@@ -117,6 +123,26 @@ async function deriveShipAssurance({ taskId, body = {}, db, githubPush, onProbeE
|
|
|
117
123
|
return { assurance: { kind: 'no_artifact', reason } };
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
const noAccess = githubAccessCause(probeErrors);
|
|
127
|
+
if (noAccess) {
|
|
128
|
+
return {
|
|
129
|
+
refusal: {
|
|
130
|
+
code: 'ship_land_not_proven',
|
|
131
|
+
status: 409,
|
|
132
|
+
message: noAccess.message,
|
|
133
|
+
details: {
|
|
134
|
+
cause: noAccess.cause,
|
|
135
|
+
install_url: noAccess.install_url || null,
|
|
136
|
+
what_this_means: noAccess.message,
|
|
137
|
+
your_options: [
|
|
138
|
+
noAccess.fix,
|
|
139
|
+
'Nothing is lost: once the server can read GitHub, the reconciler proves the merge and ships the task on its own, with its credits.',
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
120
146
|
return {
|
|
121
147
|
refusal: {
|
|
122
148
|
code: 'ship_land_not_proven',
|
|
@@ -133,6 +159,37 @@ async function deriveShipAssurance({ taskId, body = {}, db, githubPush, onProbeE
|
|
|
133
159
|
}
|
|
134
160
|
|
|
135
161
|
|
|
162
|
+
// Was the land unprovable only because this server has no way to read GitHub?
|
|
163
|
+
// PURE over the probe errors deriveShipAssurance collected. Returns null when the
|
|
164
|
+
// probes could see GitHub (a genuine "not on main" answer stays the generic
|
|
165
|
+
// refusal), else { cause, message, fix, install_url? } (task 1004760).
|
|
166
|
+
// APP_NOT_INSTALLED the App exists but nobody installed it on the repo —
|
|
167
|
+
// the owner's one remaining setup step.
|
|
168
|
+
// PUSH_UNCONFIGURED / no GitHub credential at all, and no git checkout of
|
|
169
|
+
// LOCAL_PROOF_UNAVAILABLE the project to ask instead (a hosted instance).
|
|
170
|
+
function githubAccessCause(errors) {
|
|
171
|
+
const codes = (errors || []).map((e) => e && e.code);
|
|
172
|
+
const notInstalled = (errors || []).find((e) => e && e.code === 'APP_NOT_INSTALLED');
|
|
173
|
+
if (notInstalled) {
|
|
174
|
+
const repo = notInstalled.owner && notInstalled.repo ? `${notInstalled.owner}/${notInstalled.repo}` : 'the project repository';
|
|
175
|
+
const where = notInstalled.install_url ? `at ${notInstalled.install_url}` : "from the App's page in GitHub settings (Install App)";
|
|
176
|
+
return {
|
|
177
|
+
cause: 'github_app_not_installed',
|
|
178
|
+
install_url: notInstalled.install_url || null,
|
|
179
|
+
message: `The merge may well be on main, but this server cannot check: the project's GitHub App is not installed on ${repo}, so the server cannot read the repository.`,
|
|
180
|
+
fix: `The project owner installs the GitHub App on ${repo} ${where} — one click, no other setup.`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (codes.includes('PUSH_UNCONFIGURED') || codes.includes('LOCAL_PROOF_UNAVAILABLE')) {
|
|
184
|
+
return {
|
|
185
|
+
cause: 'no_github_access',
|
|
186
|
+
message: 'The merge may well be on main, but this server cannot check: it has no GitHub access (no GitHub App or token) and no copy of the repository to look in.',
|
|
187
|
+
fix: "The project owner finishes the project's GitHub setup (Settings → GitHub) so the server can read the repository.",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
136
193
|
// Record WHY a ship was allowed, not merely that it happened. POST-COMMIT and
|
|
137
194
|
// best-effort by design: insertAuditLog uses the module pool rather than the
|
|
138
195
|
// ship's transaction client, and an audit miss must never roll back a landed ship
|
|
@@ -172,13 +229,14 @@ async function gateManualShip(res, opts) {
|
|
|
172
229
|
onProbeError: (e) => log && log.warn(`POST /tasks/${taskId}/ship: land probe failed (${e && e.message}) — no proof`),
|
|
173
230
|
});
|
|
174
231
|
if (derived.refusal) {
|
|
175
|
-
|
|
232
|
+
const { code, status, message, details } = derived.refusal;
|
|
233
|
+
res.fail(code, { status, message, details });
|
|
176
234
|
return null;
|
|
177
235
|
}
|
|
178
236
|
return derived.assurance;
|
|
179
237
|
}
|
|
180
238
|
|
|
181
239
|
module.exports = {
|
|
182
|
-
normalizeShipAssurance, deriveShipAssurance, recordShipAssurance,
|
|
240
|
+
normalizeShipAssurance, deriveShipAssurance, recordShipAssurance, githubAccessCause,
|
|
183
241
|
assertShipAssurance, gateManualShip, SHIP_BODY_SCHEMA, SHIP_ASSURANCE_KINDS: KINDS, MAX_REASON_CHARS,
|
|
184
242
|
};
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.150",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.150",
|
|
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.21.
|
|
3
|
+
"version": "1.21.150",
|
|
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",
|
package/release-notes.json
CHANGED
|
@@ -9908,5 +9908,11 @@
|
|
|
9908
9908
|
"id": "1004759",
|
|
9909
9909
|
"text": "When a project's site is too old to send Bongos feedback, the command now says 'Not sent' and keeps the note, instead of claiming it was sent."
|
|
9910
9910
|
}
|
|
9911
|
+
],
|
|
9912
|
+
"1.21.150": [
|
|
9913
|
+
{
|
|
9914
|
+
"id": "1004760",
|
|
9915
|
+
"text": "When a project's GitHub App isn't installed, a refused ship now says so and links the one-click install, instead of a bare 'not proven'."
|
|
9916
|
+
}
|
|
9911
9917
|
]
|
|
9912
9918
|
}
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.150'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
package/tests/ship_assurance.mjs
CHANGED
|
@@ -321,5 +321,69 @@ await t('no tip + no PR for the task → the 409 refusal, and getPublishStatus i
|
|
|
321
321
|
assert.equal(statusCalls, 0);
|
|
322
322
|
});
|
|
323
323
|
|
|
324
|
+
// task 1004760 — the toast instance merged real work and was refused with a bare
|
|
325
|
+
// "not proven": its GitHub App was never installed on the repo, so every probe threw
|
|
326
|
+
// APP_NOT_INSTALLED and the server could not look. The refusal must NAME that cause
|
|
327
|
+
// and the one-click fix, not leave the builder guessing.
|
|
328
|
+
console.log('\nthe refusal names a server that cannot read GitHub (task 1004760)');
|
|
329
|
+
const notInstalled = () => {
|
|
330
|
+
const e = new Error('APP_NOT_INSTALLED');
|
|
331
|
+
e.code = 'APP_NOT_INSTALLED';
|
|
332
|
+
e.owner = 'acme';
|
|
333
|
+
e.repo = 'widget';
|
|
334
|
+
e.install_url = 'https://github.com/apps/widget-app/installations/new';
|
|
335
|
+
return e;
|
|
336
|
+
};
|
|
337
|
+
await t('App not installed → the refusal says so, with the install link', async () => {
|
|
338
|
+
const r = await deriveShipAssurance({
|
|
339
|
+
taskId: 1,
|
|
340
|
+
body: {},
|
|
341
|
+
db: { getClaimPublishedSha: async () => TIP },
|
|
342
|
+
githubPush: {
|
|
343
|
+
findTaskPullRequest: async () => { throw notInstalled(); },
|
|
344
|
+
getPublishStatus: async () => null,
|
|
345
|
+
landProvenForTask: async () => { throw notInstalled(); },
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
assert.equal(r.refusal.code, 'ship_land_not_proven');
|
|
349
|
+
assert.equal(r.refusal.details.cause, 'github_app_not_installed');
|
|
350
|
+
assert.match(r.refusal.message, /not installed on acme\/widget/);
|
|
351
|
+
assert.equal(r.refusal.details.install_url, 'https://github.com/apps/widget-app/installations/new');
|
|
352
|
+
assert.ok(r.refusal.details.your_options.some((o) => o.includes('installations/new')));
|
|
353
|
+
});
|
|
354
|
+
await t('no credential and no checkout → the refusal says the server has no GitHub access', async () => {
|
|
355
|
+
for (const code of ['PUSH_UNCONFIGURED', 'LOCAL_PROOF_UNAVAILABLE']) {
|
|
356
|
+
const r = await deriveShipAssurance({
|
|
357
|
+
taskId: 1,
|
|
358
|
+
body: {},
|
|
359
|
+
db: { getClaimPublishedSha: async () => TIP },
|
|
360
|
+
githubPush: {
|
|
361
|
+
findTaskPullRequest: async () => null,
|
|
362
|
+
landProvenForTask: async () => { const e = new Error(code); e.code = code; throw e; },
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
assert.equal(r.refusal.details.cause, 'no_github_access', code);
|
|
366
|
+
assert.match(r.refusal.message, /no GitHub access/);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
await t('a server that CAN read GitHub keeps the plain refusal (the tip really is not on main)', async () => {
|
|
370
|
+
const r = await deriveShipAssurance({
|
|
371
|
+
taskId: 1,
|
|
372
|
+
body: {},
|
|
373
|
+
db: { getClaimPublishedSha: async () => TIP },
|
|
374
|
+
githubPush: {
|
|
375
|
+
findTaskPullRequest: async () => null,
|
|
376
|
+
landProvenForTask: async () => ({ proven: false, reason: 'not in main' }),
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
assert.equal(r.refusal.code, 'ship_land_not_proven');
|
|
380
|
+
assert.equal(r.refusal.details.cause, undefined);
|
|
381
|
+
assert.equal(r.refusal.message, undefined);
|
|
382
|
+
});
|
|
383
|
+
await t('the gate sends the cause as the error MESSAGE, which is what the CLI prints', () => {
|
|
384
|
+
const src = fs.readFileSync(path.join(ROOT, 'modules/lifecycle/ship-assurance.js'), 'utf8');
|
|
385
|
+
assert.match(src, /res\.fail\(code, \{ status, message, details \}\)/);
|
|
386
|
+
});
|
|
387
|
+
|
|
324
388
|
console.log(`\n${passed} passed, ${failed} failed`);
|
|
325
389
|
process.exit(failed ? 1 : 0);
|