@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 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.84",
6
- "core_contract": "1.19.84",
7
- "source_commit": "39493e5006e0adc42f628e8fcc7a96ff0a10a024",
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-12T23:32:24.005Z",
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": "5b888a43d2616ba06ba176e5611635624d1e8f8d6bf60225393639f236b5fba9",
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": "0534cf4522f5a644301946abf6a703852375fe269401902dfae10d7b9192c3d5"
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": "af4e543cec8805d479252218ee7fb173f5daa82683719b544a5809c265426638"
4085
+ "sha256": "2bd4658aa54dea1a2ee6fdd04989f69633896a45aa8461017ad6b48abeb95579"
4086
4086
  },
4087
4087
  {
4088
4088
  "path": "package.json",
4089
4089
  "mode": "0000644",
4090
- "sha256": "34009dd8769a83f36d6825e22ac90bae4370416e06e280746c78ba44afcd9624"
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": "c6df7e76c73ea039b407602d8e05f082f1ecb7447fa629fd6a9d8fdaa38f3f51"
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": "ea46629917c3236b9dfb7133af643a9fecc9a205323c345e16671b5093606fd6"
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": "ff2428b394a50abf4a593fcbfdb98afe4b56ffdbbffdc81198a102e72b480b9f"
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 — task 1002472;
389
- // 'unavailable' = we could not READ the deploy state at all — task 1002724).
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 no
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 = { state: 'unconfigured', http: 404, detail: 'no deploy-prod.yml in this repo' };
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". A 403 from a token without `actions:read`
473
- // is NOT transient, it is forever — and 'pending' on a MERGED PR makes the
474
- // reconciler suppress the land proof on every sweep (prMidDeploy), so the task
475
- // strands at 'confirmed' permanently with a bare 'pr_not_landed' and no clue
476
- // why. That is exactly the live 2026-08-12 strand (task 1002695 / PR 91:
477
- // GitHub reported MERGED with green checks, the server reported deploy
478
- // 'pending' for 18 min across 4 sweeps, and only a manual ship cleared it).
479
- // Call it what it is — we could not READ the deploy state — and let the
480
- // reconciler decide whether git history already proves the land.
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.84",
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.84",
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.84",
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.84'; // CI auto-patch carrier (ADR 0161); see changelog above
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. Anything else (5xx/403/network) stays 'pending' so a real deploy is
232
- // still waited on.
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 1002724 — an unreadable deploy state is its OWN thing: not "no workflow"
246
- // (which would land it) and not "a deploy is running" (which vetoes the land proof
247
- // forever). A 403 from a token without `actions:read` never resolves, and calling
248
- // it 'pending' stranded task 1002695 at confirmed across 4 sweeps on 2026-08-12.
249
- await t('getPublishStatus: merged + workflow-runs 5xx/403 → unavailable, never landConfirmed', async () => {
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, 403]) {
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' (a 403 from a token
344
- // without `actions:read` — permanent, not transient). merged + !landConfirmed made
345
- // prMidDeploy true, which SUPPRESSED landProvenForTask on every sweep, so four
346
- // sweeps across 18 minutes all logged a bare '1002695:pr_not_landed' and only a
347
- // manual POST /ship cleared it. A probe that cannot see anything must not outrank
348
- // main's git history, and the disposition must say which probe refused.
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: 403, detail: 'workflow-runs read failed (HTTP 403)' },
360
+ deploy_probe: { state: 'unavailable', http: 500, detail: 'workflow-runs read failed (HTTP 500)' },
354
361
  merge_sha: MERGE_SHA,
355
362
  });
356
363