@bongos/core 1.19.83 → 1.19.85

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/src/module-api.js CHANGED
@@ -593,8 +593,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
593
593
  // landed since 1.19.81 with no explicit bump. run 31650108283. (task 1002620)
594
594
  // 1.19.83 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
595
595
  // landed since 1.19.82 with no explicit bump. run 31651046759. (task 1002620)
596
+ // 1.19.84 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
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)
596
600
  // ---------------------------------------------------------------------------
597
- const CORE_VERSION = '1.19.83'; // CI auto-patch carrier (ADR 0161); see changelog above
601
+ const CORE_VERSION = '1.19.85'; // CI auto-patch carrier (ADR 0161); see changelog above
598
602
 
599
603
  // A namespaced logger so a module's log lines are attributable + consistent.
600
604
  // 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