@bongos/core 1.19.84 → 1.19.86
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 +11 -11
- package/modules/lifecycle/github-push.js +34 -15
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +5 -1
- package/tests/publish_branch_route.mjs +30 -8
- package/tests/publish_reconciler.mjs +14 -7
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.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.86",
|
|
6
|
+
"core_contract": "1.19.86",
|
|
7
|
+
"source_commit": "d492268af911f3775f9577d12281d2c38399727f",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-
|
|
9
|
+
"built_at": "2026-08-13T00:41:13.561Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 295,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 1550,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "6f0e9235712ce2021a2856ab9a969734a249d91bd64b367f1341555824ea03b8",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -3422,7 +3422,7 @@
|
|
|
3422
3422
|
{
|
|
3423
3423
|
"path": "modules/lifecycle/github-push.js",
|
|
3424
3424
|
"mode": "0000644",
|
|
3425
|
-
"sha256": "
|
|
3425
|
+
"sha256": "aef4e5dca5cbea3c7c2041b877a98204f2cb21c3a9ce017cad57955d2212cf96"
|
|
3426
3426
|
},
|
|
3427
3427
|
{
|
|
3428
3428
|
"path": "modules/lifecycle/goal-advisory.js",
|
|
@@ -4082,12 +4082,12 @@
|
|
|
4082
4082
|
{
|
|
4083
4083
|
"path": "package-lock.json",
|
|
4084
4084
|
"mode": "0000644",
|
|
4085
|
-
"sha256": "
|
|
4085
|
+
"sha256": "2bd4658aa54dea1a2ee6fdd04989f69633896a45aa8461017ad6b48abeb95579"
|
|
4086
4086
|
},
|
|
4087
4087
|
{
|
|
4088
4088
|
"path": "package.json",
|
|
4089
4089
|
"mode": "0000644",
|
|
4090
|
-
"sha256": "
|
|
4090
|
+
"sha256": "719540bff50475655aee54cfa4ca4f261e04ec1039b6e4b7d0428b6843420fc5"
|
|
4091
4091
|
},
|
|
4092
4092
|
{
|
|
4093
4093
|
"path": "public-docs/index.html",
|
|
@@ -5722,7 +5722,7 @@
|
|
|
5722
5722
|
{
|
|
5723
5723
|
"path": "src/module-api.js",
|
|
5724
5724
|
"mode": "0000644",
|
|
5725
|
-
"sha256": "
|
|
5725
|
+
"sha256": "0a7e91c72a110593259da6dca1a15fbdc2b474a5016d6ddfbb129311909ac292"
|
|
5726
5726
|
},
|
|
5727
5727
|
{
|
|
5728
5728
|
"path": "src/module-loader/loader.js",
|
|
@@ -7267,7 +7267,7 @@
|
|
|
7267
7267
|
{
|
|
7268
7268
|
"path": "tests/publish_branch_route.mjs",
|
|
7269
7269
|
"mode": "0000644",
|
|
7270
|
-
"sha256": "
|
|
7270
|
+
"sha256": "db309db9b8fe350a64708d2a0c9dd1622ad0e5bfdef7c89775721ff3daa7042d"
|
|
7271
7271
|
},
|
|
7272
7272
|
{
|
|
7273
7273
|
"path": "tests/publish_manifest.mjs",
|
|
@@ -7277,7 +7277,7 @@
|
|
|
7277
7277
|
{
|
|
7278
7278
|
"path": "tests/publish_reconciler.mjs",
|
|
7279
7279
|
"mode": "0000644",
|
|
7280
|
-
"sha256": "
|
|
7280
|
+
"sha256": "fed6533bd829adc0fc2126c48b28248c90bd32660650f2af91fee112e3f7369a"
|
|
7281
7281
|
},
|
|
7282
7282
|
{
|
|
7283
7283
|
"path": "tests/rank_gate.mjs",
|
|
@@ -385,8 +385,11 @@ async function enableAutoMerge({ nodeId }, deps = {}) {
|
|
|
385
385
|
// { pr_state, merged, merge_sha, deploy_status, deploy_probe } where
|
|
386
386
|
// pr_state ∈ open|closed|none,
|
|
387
387
|
// deploy_status ∈ pending|success|none|unconfigured|unavailable
|
|
388
|
-
// ('unconfigured' = this repo has no deploy-prod.yml
|
|
389
|
-
//
|
|
388
|
+
// ('unconfigured' = this repo has no deploy-prod.yml (404), OR the token can never
|
|
389
|
+
// read the Actions API (403 — task 1002737: a permanent scope gap reads the same
|
|
390
|
+
// as a permanently-missing workflow, so both are "no readable deploy to wait for";
|
|
391
|
+
// task 1002472 for the 404 case);
|
|
392
|
+
// 'unavailable' = a transient read failure (5xx/429/other) — task 1002724).
|
|
390
393
|
// deploy_probe = { state, http, detail } — WHY the probe landed where it did,
|
|
391
394
|
// so a stuck disposition is diagnosable without a shell on the host.
|
|
392
395
|
// Mirrors ship.js pollCiDeploy's two-stage check (PR merged -> deploy run).
|
|
@@ -457,27 +460,43 @@ async function getPublishStatus({ branch }, deps = {}) {
|
|
|
457
460
|
detail: exact ? 'deploy run for the merge commit' : 'main deploy succeeded after the merge (ADR 0161 carrier)',
|
|
458
461
|
};
|
|
459
462
|
}
|
|
460
|
-
} else if (runsRes.status === 404) {
|
|
461
|
-
// task 1002472 — the workflow FILE does not exist in this repo, so there is
|
|
462
|
-
// deploy to wait for and 'pending' would never resolve. The 404 is unambiguous:
|
|
463
|
+
} else if (runsRes.status === 404 || runsRes.status === 403) {
|
|
464
|
+
// task 1002472 — the workflow FILE does not exist in this repo (404), so there is
|
|
465
|
+
// no deploy to wait for and 'pending' would never resolve. The 404 is unambiguous:
|
|
463
466
|
// the /pulls fetch above already threw STATUS_FETCH_FAILED if the repo were
|
|
464
467
|
// unreachable, so reaching here means the repo resolved and only the workflow is
|
|
465
468
|
// missing — a permanent condition. Instances that publish by pin-bump
|
|
466
469
|
// (package-core → bongos upgrade, ADR 0108) never have a deploy-prod.yml at all;
|
|
467
470
|
// reporting 'pending' pinned every merged PR at confirmed forever.
|
|
471
|
+
//
|
|
472
|
+
// task 1002737 — a 403 is the SAME permanent shape, just for a different reason:
|
|
473
|
+
// GITHUB_PUSH_TOKEN has Contents/Pull requests/Commit statuses but not
|
|
474
|
+
// `actions:read`, so the Actions API is categorically unreadable, not
|
|
475
|
+
// intermittently unreadable. That is not "waiting on a deploy" (transient,
|
|
476
|
+
// 5xx/429 territory) — it is "this instance can never see deploy runs", which is
|
|
477
|
+
// exactly what 'unconfigured' already means for the no-workflow-file case. Folding
|
|
478
|
+
// 403 into 'unconfigured' (rather than 'unavailable') lets landConfirmed() flip the
|
|
479
|
+
// moment the PR merges, instead of depending on the git-history bypass
|
|
480
|
+
// (deployProbeBlind) to route around a probe that will 403 on every sweep forever.
|
|
468
481
|
deployStatus = 'unconfigured';
|
|
469
|
-
deployProbe = {
|
|
482
|
+
deployProbe = {
|
|
483
|
+
state: 'unconfigured', http: runsRes.status,
|
|
484
|
+
detail: runsRes.status === 404
|
|
485
|
+
? 'no deploy-prod.yml in this repo'
|
|
486
|
+
: 'workflow-runs read forbidden (token lacks Actions:read) — treated as no readable deploy to wait for',
|
|
487
|
+
};
|
|
470
488
|
} else {
|
|
471
489
|
// task 1002724 — this branch used to fall through as 'pending' on the comment
|
|
472
|
-
// "5xx/403/rate-limit is transient".
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
// 'pending' for 18 min across 4 sweeps,
|
|
479
|
-
// Call it what it is — we could not READ the
|
|
480
|
-
// reconciler decide whether git history already
|
|
490
|
+
// "5xx/403/rate-limit is transient". 403 is handled above (task 1002737: it is
|
|
491
|
+
// forever, not transient); what's left here (5xx, 429, other 4xx) IS genuinely
|
|
492
|
+
// transient or unexpected, so 'pending' on a MERGED PR making the reconciler
|
|
493
|
+
// suppress the land proof on every sweep (prMidDeploy) used to strand the task at
|
|
494
|
+
// 'confirmed' permanently with a bare 'pr_not_landed' and no clue why. That was
|
|
495
|
+
// the live 2026-08-12 strand (task 1002695 / PR 91: GitHub reported MERGED with
|
|
496
|
+
// green checks, the server reported deploy 'pending' for 18 min across 4 sweeps,
|
|
497
|
+
// and only a manual ship cleared it). Call it what it is — we could not READ the
|
|
498
|
+
// deploy state — and let the reconciler decide whether git history already
|
|
499
|
+
// proves the land.
|
|
481
500
|
deployStatus = 'unavailable';
|
|
482
501
|
deployProbe = { state: 'unavailable', http: runsRes.status, detail: `workflow-runs read failed (HTTP ${runsRes.status})` };
|
|
483
502
|
}
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.86",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.86",
|
|
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.19.
|
|
3
|
+
"version": "1.19.86",
|
|
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/src/module-api.js
CHANGED
|
@@ -595,8 +595,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
595
595
|
// landed since 1.19.82 with no explicit bump. run 31651046759. (task 1002620)
|
|
596
596
|
// 1.19.84 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
597
597
|
// landed since 1.19.83 with no explicit bump. run 31651217970. (task 1002620)
|
|
598
|
+
// 1.19.85 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
599
|
+
// landed since 1.19.84 with no explicit bump. run 31651469729. (task 1002620)
|
|
600
|
+
// 1.19.86 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
601
|
+
// landed since 1.19.85 with no explicit bump. run 31655210981. (task 1002620)
|
|
598
602
|
// ---------------------------------------------------------------------------
|
|
599
|
-
const CORE_VERSION = '1.19.
|
|
603
|
+
const CORE_VERSION = '1.19.86'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
600
604
|
|
|
601
605
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
602
606
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -228,8 +228,9 @@ await t('getPublishStatus: no PR for the head → none', async () => {
|
|
|
228
228
|
// task 1002472 — an instance with NO deploy-prod.yml must not wait on it forever.
|
|
229
229
|
// The 404 is unambiguous: the /pulls fetch above already threw if the repo were
|
|
230
230
|
// unreachable, so a 404 HERE means the workflow file is absent — permanent, not
|
|
231
|
-
// transient.
|
|
232
|
-
// still
|
|
231
|
+
// transient. task 1002737 folds 403 into this same bucket (see the test below);
|
|
232
|
+
// anything else (5xx/429/network) still stays 'pending'/'unavailable' so a real
|
|
233
|
+
// deploy is still waited on.
|
|
233
234
|
await t('getPublishStatus: merged + workflow-runs 404 → unconfigured (no deploy-prod.yml)', async () => {
|
|
234
235
|
const mergeSha = 'd'.repeat(40);
|
|
235
236
|
const fetchImpl = async (url) => {
|
|
@@ -242,18 +243,39 @@ await t('getPublishStatus: merged + workflow-runs 404 → unconfigured (no deplo
|
|
|
242
243
|
assert.equal(s.deploy_status, 'unconfigured', 'an absent workflow is permanent, not pending');
|
|
243
244
|
});
|
|
244
245
|
|
|
245
|
-
// task
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
|
|
246
|
+
// task 1002737 — a 403 (token lacks `actions:read`) is the SAME permanent shape as a
|
|
247
|
+
// missing workflow file: the Actions API can never be read on this token, not just
|
|
248
|
+
// right now. Folding it into 'unconfigured' lets landConfirmed() flip the instant the
|
|
249
|
+
// PR merges, instead of leaning on the git-history bypass (deployProbeBlind) to route
|
|
250
|
+
// around a probe that would 403 on every single sweep forever. This is the live
|
|
251
|
+
// cloudbongos.com shape (GITHUB_PUSH_TOKEN has Contents/Pull requests/Commit statuses
|
|
252
|
+
// but not Actions) that stranded 1001772/1002725/1002726/1002729 at confirmed.
|
|
253
|
+
await t('getPublishStatus: merged + workflow-runs 403 → unconfigured (permanent scope gap, not unavailable)', async () => {
|
|
254
|
+
const mergeSha = 'f'.repeat(40);
|
|
255
|
+
const fetchImpl = async (url) => {
|
|
256
|
+
if (url.includes('/pulls?head=')) return jsonRes(200, [{ state: 'closed', merged_at: '2026-07-26T00:00:00Z', merge_commit_sha: mergeSha, html_url: 'http://pr/1' }]);
|
|
257
|
+
if (url.includes('deploy-prod.yml/runs')) return jsonRes(403, { message: 'Resource not accessible by personal access token' });
|
|
258
|
+
throw new Error('unexpected ' + url);
|
|
259
|
+
};
|
|
260
|
+
const s = await gp.getPublishStatus({ branch: 'claude/x' }, { token: 'TT', repoInfo: REPO, fetchImpl });
|
|
261
|
+
assert.equal(s.deploy_status, 'unconfigured', 'a permanent scope gap must resolve like a missing workflow, not stay unavailable');
|
|
262
|
+
assert.equal(s.deploy_probe.http, 403, 'the probe must carry the HTTP status for diagnosis');
|
|
263
|
+
assert.equal(gp.landConfirmed(s), true, 'a merged PR with an unreadable-forever deploy probe must land on its own');
|
|
264
|
+
assert.equal(gp.deployProbeBlind(s), false, 'this is not blindness — it is a known-permanent non-deploying config');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// task 1002724 — a genuinely TRANSIENT read failure (5xx/429/other) is its OWN thing:
|
|
268
|
+
// not "no workflow" (which would land it) and not "a deploy is running" (which vetoes
|
|
269
|
+
// the land proof forever). Calling it 'pending' stranded task 1002695 at confirmed
|
|
270
|
+
// across 4 sweeps on 2026-08-12.
|
|
271
|
+
await t('getPublishStatus: merged + workflow-runs 5xx → unavailable, never landConfirmed', async () => {
|
|
250
272
|
const mergeSha = 'e'.repeat(40);
|
|
251
273
|
const mk = (code) => async (url) => {
|
|
252
274
|
if (url.includes('/pulls?head=')) return jsonRes(200, [{ state: 'closed', merged_at: '2026-07-26T00:00:00Z', merge_commit_sha: mergeSha, html_url: 'http://pr/1' }]);
|
|
253
275
|
if (url.includes('deploy-prod.yml/runs')) return jsonRes(code, { message: 'nope' });
|
|
254
276
|
throw new Error('unexpected ' + url);
|
|
255
277
|
};
|
|
256
|
-
for (const code of [500, 502,
|
|
278
|
+
for (const code of [500, 502, 429]) {
|
|
257
279
|
const s = await gp.getPublishStatus({ branch: 'claude/x' }, { token: 'TT', repoInfo: REPO, fetchImpl: mk(code) });
|
|
258
280
|
assert.equal(s.deploy_status, 'unavailable', `${code} must NOT be read as "no workflow" nor as a running deploy`);
|
|
259
281
|
assert.equal(s.deploy_probe.http, code, 'the probe must carry the HTTP status for diagnosis');
|
|
@@ -340,17 +340,24 @@ test('1002472 merged + deploy unconfigured + published tip in main → ships', a
|
|
|
340
340
|
// task 1002724 — the deploy probe went BLIND and vetoed a provable land forever.
|
|
341
341
|
//
|
|
342
342
|
// Live on 2026-08-12: task 1002695 / PR 91 was MERGED with green checks at
|
|
343
|
-
// 20:25:23Z, but the server's deploy probe answered 'pending' (
|
|
344
|
-
// without `actions:read` — permanent, not
|
|
345
|
-
//
|
|
346
|
-
// sweeps across 18 minutes all logged a
|
|
347
|
-
// manual POST /ship cleared it. A probe
|
|
348
|
-
// main's git history, and the
|
|
343
|
+
// 20:25:23Z, but the server's deploy probe answered 'pending' (at the time, a 403
|
|
344
|
+
// from a token without `actions:read` fell through to 'pending' — permanent, not
|
|
345
|
+
// transient). merged + !landConfirmed made prMidDeploy true, which SUPPRESSED
|
|
346
|
+
// landProvenForTask on every sweep, so four sweeps across 18 minutes all logged a
|
|
347
|
+
// bare '1002695:pr_not_landed' and only a manual POST /ship cleared it. A probe
|
|
348
|
+
// that cannot see anything must not outrank main's git history, and the
|
|
349
|
+
// disposition must say which probe refused.
|
|
350
|
+
//
|
|
351
|
+
// task 1002737 later gave THAT specific 403 shape its own direct fix (folded into
|
|
352
|
+
// 'unconfigured', landConfirmed() true immediately — see publish_branch_route.mjs).
|
|
353
|
+
// This test's fixture uses a 500 instead, so it still exercises the general "blind
|
|
354
|
+
// probe, git history wins" safety net this task built — the one still needed for a
|
|
355
|
+
// genuinely transient 5xx/429, where 'unconfigured' would be the wrong answer.
|
|
349
356
|
// ---------------------------------------------------------------------------
|
|
350
357
|
const BLIND = (id) => ({
|
|
351
358
|
...landedPrFor(id),
|
|
352
359
|
deploy_status: 'unavailable',
|
|
353
|
-
deploy_probe: { state: 'unavailable', http:
|
|
360
|
+
deploy_probe: { state: 'unavailable', http: 500, detail: 'workflow-runs read failed (HTTP 500)' },
|
|
354
361
|
merge_sha: MERGE_SHA,
|
|
355
362
|
});
|
|
356
363
|
|