@bongos/core 1.21.104 → 1.21.105

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.21.104",
6
- "core_contract": "1.21.104",
7
- "source_commit": "cfc8c3651ea3f442385e6d0ba405c6a578b4d83a",
5
+ "core_version": "1.21.105",
6
+ "core_contract": "1.21.105",
7
+ "source_commit": "12969410e588a86536f6426de5dcd95390b3be6b",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-10-09T13:49:17.283Z",
9
+ "built_at": "2026-10-09T14:04:06.264Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 592,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 3642,
20
- "tree_sha256": "b23d12f504071337f2882e99100b69132448daa2e65560c851411b6a667c5f67",
20
+ "tree_sha256": "10dd1c422b8f42c90e7521862acb305b14d72053ddbe6c75511033ab149b694b",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2872,7 +2872,7 @@
2872
2872
  {
2873
2873
  "path": "docs/module-api-changelog.md",
2874
2874
  "mode": "0000644",
2875
- "sha256": "6e7ad3fddfaca1cfb1dfe79cbdb4a5588007b30eedd458df7212a971da96e324"
2875
+ "sha256": "9e09c3f6c9d01b93d3a0857d2115578a4e2268491278f11656efcf499c20b766"
2876
2876
  },
2877
2877
  {
2878
2878
  "path": "docs/modules-contract.md",
@@ -6832,7 +6832,7 @@
6832
6832
  {
6833
6833
  "path": "modules/lifecycle/check-rollup.js",
6834
6834
  "mode": "0000644",
6835
- "sha256": "3a69defc4e2157c104c9a95d2b67f4b68053c0a0e049683ddd9eb970d3f69871"
6835
+ "sha256": "9109d29e5fc6739419c6584d4be2990516c2736c398ed8e3b916bb8b4627e0bb"
6836
6836
  },
6837
6837
  {
6838
6838
  "path": "modules/lifecycle/claim-eligibility.js",
@@ -6992,7 +6992,7 @@
6992
6992
  {
6993
6993
  "path": "modules/lifecycle/github-push.js",
6994
6994
  "mode": "0000644",
6995
- "sha256": "e52b3d90814272cacce172bccd24f437bac666dcb593b4604d435a5504c18ba7"
6995
+ "sha256": "65386fff8736c1b55c1f105c595758161e718352077603d1ba9aa7dd05d6d1d6"
6996
6996
  },
6997
6997
  {
6998
6998
  "path": "modules/lifecycle/goal-advisory.js",
@@ -10137,12 +10137,12 @@
10137
10137
  {
10138
10138
  "path": "package-lock.json",
10139
10139
  "mode": "0000644",
10140
- "sha256": "7f4d097ca5d827a3fad698b3e3fbbedc38f7469e29b565cfed34489f4eaab008"
10140
+ "sha256": "967618b349cf51650bbb10bb29c9304205ce8b84b0a61b0f26a84ff6b65751e8"
10141
10141
  },
10142
10142
  {
10143
10143
  "path": "package.json",
10144
10144
  "mode": "0000644",
10145
- "sha256": "a0fb2f62847ad58e78ec122f44bb7e97281f124bdfd0324c767a50bfe26b5a9e"
10145
+ "sha256": "6312c6a7441ab5c39cb567c6272d4a753edf0e2896a53b6cbb83e8c0713866d9"
10146
10146
  },
10147
10147
  {
10148
10148
  "path": "public-docs/index.html",
@@ -10162,7 +10162,7 @@
10162
10162
  {
10163
10163
  "path": "release-notes.json",
10164
10164
  "mode": "0000644",
10165
- "sha256": "f65d5a86053502037145a87a3e996ce4ff32c84651d4a8f8afe62c486172ef2d"
10165
+ "sha256": "7a99e0dae60fe796637cc0190ef51e49b0ff707b1a193bdab8b7f9444b953134"
10166
10166
  },
10167
10167
  {
10168
10168
  "path": "scripts/cron/style_guide_audit.sh",
@@ -11672,7 +11672,7 @@
11672
11672
  {
11673
11673
  "path": "scripts/gds/ship-land.js",
11674
11674
  "mode": "0000644",
11675
- "sha256": "e518039d82343b5ff3bb21e1a6e0a5a8e0b9368d05b791be4910d76ea075378e"
11675
+ "sha256": "0b60f062ccf052a0eecb71fff3e0abf3c75cb2399786a286ab54df4091fff787"
11676
11676
  },
11677
11677
  {
11678
11678
  "path": "scripts/gds/ship-memory-sync.js",
@@ -12437,7 +12437,7 @@
12437
12437
  {
12438
12438
  "path": "src/module-api.js",
12439
12439
  "mode": "0000644",
12440
- "sha256": "a00e2f13c266f639d3052f84844f2f38634759e57cfd81830667a9bf1e3e7be7"
12440
+ "sha256": "9f15b0adaa8dea1358b3c6d154c7ff2b378ce4bb7c8ba23c21f611861836aa11"
12441
12441
  },
12442
12442
  {
12443
12443
  "path": "src/module-loader/catalog.js",
@@ -14092,7 +14092,7 @@
14092
14092
  {
14093
14093
  "path": "tests/github_push_merge_driver.mjs",
14094
14094
  "mode": "0000644",
14095
- "sha256": "c4783b6e8171f96f4a83520cfc4335a313dbfce4702cccea6972e520d6f08deb"
14095
+ "sha256": "5bf4d9ab391f0e457ec11b47197dd812487098d120f8fbba87dbddf7e23e92b9"
14096
14096
  },
14097
14097
  {
14098
14098
  "path": "tests/github_push_stderr_truncation.mjs",
@@ -3045,5 +3045,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
3045
3045
  landed since 1.21.102 with no explicit bump. run 37936846665. (task 1002620)
3046
3046
  1.21.104 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3047
3047
  landed since 1.21.103 with no explicit bump. run 37939497390. (task 1002620)
3048
+ 1.21.105 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
3049
+ landed since 1.21.104 with no explicit bump. run 37941330141. (task 1002620)
3048
3050
  ---------------------------------------------------------------------------
3049
3051
  ```
@@ -39,4 +39,14 @@ function readCheckRollup(rollup) {
39
39
  };
40
40
  }
41
41
 
42
- module.exports = { readCheckRollup, UNIT_REPORT_CONTEXT };
42
+ // The conclusions that mean a check really FAILED (task 1004666). One set for both
43
+ // readers: the laptop's ship-land.js (task 1003764) and the server's mergeBlockReason.
44
+ // readCheckRollup maps a status context's state into `conclusion`, so PENDING and
45
+ // EXPECTED (posted but unfinished, or required but not yet reported) land there too —
46
+ // those are waiting, not red. The server reader used to count anything outside
47
+ // SUCCESS/NEUTRAL/SKIPPED as failing, which named a still-running check as the reason
48
+ // a PR was red and sent the publish reconciler after a regen-heal it did not need.
49
+ const RED_CONCLUSIONS = new Set(['FAILURE', 'ERROR', 'TIMED_OUT', 'CANCELLED', 'ACTION_REQUIRED', 'STARTUP_FAILURE']);
50
+ const isRedCheck = (c) => !!c && RED_CONCLUSIONS.has(String(c.conclusion || '').toUpperCase());
51
+
52
+ module.exports = { readCheckRollup, UNIT_REPORT_CONTEXT, RED_CONCLUSIONS, isRedCheck };
@@ -40,7 +40,7 @@ const os = require('node:os');
40
40
  const path = require('node:path');
41
41
  const { promisify } = require('node:util');
42
42
  const { repoInfo, userAgent } = require('../../src/module-api');
43
- const { readCheckRollup, UNIT_REPORT_CONTEXT } = require('./check-rollup');
43
+ const { readCheckRollup, UNIT_REPORT_CONTEXT, isRedCheck } = require('./check-rollup'); // isRedCheck: only a FAILED check is red, never a pending one (task 1004666)
44
44
  const { loadRepoInfo } = repoInfo;
45
45
 
46
46
  const execFileP = promisify(execFile);
@@ -951,10 +951,6 @@ async function getMergeState({ branch }, deps = {}) {
951
951
  };
952
952
  }
953
953
 
954
- // A non-passing check conclusion (everything that isn't a clean success/skip).
955
- const _BAD_CHECK = (c) =>
956
- c.conclusion && !['SUCCESS', 'NEUTRAL', 'SKIPPED'].includes(String(c.conclusion).toUpperCase());
957
-
958
954
  // Human-readable reason a not-yet-merged PR isn't mergeable, surfaced to a
959
955
  // credential-less session so it reports the truth instead of guessing.
960
956
  function mergeBlockReason(s) {
@@ -967,7 +963,7 @@ function mergeBlockReason(s) {
967
963
  if (s.review_decision === 'CHANGES_REQUESTED') return 'changes_requested';
968
964
  if (s.review_decision === 'REVIEW_REQUIRED') return 'review_required';
969
965
  if (s.merge_state === 'BLOCKED') {
970
- const bad = s.checks.filter(_BAD_CHECK);
966
+ const bad = s.checks.filter(isRedCheck);
971
967
  if (bad.length) return `failing_check:${bad[0].name}`;
972
968
  // Red, and the failing check is one of the nulled nodes: say the list is blind
973
969
  // rather than guess "review or pending", which sent a ship retrying (task 1003988).
@@ -975,7 +971,7 @@ function mergeBlockReason(s) {
975
971
  return 'blocked_required_review_or_pending_check';
976
972
  }
977
973
  if (s.merge_state === 'UNSTABLE') {
978
- const bad = s.checks.filter(_BAD_CHECK);
974
+ const bad = s.checks.filter(isRedCheck);
979
975
  return bad.length ? `non_required_check_failing:${bad[0].name}` : 'non_required_check_pending';
980
976
  }
981
977
  if (s.mergeable === 'UNKNOWN' || s.merge_state === 'UNKNOWN' || s.merge_state == null) {
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.104",
3
+ "version": "1.21.105",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.104",
9
+ "version": "1.21.105",
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.104",
3
+ "version": "1.21.105",
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",
@@ -9470,5 +9470,11 @@
9470
9470
  "id": "1004665",
9471
9471
  "text": "When a builder re-runs a review and attaches a screenshot, the screenshot now actually gets attached to the task. Before, it was quietly thrown away, so reviewers asking for a picture never saw one unless someone added it by h"
9472
9472
  }
9473
+ ],
9474
+ "1.21.105": [
9475
+ {
9476
+ "id": "1004666",
9477
+ "text": "A check that is still running is no longer reported as a failed one. Before, a project whose test service posts a \"still working\" status would see its change called broken and trigger a pointless repair attempt; now it is corr"
9478
+ }
9473
9479
  ]
9474
9480
  }
@@ -18,7 +18,7 @@ const { normalizeApiError } = require('./ship-io.js');
18
18
  const { plainCardLines } = require('./plain-cards.js');
19
19
  // The server's own reader of a PR's check rollup (task 1003988), reused so the laptop
20
20
  // poll names a red PR exactly the way publish-status does (task 1003764). Dependency-free.
21
- const { readCheckRollup, UNIT_REPORT_CONTEXT } = require('../../modules/lifecycle/check-rollup.js');
21
+ const { readCheckRollup, UNIT_REPORT_CONTEXT, RED_CONCLUSIONS } = require('../../modules/lifecycle/check-rollup.js');
22
22
  const { prMergeSentence } = require('../../modules/lifecycle/page-tweak-hold.js');
23
23
 
24
24
  // ---------- the land outcome (task 1002541, part 1) ----------
@@ -474,7 +474,7 @@ function redBlocksMerge(md) {
474
474
  // A check conclusion (CheckRun) or state (status context) that means FAILED. Pending and
475
475
  // expected are waiting, not red — reporting them as failures would be the mirror image of
476
476
  // the bug this names (a failure dressed as waiting).
477
- const RED_CONCLUSIONS = new Set(['FAILURE', 'ERROR', 'TIMED_OUT', 'CANCELLED', 'ACTION_REQUIRED', 'STARTUP_FAILURE']);
477
+ // The set itself lives in check-rollup.js, shared with the server's mergeBlockReason (task 1004666).
478
478
 
479
479
  // localMergeDriver — task 1003764. The laptop path watches its PR with the builder's own
480
480
  // gh, and read only state + mergeCommit, so a red required check was invisible for the
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.104'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.105'; // 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');
@@ -12,6 +12,7 @@
12
12
 
13
13
  import { strict as assert } from 'node:assert';
14
14
  import { createRequire } from 'node:module';
15
+ import fs from 'node:fs';
15
16
 
16
17
  const require = createRequire(import.meta.url);
17
18
  const gh = require('../modules/lifecycle/github-push.js');
@@ -169,6 +170,51 @@ test('mergeBlockReason maps the common blocker states', () => {
169
170
  assert.equal(gh.mergeBlockReason({ ...open, merged: true }), null);
170
171
  });
171
172
 
173
+ // --- task 1004666: a check still running is waiting, not red --------------
174
+ //
175
+ // check-rollup maps a status context's state into `conclusion`, so PENDING and
176
+ // EXPECTED (posted but unfinished, or required but not yet reported) reached
177
+ // mergeBlockReason as conclusions. It counted anything outside SUCCESS/NEUTRAL/
178
+ // SKIPPED as failing, so a BLOCKED PR waiting on a third-party status read
179
+ // "failing_check:<context>" and the reconciler tried a regen-heal on it.
180
+
181
+ const ctx = (context, state) => ({ __typename: 'StatusContext', context, state, description: null });
182
+
183
+ test('1004666 a PENDING or EXPECTED status context on a BLOCKED PR reads as waiting', async () => {
184
+ for (const state of ['PENDING', 'EXPECTED']) {
185
+ const { fetchImpl } = mkFetch({ node: prNode({ mergeStateStatus: 'BLOCKED', rollup: 'PENDING', checks: [TRUST, ctx('ci/external', state)] }) });
186
+ const r = await gh.mergeIfGreen({ branch: 'claude/wt' }, DEPS(fetchImpl));
187
+ assert.equal(r.reason, 'blocked_required_review_or_pending_check', `${state} is not a failure`);
188
+ }
189
+ });
190
+
191
+ test('1004666 on an UNSTABLE PR a pending optional check is pending, not failing', () => {
192
+ const open = { pr_state: 'open', merged: false };
193
+ assert.equal(gh.mergeBlockReason({ ...open, merge_state: 'UNSTABLE', checks: [{ name: 'e2e', conclusion: 'PENDING' }] }), 'non_required_check_pending');
194
+ assert.equal(gh.mergeBlockReason({ ...open, merge_state: 'UNSTABLE', checks: [{ name: 'e2e', conclusion: 'TIMED_OUT' }] }), 'non_required_check_failing:e2e');
195
+ });
196
+
197
+ test('1004666 every red conclusion still names its check, past a pending one', () => {
198
+ for (const state of ['FAILURE', 'ERROR', 'TIMED_OUT', 'CANCELLED', 'ACTION_REQUIRED', 'STARTUP_FAILURE']) {
199
+ const open = { pr_state: 'open', merged: false, merge_state: 'BLOCKED' };
200
+ assert.equal(gh.mergeBlockReason({ ...open, checks: [{ name: 'ci/external', conclusion: 'PENDING' }, { name: 'unit', conclusion: state }] }), 'failing_check:unit', state);
201
+ }
202
+ });
203
+
204
+ test('1004666 the server and the laptop read red from ONE set', () => {
205
+ const rollup = require('../modules/lifecycle/check-rollup.js');
206
+ const land = fs.readFileSync(new URL('../scripts/gds/ship-land.js', import.meta.url), 'utf8');
207
+ const push = fs.readFileSync(new URL('../modules/lifecycle/github-push.js', import.meta.url), 'utf8');
208
+ assert.ok(land.includes("RED_CONCLUSIONS } = require('../../modules/lifecycle/check-rollup.js')"), 'ship-land imports the shared set');
209
+ assert.doesNotMatch(land, /const RED_CONCLUSIONS\s*=/, 'and no longer declares its own');
210
+ assert.doesNotMatch(push, /_BAD_CHECK|'SUCCESS', 'NEUTRAL', 'SKIPPED'/, 'the old allow-list reading is gone');
211
+ for (const c of ['PENDING', 'EXPECTED', 'SUCCESS', 'NEUTRAL', 'SKIPPED', 'STALE', null, undefined]) {
212
+ assert.equal(rollup.isRedCheck({ conclusion: c }), false, String(c));
213
+ }
214
+ assert.equal(rollup.isRedCheck({ conclusion: 'failure' }), true, 'case-insensitive like the old reader');
215
+ assert.equal(rollup.isRedCheck(null), false);
216
+ });
217
+
172
218
  // --- task 1003988: a red PR names what failed, or says it cannot see -------
173
219
  //
174
220
  // Without Checks:read GitHub nulls every CheckRun node and keeps the rollup state,