@bongos/core 1.20.12 → 1.20.13
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 +14 -14
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/gds/autobongos-run.js +4 -4
- package/scripts/gds/autobongos-verify.js +27 -2
- package/src/module-api.js +1 -1
- package/tests/autobongos_loop.mjs +41 -0
- package/tests/autobongos_verify.mjs +22 -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.20.
|
|
6
|
-
"core_contract": "1.20.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.20.13",
|
|
6
|
+
"core_contract": "1.20.13",
|
|
7
|
+
"source_commit": "1e8134c4bbd7d8623139bfd31ba2701c5bf120ac",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-30T11:
|
|
9
|
+
"built_at": "2026-09-30T11:48:47.948Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 556,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 3208,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "ae284b5e2f56e08cb0b29b13e77a015dc35db8f5be3a99a96a731704c7da0053",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2772,7 +2772,7 @@
|
|
|
2772
2772
|
{
|
|
2773
2773
|
"path": "docs/module-api-changelog.md",
|
|
2774
2774
|
"mode": "0000644",
|
|
2775
|
-
"sha256": "
|
|
2775
|
+
"sha256": "a0725bf013ddc823d06b866f5008497dddf1bf63e2e22852cd6eb79525ce5052"
|
|
2776
2776
|
},
|
|
2777
2777
|
{
|
|
2778
2778
|
"path": "docs/modules-contract.md",
|
|
@@ -8862,12 +8862,12 @@
|
|
|
8862
8862
|
{
|
|
8863
8863
|
"path": "package-lock.json",
|
|
8864
8864
|
"mode": "0000644",
|
|
8865
|
-
"sha256": "
|
|
8865
|
+
"sha256": "09eb2f9f11cdcb5cb24ccddee43c9d09d5e1135a452e0827542ea96d70b2e85f"
|
|
8866
8866
|
},
|
|
8867
8867
|
{
|
|
8868
8868
|
"path": "package.json",
|
|
8869
8869
|
"mode": "0000644",
|
|
8870
|
-
"sha256": "
|
|
8870
|
+
"sha256": "c80938c396a4982970d0850791d002ea2f5c49b93c8e75f6066c33c77bc7ae78"
|
|
8871
8871
|
},
|
|
8872
8872
|
{
|
|
8873
8873
|
"path": "public-docs/index.html",
|
|
@@ -8887,7 +8887,7 @@
|
|
|
8887
8887
|
{
|
|
8888
8888
|
"path": "release-notes.json",
|
|
8889
8889
|
"mode": "0000644",
|
|
8890
|
-
"sha256": "
|
|
8890
|
+
"sha256": "a3de1adc96eea0bd98814403baf635fc2523506d45ca0df0ae263c917b324757"
|
|
8891
8891
|
},
|
|
8892
8892
|
{
|
|
8893
8893
|
"path": "scripts/bongos-mcp.js",
|
|
@@ -9047,7 +9047,7 @@
|
|
|
9047
9047
|
{
|
|
9048
9048
|
"path": "scripts/gds/autobongos-run.js",
|
|
9049
9049
|
"mode": "0000644",
|
|
9050
|
-
"sha256": "
|
|
9050
|
+
"sha256": "5467681b59e569c4d7fccd4ed6ea93cd598a26ddd2fd42c384375f37ae0243ea"
|
|
9051
9051
|
},
|
|
9052
9052
|
{
|
|
9053
9053
|
"path": "scripts/gds/autobongos-service.cmd",
|
|
@@ -9062,7 +9062,7 @@
|
|
|
9062
9062
|
{
|
|
9063
9063
|
"path": "scripts/gds/autobongos-verify.js",
|
|
9064
9064
|
"mode": "0000644",
|
|
9065
|
-
"sha256": "
|
|
9065
|
+
"sha256": "0be5358189145464d2893cebd53057b9fb35be94c2b56bb1b09f386c1f5a6252"
|
|
9066
9066
|
},
|
|
9067
9067
|
{
|
|
9068
9068
|
"path": "scripts/gds/autonomy-gate.js",
|
|
@@ -11002,7 +11002,7 @@
|
|
|
11002
11002
|
{
|
|
11003
11003
|
"path": "src/module-api.js",
|
|
11004
11004
|
"mode": "0000644",
|
|
11005
|
-
"sha256": "
|
|
11005
|
+
"sha256": "20017aec62763445738eeed4b8c1d23cbe6789a77223992022cb4c9e7747111c"
|
|
11006
11006
|
},
|
|
11007
11007
|
{
|
|
11008
11008
|
"path": "src/module-loader/catalog.js",
|
|
@@ -11447,12 +11447,12 @@
|
|
|
11447
11447
|
{
|
|
11448
11448
|
"path": "tests/autobongos_loop.mjs",
|
|
11449
11449
|
"mode": "0000644",
|
|
11450
|
-
"sha256": "
|
|
11450
|
+
"sha256": "e14d2c211c28ebada8e915d0d336989e74410afb68924d9b89dc8efd666457c8"
|
|
11451
11451
|
},
|
|
11452
11452
|
{
|
|
11453
11453
|
"path": "tests/autobongos_verify.mjs",
|
|
11454
11454
|
"mode": "0000644",
|
|
11455
|
-
"sha256": "
|
|
11455
|
+
"sha256": "8b0958dba2589ca94671843a30d317da35b76df6eff9ab75660a0a253f98b7d3"
|
|
11456
11456
|
},
|
|
11457
11457
|
{
|
|
11458
11458
|
"path": "tests/autonomous_fit_protected_paths.mjs",
|
|
@@ -2681,5 +2681,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2681
2681
|
landed since 1.20.10 with no explicit bump. run 36672606449. (task 1002620)
|
|
2682
2682
|
1.20.12 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2683
2683
|
landed since 1.20.11 with no explicit bump. run 36709211337. (task 1002620)
|
|
2684
|
+
1.20.13 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2685
|
+
landed since 1.20.12 with no explicit bump. run 36710762884. (task 1002620)
|
|
2684
2686
|
---------------------------------------------------------------------------
|
|
2685
2687
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.13",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.20.
|
|
9
|
+
"version": "1.20.13",
|
|
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.20.
|
|
3
|
+
"version": "1.20.13",
|
|
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
|
@@ -8124,5 +8124,11 @@
|
|
|
8124
8124
|
"id": "1004190",
|
|
8125
8125
|
"text": "The task list no longer offers you work the system will refuse: a job waiting on another job that hasn't finished yet is now hidden until it's actually ready to start."
|
|
8126
8126
|
}
|
|
8127
|
+
],
|
|
8128
|
+
"1.20.13": [
|
|
8129
|
+
{
|
|
8130
|
+
"id": "1004180",
|
|
8131
|
+
"text": "When the unattended runner fails to pick up a task, its log now always says why, starting with the sentence that explains it, instead of an empty line or a fragment cut off mid-word."
|
|
8132
|
+
}
|
|
8127
8133
|
]
|
|
8128
8134
|
}
|
|
@@ -42,7 +42,7 @@ const gauge = require('./autonomy-gauge.js');
|
|
|
42
42
|
const {
|
|
43
43
|
buildWorkerArgs, buildWorkerPrompt, classifyRun, DEFAULT_MODEL, DEFAULT_WORKER_TIMEOUT_MS,
|
|
44
44
|
} = require('./autobongos-loop.js');
|
|
45
|
-
const { verifyShip } = require('./autobongos-verify.js');
|
|
45
|
+
const { verifyShip, failureReason } = require('./autobongos-verify.js');
|
|
46
46
|
const fence = require('../../modules/autonomy/fence.js');
|
|
47
47
|
// The ADR 0043 protected-path registry, reused rather than re-derived. scripts/
|
|
48
48
|
// sits outside the kernel boundary, so this direct core require is allowed here
|
|
@@ -466,7 +466,7 @@ async function iteration(opts, deps = {}) {
|
|
|
466
466
|
const wtName = deps.worktreeName ? deps.worktreeName(task.id) : worktreeName(task.id);
|
|
467
467
|
const made = await runCmd(process.execPath, [path.join('scripts', 'gds', 'worktree.js'), 'add', wtName, '--base', 'origin/main']);
|
|
468
468
|
if (!made.ok) {
|
|
469
|
-
return log({ event: 'worktree_failed', task_id: task.id, worktree: wtName, reason: (made
|
|
469
|
+
return log({ event: 'worktree_failed', task_id: task.id, worktree: wtName, reason: failureReason(made, 'worktree.js') });
|
|
470
470
|
}
|
|
471
471
|
const wtPath = path.join(REPO_ROOT, '.claude', 'worktrees', wtName);
|
|
472
472
|
|
|
@@ -475,7 +475,7 @@ async function iteration(opts, deps = {}) {
|
|
|
475
475
|
const claimed = await runCmd(process.execPath, [path.join(REPO_ROOT, 'scripts', 'gds', 'claim.js'), String(task.id)], { cwd: wtPath });
|
|
476
476
|
if (!claimed.ok) {
|
|
477
477
|
await runCmd(process.execPath, [path.join('scripts', 'gds', 'worktree.js'), 'remove', wtName]);
|
|
478
|
-
return log({ event: 'claim_failed', task_id: task.id, worktree: wtName, reason: (claimed
|
|
478
|
+
return log({ event: 'claim_failed', task_id: task.id, worktree: wtName, reason: failureReason(claimed, 'claim.js') });
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
// 5. Work, in that tree.
|
|
@@ -966,5 +966,5 @@ module.exports = {
|
|
|
966
966
|
readArgv, pickTask, iteration, record, logPath, spawnWorker, workerEnv, WORKER_ENV_ALLOW, resolveWorkerBin,
|
|
967
967
|
worktreeName, readFence, reconcileLeftoverClaims, pollSignals, waitOrJump, forever, CLAIM_PREFIX, countWorkedSince, codeDrifted, loadedHead, UPGRADE_EXIT_CODE, MIN_UPTIME_BEFORE_UPGRADE_MS,
|
|
968
968
|
heartbeatPath, writeHeartbeat, readHeartbeat, pidAlive, anotherRunnerIsAlive, HEARTBEAT_STALE_MS,
|
|
969
|
-
isRunnerTree, RUNNER_TREE_RE, postHeartbeat, RUNNER_STARTED_AT,
|
|
969
|
+
isRunnerTree, RUNNER_TREE_RE, postHeartbeat, RUNNER_STARTED_AT, failureReason,
|
|
970
970
|
};
|
|
@@ -40,6 +40,31 @@ const { execFile } = require('node:child_process');
|
|
|
40
40
|
|
|
41
41
|
const { classifyStrand, WATCHED } = require('./strand-watch.js');
|
|
42
42
|
|
|
43
|
+
// failureReason — what a failed child process said, fit for one run-log field
|
|
44
|
+
// (task 1004180).
|
|
45
|
+
//
|
|
46
|
+
// Two defects this replaces, both seen live. `(stderr || stdout).slice(-400)`
|
|
47
|
+
// logged reason:"" when a child failed with nothing on either stream — which
|
|
48
|
+
// reads as "the cause was captured and it was nothing". And it kept only the
|
|
49
|
+
// TAIL, so a long refusal card lost its first line, the one that says WHY, and
|
|
50
|
+
// the log began mid-word ("ot look for a way round it."). So: prefer stderr,
|
|
51
|
+
// else stdout; collapse whitespace so a multi-line card reads as one line; keep
|
|
52
|
+
// the HEAD plus a short tail with a marked cut; and when there is no text at all,
|
|
53
|
+
// say which program exited with which code rather than saying nothing.
|
|
54
|
+
function failureReason(res, what, max = 400) {
|
|
55
|
+
const flat = (s) => String(s || '').replace(/\s+/g, ' ').trim();
|
|
56
|
+
const text = flat(res && res.stderr) || flat(res && res.stdout);
|
|
57
|
+
if (!text) {
|
|
58
|
+
const code = res && res.code !== undefined && res.code !== null ? res.code : '?';
|
|
59
|
+
return `${what} exited ${code} with no output`;
|
|
60
|
+
}
|
|
61
|
+
if (text.length <= max) return text;
|
|
62
|
+
const cut = ' … ';
|
|
63
|
+
const tail = Math.floor((max - cut.length) / 3);
|
|
64
|
+
const head = max - cut.length - tail;
|
|
65
|
+
return text.slice(0, head) + cut + text.slice(text.length - tail);
|
|
66
|
+
}
|
|
67
|
+
|
|
43
68
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
44
69
|
|
|
45
70
|
// Statuses a claim can still be sitting in when the worker is already gone.
|
|
@@ -318,7 +343,7 @@ async function verifyShip({ taskId, taskTitle = null, worker = null }, deps = {}
|
|
|
318
343
|
else {
|
|
319
344
|
const r = await release(taskId, `autobongos: ${v.shape} — ${String(v.reason || '').slice(0, 180)}`);
|
|
320
345
|
out.released = !!(r && r.ok);
|
|
321
|
-
if (!out.released) out.release_error = (r
|
|
346
|
+
if (!out.released) out.release_error = failureReason(r, 'release.js', 300);
|
|
322
347
|
}
|
|
323
348
|
}
|
|
324
349
|
|
|
@@ -374,4 +399,4 @@ if (require.main === module) {
|
|
|
374
399
|
});
|
|
375
400
|
}
|
|
376
401
|
|
|
377
|
-
module.exports = { classifyLedger, blockerFor, probeArtifact, fileBlocker, verifyShip };
|
|
402
|
+
module.exports = { classifyLedger, blockerFor, probeArtifact, fileBlocker, verifyShip, failureReason };
|
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.20.
|
|
78
|
+
const CORE_VERSION = '1.20.13'; // 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');
|
|
@@ -381,6 +381,47 @@ test('a refused claim tears the tree down again and never spawns a worker', asyn
|
|
|
381
381
|
assert.match(row.reason, /ALREADY_CLAIMED/);
|
|
382
382
|
});
|
|
383
383
|
|
|
384
|
+
// task 1004180 — observed live: reason:"" when claim.js failed with nothing on
|
|
385
|
+
// either stream, and reasons that began mid-word ("ot look for a way round it.")
|
|
386
|
+
// because `.slice(-400)` kept only the TAIL of a long refusal card.
|
|
387
|
+
test('a claim that fails SILENTLY still says what happened, never an empty reason', async () => {
|
|
388
|
+
const h = harness({ run: async (bin, args) => {
|
|
389
|
+
const script = String(args[0]).split(/[\\/]/).pop();
|
|
390
|
+
return script === 'claim.js' ? { ok: false, code: 3, stdout: '', stderr: ' ' } : { ok: true, code: 0, stdout: '', stderr: '' };
|
|
391
|
+
} });
|
|
392
|
+
const row = await runner.iteration(opts, h.deps);
|
|
393
|
+
assert.equal(row.event, 'claim_failed');
|
|
394
|
+
assert.equal(row.reason, 'claim.js exited 3 with no output');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test('a worktree that cannot be made says so even with no output', async () => {
|
|
398
|
+
const h = harness({ run: async () => ({ ok: false, code: 128, stdout: '', stderr: '' }) });
|
|
399
|
+
const row = await runner.iteration(opts, h.deps);
|
|
400
|
+
assert.equal(row.event, 'worktree_failed');
|
|
401
|
+
assert.equal(row.reason, 'worktree.js exited 128 with no output');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test('a long refusal keeps its HEAD — the part that says why — not just the tail', async () => {
|
|
405
|
+
const why = 'Your work changes the tools that would be used to update it, so the automatic update was refused on purpose.';
|
|
406
|
+
const long = why + ' ' + 'x'.repeat(2000) + ' By design: Yes. Do not look for a way round it.';
|
|
407
|
+
const h = harness({ run: async (bin, args) => {
|
|
408
|
+
const script = String(args[0]).split(/[\\/]/).pop();
|
|
409
|
+
return script === 'claim.js' ? { ok: false, code: 1, stdout: '', stderr: long } : { ok: true, code: 0, stdout: '', stderr: '' };
|
|
410
|
+
} });
|
|
411
|
+
const row = await runner.iteration(opts, h.deps);
|
|
412
|
+
assert.ok(row.reason.startsWith(why), 'the reason must start where the message starts');
|
|
413
|
+
assert.ok(row.reason.endsWith('Do not look for a way round it.'), 'and still carry the tail');
|
|
414
|
+
assert.ok(row.reason.includes('…'), 'the cut is marked');
|
|
415
|
+
assert.ok(row.reason.length <= 400, `bounded, got ${row.reason.length}`);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test('failureReason: stdout is used when stderr is empty, whitespace is collapsed, short text is untouched', () => {
|
|
419
|
+
assert.equal(runner.failureReason({ code: 1, stdout: 'line one' + String.fromCharCode(10) + ' line two', stderr: '' }, 'claim.js'), 'line one line two');
|
|
420
|
+
assert.equal(runner.failureReason({ code: 1, stdout: 'ignored', stderr: 'wins' }, 'claim.js'), 'wins');
|
|
421
|
+
assert.equal(runner.failureReason(null, 'claim.js'), 'claim.js exited ? with no output');
|
|
422
|
+
assert.equal(runner.failureReason({ code: 1, stderr: 'a'.repeat(400) }, 'x').length, 400);
|
|
423
|
+
});
|
|
424
|
+
|
|
384
425
|
test('a worker that cannot finish frees the claim; one that really shipped does not', async () => {
|
|
385
426
|
// Which of these happens is now decided by the LEDGER, not by the worker's own
|
|
386
427
|
// verdict: a stuck worker leaves the task still `active`, a finished one leaves
|
|
@@ -265,6 +265,28 @@ test('a release that fails does not get reported as a release', async () => {
|
|
|
265
265
|
assert.equal(out.blocker.linked, false);
|
|
266
266
|
});
|
|
267
267
|
|
|
268
|
+
// task 1004180 — the release error used to keep only the last 300 characters,
|
|
269
|
+
// cutting off the start of the message, which is the part that says why.
|
|
270
|
+
test('a long release error keeps its head, and an empty one still names the exit', async () => {
|
|
271
|
+
const why = 'Only the original session can release it.';
|
|
272
|
+
const long = await verifyShip({ taskId: 42 }, {
|
|
273
|
+
api: fakeApi(),
|
|
274
|
+
task: task({ id: '42', status: 'active' }),
|
|
275
|
+
release: async () => ({ ok: false, code: 1, stderr: why + ' ' + 'y'.repeat(1000) }),
|
|
276
|
+
fileBlocker: async (_a, _r, opts) => ({ filed: true, blockerId: 7, linked: opts.link }),
|
|
277
|
+
});
|
|
278
|
+
assert.ok(long.release_error.startsWith(why), long.release_error.slice(0, 60));
|
|
279
|
+
assert.ok(long.release_error.length <= 300);
|
|
280
|
+
|
|
281
|
+
const silent = await verifyShip({ taskId: 42 }, {
|
|
282
|
+
api: fakeApi(),
|
|
283
|
+
task: task({ id: '42', status: 'active' }),
|
|
284
|
+
release: async () => ({ ok: false, code: 2, stdout: '', stderr: '' }),
|
|
285
|
+
fileBlocker: async (_a, _r, opts) => ({ filed: true, blockerId: 7, linked: opts.link }),
|
|
286
|
+
});
|
|
287
|
+
assert.equal(silent.release_error, 'release.js exited 2 with no output');
|
|
288
|
+
});
|
|
289
|
+
|
|
268
290
|
test('the artifact probe only runs when the ledger claims success', async () => {
|
|
269
291
|
let probes = 0;
|
|
270
292
|
const probeArtifactSpy = async () => { probes += 1; return notOnMain; };
|