@bongos/core 1.19.557 → 1.19.558
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 +12 -12
- package/docs/architecture.md +2 -1
- package/docs/module-api-changelog.md +2 -0
- package/modules/lifecycle/done-when.js +31 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/auto_satisfy_criteria.mjs +53 -1
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.558",
|
|
6
|
+
"core_contract": "1.19.558",
|
|
7
|
+
"source_commit": "3f1c35f7e8595d597e7174a4b57801739138e35a",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-06T16:32
|
|
9
|
+
"built_at": "2026-09-06T16:50:32.542Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 443,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 2487,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "cf1dadb6f0fb444dffe307f0e5b6d7ad76eb9e3fc7833a3e034f456f45ecf4c3",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -1787,7 +1787,7 @@
|
|
|
1787
1787
|
{
|
|
1788
1788
|
"path": "docs/architecture.md",
|
|
1789
1789
|
"mode": "0000644",
|
|
1790
|
-
"sha256": "
|
|
1790
|
+
"sha256": "1fe7548e94bd9b29642bdddc8dacb201072782f181b517be2bac5fed1b1cd356"
|
|
1791
1791
|
},
|
|
1792
1792
|
{
|
|
1793
1793
|
"path": "docs/branding-contract.md",
|
|
@@ -2657,7 +2657,7 @@
|
|
|
2657
2657
|
{
|
|
2658
2658
|
"path": "docs/module-api-changelog.md",
|
|
2659
2659
|
"mode": "0000644",
|
|
2660
|
-
"sha256": "
|
|
2660
|
+
"sha256": "7fd202776ba59c21897773679f9f07221de79ba577a6d5e6500df52698b85386"
|
|
2661
2661
|
},
|
|
2662
2662
|
{
|
|
2663
2663
|
"path": "docs/modules-contract.md",
|
|
@@ -5482,7 +5482,7 @@
|
|
|
5482
5482
|
{
|
|
5483
5483
|
"path": "modules/lifecycle/done-when.js",
|
|
5484
5484
|
"mode": "0000644",
|
|
5485
|
-
"sha256": "
|
|
5485
|
+
"sha256": "4e798aff55de6481ea9da3f8a08e68d99902f3eac4749e30d727517ff61176b5"
|
|
5486
5486
|
},
|
|
5487
5487
|
{
|
|
5488
5488
|
"path": "modules/lifecycle/est-advisory.js",
|
|
@@ -7477,12 +7477,12 @@
|
|
|
7477
7477
|
{
|
|
7478
7478
|
"path": "package-lock.json",
|
|
7479
7479
|
"mode": "0000644",
|
|
7480
|
-
"sha256": "
|
|
7480
|
+
"sha256": "3d98dfe15e37d556fbb8b69fe49aefaa10ffb350560704361a420803e5bd1169"
|
|
7481
7481
|
},
|
|
7482
7482
|
{
|
|
7483
7483
|
"path": "package.json",
|
|
7484
7484
|
"mode": "0000644",
|
|
7485
|
-
"sha256": "
|
|
7485
|
+
"sha256": "94d2145a407d981305d11aa4f7b6fcb2997d831856b978836ceece7f91f48de5"
|
|
7486
7486
|
},
|
|
7487
7487
|
{
|
|
7488
7488
|
"path": "public-docs/index.html",
|
|
@@ -9177,7 +9177,7 @@
|
|
|
9177
9177
|
{
|
|
9178
9178
|
"path": "src/module-api.js",
|
|
9179
9179
|
"mode": "0000644",
|
|
9180
|
-
"sha256": "
|
|
9180
|
+
"sha256": "8e9e2a140d38ec1307d8c062aeb950ee5cadc17a0a166d5e96ec41f3b202c375"
|
|
9181
9181
|
},
|
|
9182
9182
|
{
|
|
9183
9183
|
"path": "src/module-loader/catalog.js",
|
|
@@ -9437,7 +9437,7 @@
|
|
|
9437
9437
|
{
|
|
9438
9438
|
"path": "tests/auto_satisfy_criteria.mjs",
|
|
9439
9439
|
"mode": "0000644",
|
|
9440
|
-
"sha256": "
|
|
9440
|
+
"sha256": "d63beea7b680a194d163aa5ad11806c9aa860de93d614195a05a33082e9f8adb"
|
|
9441
9441
|
},
|
|
9442
9442
|
{
|
|
9443
9443
|
"path": "tests/autonomous_fit_protected_paths.mjs",
|
package/docs/architecture.md
CHANGED
|
@@ -120,7 +120,8 @@ tasks(id, version_id, title, description, status, touches text[], est_minutes, e
|
|
|
120
120
|
shipped = merged to main + deployed; credits awarded here (credit_log.reason =
|
|
121
121
|
'task.shipped' — ADR 0120 pays on land, not on confirm; which streams
|
|
122
122
|
actually pay depends on the instance reward mode, ADR 0146)
|
|
123
|
-
indexes: (version_id, status), (priority), GIN(touches)
|
|
123
|
+
indexes: (version_id, status), (priority), GIN(touches), (goal_id), (parent_task_id),
|
|
124
|
+
(created_by), (shipped_by) WHERE needs_rebase
|
|
124
125
|
task_blockers(task_id, blocker_ref) -- markdown anchors are canonical
|
|
125
126
|
claims(id, task_id, builder_id, worktree_name, claimed_at, released_at, outcome)
|
|
126
127
|
UNIQUE INDEX uniq_active_claim_per_task ON (task_id) WHERE released_at IS NULL
|
|
@@ -1563,5 +1563,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1563
1563
|
landed since 1.19.555 with no explicit bump. run 34045323629. (task 1002620)
|
|
1564
1564
|
1.19.557 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1565
1565
|
landed since 1.19.556 with no explicit bump. run 34045753301. (task 1002620)
|
|
1566
|
+
1.19.558 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1567
|
+
landed since 1.19.557 with no explicit bump. run 34046699120. (task 1002620)
|
|
1566
1568
|
---------------------------------------------------------------------------
|
|
1567
1569
|
```
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
const { pool } = require('../../src/module-api');
|
|
27
27
|
const { nonTerminalSql } = require('./task-status');
|
|
28
|
+
const { SMOKE_NOT_LIKE } = require('./db-shared.js');
|
|
28
29
|
|
|
29
30
|
// isPendingReview — the ONE derivation of the "met — pending review" auto-flag
|
|
30
31
|
// (ADR 0086 §6 / BV1.R63): a criterion with >=1 linked task, every one shipped,
|
|
@@ -445,8 +446,33 @@ async function autoSatisfyShippedCriteria(exec, { taskId = null } = {}) {
|
|
|
445
446
|
// Cascade. Deliberately re-derived from the table rather than from `criteria`:
|
|
446
447
|
// a goal completes when ALL of its criteria are satisfied, and the ones that
|
|
447
448
|
// closed in THIS call may not be all of them. Mirrors db.achieveGoalIfComplete
|
|
448
|
-
// exactly — open goals only, >=1 criterion, zero unsatisfied
|
|
449
|
-
// so it sees the flips above
|
|
449
|
+
// exactly — open goals only, >=1 criterion, zero unsatisfied, AND no unfinished
|
|
450
|
+
// task (BV1.R09, task 1003596) — but on `exec`, so it sees the flips above
|
|
451
|
+
// inside the same transaction.
|
|
452
|
+
//
|
|
453
|
+
// THE THIRD CLAUSE, and why it must run HERE and not only in db-goals.js. This
|
|
454
|
+
// is the path that actually fires on every ship (`db-ship.js`) and every
|
|
455
|
+
// reconciler tick (`publish-reconciler.js`); `achieveGoalIfComplete` covers the
|
|
456
|
+
// manual `POST /done-when/:id/satisfy`. The two are a DELIBERATE duplicate —
|
|
457
|
+
// collapsing them into one helper creates a require cycle (db-goals requires
|
|
458
|
+
// done-when) — so the rule is written twice on purpose and both copies must
|
|
459
|
+
// move together, or a goal closes correctly through one door and wrongly
|
|
460
|
+
// through the other. ADR 0250 D2.
|
|
461
|
+
//
|
|
462
|
+
// `exec` is load-bearing for THIS clause specifically. The shipping task's flip
|
|
463
|
+
// to `shipped` is uncommitted at this point in the ship transaction; on the
|
|
464
|
+
// module pool the subquery would still see it as `confirmed` — non-terminal —
|
|
465
|
+
// and refuse to close a goal whose last task just landed. Every goal would then
|
|
466
|
+
// need a second, later trigger to close at all.
|
|
467
|
+
//
|
|
468
|
+
// Shipping is NOT blocked by any of this: the ship's own writes are untouched
|
|
469
|
+
// and this UPDATE simply matches no row, leaving the goal open.
|
|
470
|
+
//
|
|
471
|
+
// The subquery is index-served: migration 228 added idx_tasks_goal ON tasks
|
|
472
|
+
// (goal_id), so this is a small index scan per candidate goal, and the candidate
|
|
473
|
+
// set is only the goals whose criteria just closed in this call. (Raised in
|
|
474
|
+
// review off docs/architecture.md, whose tasks index list predated 228 and has
|
|
475
|
+
// been corrected.)
|
|
450
476
|
const goalIds = [...new Set(criteria.map((c) => c.goal_id).filter((g) => g != null))].map(Number);
|
|
451
477
|
if (goalIds.length === 0) return { criteria, goals: [] };
|
|
452
478
|
const { rows: goals } = await exec.query(
|
|
@@ -456,6 +482,9 @@ async function autoSatisfyShippedCriteria(exec, { taskId = null } = {}) {
|
|
|
456
482
|
AND g.status = 'open'
|
|
457
483
|
AND EXISTS (SELECT 1 FROM done_when_criteria d WHERE d.goal_id = g.id)
|
|
458
484
|
AND NOT EXISTS (SELECT 1 FROM done_when_criteria d WHERE d.goal_id = g.id AND NOT d.satisfied)
|
|
485
|
+
AND NOT EXISTS (SELECT 1 FROM tasks t WHERE t.goal_id = g.id
|
|
486
|
+
AND ${nonTerminalSql()}
|
|
487
|
+
AND ${SMOKE_NOT_LIKE.replace(/title/, 't.title')})
|
|
459
488
|
RETURNING g.id, g.title, g.version_id, g.status`,
|
|
460
489
|
[goalIds]
|
|
461
490
|
);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.558",
|
|
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.558",
|
|
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.558",
|
|
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
|
@@ -49,7 +49,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
49
49
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
50
50
|
// the entry to that file. Look for a version's history there, not here.
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
|
-
const CORE_VERSION = '1.19.
|
|
52
|
+
const CORE_VERSION = '1.19.558'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
53
53
|
|
|
54
54
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
55
55
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -213,7 +213,6 @@ await test('closeCompletedWorkForShip: silent on the common no-op, and logs what
|
|
|
213
213
|
assert.match(loud[0], /shipTask 9/);
|
|
214
214
|
});
|
|
215
215
|
|
|
216
|
-
summary();
|
|
217
216
|
|
|
218
217
|
// ---- 4. the terminal vocabulary has exactly one spelling (task 1003588) ----
|
|
219
218
|
|
|
@@ -233,3 +232,56 @@ await test('VOCABULARY: no consumer re-types the terminal set behind the constan
|
|
|
233
232
|
f + ' must read the terminal set from task-status.js, not spell it again');
|
|
234
233
|
}
|
|
235
234
|
});
|
|
235
|
+
|
|
236
|
+
// ---- 5. the cascade carries R08's rule too (BV1.R09, task 1003596) ---------
|
|
237
|
+
//
|
|
238
|
+
// The two achievement writers are a DELIBERATE duplicate — collapsing them makes
|
|
239
|
+
// a require cycle (db-goals requires done-when) — so the danger is that only one
|
|
240
|
+
// moves and a goal then closes correctly through the manual door and wrongly
|
|
241
|
+
// through the ship. These cases pin the third clause on the path that actually
|
|
242
|
+
// fires on every ship and every reconciler tick.
|
|
243
|
+
|
|
244
|
+
await test('CASCADE (R09): an unfinished task in the goal blocks the flip', async () => {
|
|
245
|
+
const exec = fakeExec({ rows: [CRIT] }, { rows: [] });
|
|
246
|
+
await autoSatisfyShippedCriteria(exec, { taskId: 5 });
|
|
247
|
+
const cascade = String(exec.calls[1].sql).replace(/\s+/g, ' ');
|
|
248
|
+
assert.match(cascade, /NOT EXISTS \(SELECT 1 FROM tasks t WHERE t\.goal_id = g\.id/,
|
|
249
|
+
'satisfied criteria are necessary, not sufficient — ADR 0250 D2');
|
|
250
|
+
assert.match(cascade, /t\.status NOT IN \('shipped', 'abandoned'\)/,
|
|
251
|
+
'the terminal set is R01\'s, so completed/confirmed hold the goal open');
|
|
252
|
+
assert.match(cascade, /t\.title NOT LIKE/,
|
|
253
|
+
'a smoke fixture must not wedge a real goal open forever');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
await test('CASCADE (R09): the rule matches db-goals.js — both writers or neither', () => {
|
|
257
|
+
// The duplicate is intentional; the DRIFT is the bug. Both copies must ask for
|
|
258
|
+
// the same terminal set and the same smoke exclusion.
|
|
259
|
+
const dbGoals = readFileSync(new URL('../modules/lifecycle/db-goals.js', import.meta.url), 'utf8');
|
|
260
|
+
const doneWhen = readFileSync(new URL('../modules/lifecycle/done-when.js', import.meta.url), 'utf8');
|
|
261
|
+
for (const [name, src] of [['db-goals.js', dbGoals], ['done-when.js', doneWhen]]) {
|
|
262
|
+
assert.match(src, /FROM tasks t/, name + ' must consult tasks at all');
|
|
263
|
+
assert.match(src, /\$\{nonTerminalSql\(\)\}/,
|
|
264
|
+
name + ' must read the terminal set from task-status.js, not re-type it');
|
|
265
|
+
assert.match(src, /SMOKE_NOT_LIKE\.replace/,
|
|
266
|
+
name + ' must exclude smoke rows the way version_progress does');
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
await test('CASCADE (R09): the check runs on the caller exec, never the module pool', async () => {
|
|
271
|
+
// Load-bearing for this clause specifically: the shipping task's flip to
|
|
272
|
+
// `shipped` is uncommitted here, so on the module pool the subquery would still
|
|
273
|
+
// read it as `confirmed` — non-terminal — and refuse to close a goal whose last
|
|
274
|
+
// task just landed.
|
|
275
|
+
const exec = fakeExec({ rows: [CRIT] }, { rows: [] });
|
|
276
|
+
await autoSatisfyShippedCriteria(exec, { taskId: 5 });
|
|
277
|
+
assert.equal(exec.calls.length, 2, 'both statements go through the passed exec');
|
|
278
|
+
assert.match(String(exec.calls[1].sql), /UPDATE goals g/,
|
|
279
|
+
'the cascade UPDATE is the second, and it is the one carrying the task check');
|
|
280
|
+
const src = readFileSync(new URL('../modules/lifecycle/done-when.js', import.meta.url), 'utf8');
|
|
281
|
+
const start = src.indexOf('async function autoSatisfyShippedCriteria');
|
|
282
|
+
const body = src.slice(start, src.indexOf('describeAutoClose', start));
|
|
283
|
+
assert.equal(/\bpool\.query\(/.test(body), false,
|
|
284
|
+
'nothing in this function may reach the module pool');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
summary();
|