@bongos/core 1.19.40 → 1.19.41

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.40",
6
- "core_contract": "1.19.40",
7
- "source_commit": "512e419a012e048b5e7d5904053cbc45df054a09",
5
+ "core_version": "1.19.41",
6
+ "core_contract": "1.19.41",
7
+ "source_commit": "ef65ee2c6121ae010e5ad2c1ac67b349cf9b9161",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-11T16:35:25.688Z",
9
+ "built_at": "2026-08-11T16:42:56.706Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 289,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 1523,
20
- "tree_sha256": "39b4dbb47e0100a520bddafb358c1c89d7a74bc6975261774d9dd12059d544dc",
20
+ "tree_sha256": "8bdaf758774aaff55446f0ca5ac714795f728937d893a1085e21cf14db853915",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -3377,12 +3377,12 @@
3377
3377
  {
3378
3378
  "path": "modules/lifecycle/db.js",
3379
3379
  "mode": "0000644",
3380
- "sha256": "c7dfc49df888857c8975077a069e8730d16926d8e92776f09489f69a40068f31"
3380
+ "sha256": "57c88852ff0aa9f0b3673fa62b35f352d92cc9070e7872137003e395c42eb026"
3381
3381
  },
3382
3382
  {
3383
3383
  "path": "modules/lifecycle/done-when.js",
3384
3384
  "mode": "0000644",
3385
- "sha256": "803ce237622eaba321c6fd87db1645b8ba474af3d3486643aa9d52a6b2bd27d1"
3385
+ "sha256": "4e6f6af1cdaf026466155c1b0656a0e89872aa2bb264f36c50997ec4f069e808"
3386
3386
  },
3387
3387
  {
3388
3388
  "path": "modules/lifecycle/github-push.js",
@@ -4027,12 +4027,12 @@
4027
4027
  {
4028
4028
  "path": "package-lock.json",
4029
4029
  "mode": "0000644",
4030
- "sha256": "290c88cdeae475bf124eb3e74292108c669006b69414d258ae3d961b9e80f743"
4030
+ "sha256": "3d5765b40ed913e05bea697ecb878e98fa799e667a5352d4d8f37236a0b671b4"
4031
4031
  },
4032
4032
  {
4033
4033
  "path": "package.json",
4034
4034
  "mode": "0000644",
4035
- "sha256": "316768e98e388920e8710c944cd4fbfd7c2bf947a742a3aa6698d66b35e838e6"
4035
+ "sha256": "552a8ef2e0d6b1a69377cbf67bdc0b49574312a68b0ac4162bc606db5870afff"
4036
4036
  },
4037
4037
  {
4038
4038
  "path": "public-docs/index.html",
@@ -5652,7 +5652,7 @@
5652
5652
  {
5653
5653
  "path": "src/module-api.js",
5654
5654
  "mode": "0000644",
5655
- "sha256": "a122aca901bb83b94b1d475a651eae90c7a7f72251703ce3a81cd14dcf39fe66"
5655
+ "sha256": "f1c39c1f5862092c57f1c5f5232dfce0a9ac41376838899c92c286df3178936f"
5656
5656
  },
5657
5657
  {
5658
5658
  "path": "src/module-loader/loader.js",
@@ -6172,7 +6172,7 @@
6172
6172
  {
6173
6173
  "path": "tests/criterion_progress.mjs",
6174
6174
  "mode": "0000644",
6175
- "sha256": "3ba6ac8556e9f5f0cd57f724cc136d024d64f684ff1d892bf6825e1b8ec0822d"
6175
+ "sha256": "e5b5ccf87a34658b3b25e4289caf702fbc44d77bacfec5bd823d5f6666ff213b"
6176
6176
  },
6177
6177
  {
6178
6178
  "path": "tests/criterion_proposal.mjs",
@@ -6442,7 +6442,7 @@
6442
6442
  {
6443
6443
  "path": "tests/goal_achievement.mjs",
6444
6444
  "mode": "0000644",
6445
- "sha256": "0838db3cbd5922e47777a25828b2a1b662284a251c5b8309610da634d448c90a"
6445
+ "sha256": "a6ddb674d4ce1f6da9eb7b653ef92b9f0fcc85387e70bdf5358aff84ef518824"
6446
6446
  },
6447
6447
  {
6448
6448
  "path": "tests/goal_advisory.mjs",
@@ -6482,7 +6482,7 @@
6482
6482
  {
6483
6483
  "path": "tests/goal_routes.mjs",
6484
6484
  "mode": "0000644",
6485
- "sha256": "cfd76bf4bd4ed22702861833966cd3eb02dee9815d11d0429cb2773ae17a48ad"
6485
+ "sha256": "4fd3ec8ef855b0131e17931908e9aebf16238d45febdf0b8306e2908152e2655"
6486
6486
  },
6487
6487
  {
6488
6488
  "path": "tests/goal_scope_check.mjs",
@@ -893,7 +893,10 @@ async function listCriteriaForGoal(goalId, deps = {}) {
893
893
  counts AS (
894
894
  SELECT tc.criterion_id,
895
895
  count(*)::int AS linked_tasks,
896
- count(*) FILTER (WHERE t.status <> 'shipped')::int AS unshipped_tasks
896
+ -- task 1002651 (migration 017's family): abandoned is a terminal
897
+ -- state (deliberately cut), not pending work — it must not hold a
898
+ -- criterion's "met — pending review" flip hostage forever.
899
+ count(*) FILTER (WHERE t.status NOT IN ('shipped', 'abandoned'))::int AS unshipped_tasks
897
900
  FROM task_criteria tc
898
901
  JOIN tasks t ON t.id = tc.task_id
899
902
  WHERE tc.criterion_id IN (SELECT id FROM crit)
@@ -126,7 +126,7 @@ async function listPendingReviewCriteria({ versionId = null } = {}, deps = {}) {
126
126
  JOIN tasks t ON t.id = tc.task_id
127
127
  WHERE c.satisfied = false
128
128
  GROUP BY c.id
129
- HAVING count(*) FILTER (WHERE t.status <> 'shipped') = 0
129
+ HAVING count(*) FILTER (WHERE t.status NOT IN ('shipped', 'abandoned')) = 0
130
130
  ),
131
131
  goal_unsat AS (
132
132
  SELECT goal_id, count(*)::int AS unsatisfied
@@ -269,7 +269,9 @@ function buildCriterionProgress(versionId, rows, unattributedCount = 0, goalRows
269
269
  const counts = { total: c.tasks.length };
270
270
  for (const t of c.tasks) counts[t.status] = (counts[t.status] || 0) + 1;
271
271
  const shipped = counts.shipped || 0;
272
- const remaining = c.tasks.filter((t) => t.status !== 'shipped');
272
+ // task 1002651: abandoned is terminal (cut work, migration 017's family) —
273
+ // it is not "remaining" and must not block the pending-review flip.
274
+ const remaining = c.tasks.filter((t) => t.status !== 'shipped' && t.status !== 'abandoned');
273
275
  // Auto-flag (ADR 0086 §6 / BV1.R63): a criterion is "met — pending review"
274
276
  // when it has ≥1 linked task and every one has shipped, but it has NOT yet
275
277
  // been confirmed (satisfied=false). Purely DERIVED — no stored column, no
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.40",
3
+ "version": "1.19.41",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.40",
9
+ "version": "1.19.41",
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.40",
3
+ "version": "1.19.41",
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
@@ -507,8 +507,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
507
507
  // landed since 1.19.38 with no explicit bump. run 31512284325. (task 1002620)
508
508
  // 1.19.40 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
509
509
  // landed since 1.19.39 with no explicit bump. run 31513136724. (task 1002620)
510
+ // 1.19.41 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
511
+ // landed since 1.19.40 with no explicit bump. run 31513793491. (task 1002620)
510
512
  // ---------------------------------------------------------------------------
511
- const CORE_VERSION = '1.19.40'; // CI auto-patch carrier (ADR 0161); see changelog above
513
+ const CORE_VERSION = '1.19.41'; // CI auto-patch carrier (ADR 0161); see changelog above
512
514
 
513
515
  // A namespaced logger so a module's log lines are attributable + consistent.
514
516
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -247,6 +247,40 @@ test('isPendingReview: agrees with the rollup it was extracted from', () => {
247
247
  assert.equal(b.pending_review, false);
248
248
  });
249
249
 
250
+ // --- task 1002651 (idea 1000635): abandoned tasks are terminal, not "remaining" ---
251
+
252
+ test('1002651 one shipped + one abandoned → remaining=0, criterion flips to pending review', () => {
253
+ const rows = [
254
+ row(1, 20, 'c-ab', false, 300, 'landed', 'shipped'),
255
+ row(1, 20, 'c-ab', false, 301, 'cut', 'abandoned'),
256
+ ];
257
+ const c = buildCriterionProgress('V', rows, 0).criteria[0];
258
+ assert.equal(c.counts.remaining, 0, 'the abandoned task no longer counts as unshipped');
259
+ assert.deepEqual(c.remaining, [], 'the remaining list omits deliberately-cut work');
260
+ assert.equal(c.counts.abandoned, 1, 'the per-status bucket still reports it honestly');
261
+ assert.equal(c.pending_review, true, 'the criterion surfaces for human confirm instead of hanging forever');
262
+ });
263
+
264
+ test('1002651 abandoned does not hide genuinely unshipped work', () => {
265
+ const rows = [
266
+ row(1, 21, 'c-mix', false, 310, 'landed', 'shipped'),
267
+ row(1, 21, 'c-mix', false, 311, 'cut', 'abandoned'),
268
+ row(1, 21, 'c-mix', false, 312, 'in-flight', 'ready'),
269
+ ];
270
+ const c = buildCriterionProgress('V', rows, 0).criteria[0];
271
+ assert.equal(c.counts.remaining, 1);
272
+ assert.deepEqual(c.remaining.map((t) => t.id), [312], 'only the live task remains');
273
+ assert.equal(c.pending_review, false, 'live work still blocks the flip');
274
+ });
275
+
276
+ test('1002651 an all-abandoned criterion surfaces for review rather than auto-satisfying silently', () => {
277
+ const rows = [row(1, 22, 'c-cut', false, 320, 'cut', 'abandoned')];
278
+ const c = buildCriterionProgress('V', rows, 0).criteria[0];
279
+ // linked>0 + unshipped=0 → pending review: a human confirms or rejects what
280
+ // "met" means for a criterion whose entire linked work was cut.
281
+ assert.equal(c.pending_review, true);
282
+ });
283
+
250
284
  console.log('');
251
285
  console.log(`criterionProgress aggregation tests: ${passed} passed, ${failed} failed`);
252
286
  if (failed > 0) process.exit(1);
@@ -138,7 +138,9 @@ await test('listPendingReviewCriteria: SQL encodes the auto-flag (all shipped) +
138
138
  const sql = String(find(pool.queries, /WITH pending AS/).sql).replace(/\s+/g, ' ');
139
139
  // The derivation: satisfied=false AND every linked task shipped (zero non-shipped).
140
140
  assert.match(sql, /c\.satisfied = false/, 'only unconfirmed criteria are pending');
141
- assert.match(sql, /HAVING count\(\*\) FILTER \(WHERE t\.status <> 'shipped'\) = 0/, 'all linked tasks must be shipped');
141
+ // task 1002651: abandoned is terminal (cut work) — it must not hold the queue
142
+ // entry hostage, so the filter counts only live-and-unshipped tasks.
143
+ assert.match(sql, /HAVING count\(\*\) FILTER \(WHERE t\.status NOT IN \('shipped', 'abandoned'\)\) = 0/, 'all LIVE linked tasks must be shipped');
142
144
  assert.match(sql, /is_last_in_goal/, 'surfaces whether confirming completes the goal');
143
145
  });
144
146
 
@@ -173,6 +173,17 @@ await test('listCriteriaForGoal: pending_review false once the criterion is conf
173
173
  assert.equal(out.pending_review, false);
174
174
  });
175
175
 
176
+ await test('listCriteriaForGoal: the unshipped filter excludes abandoned (task 1002651)', async () => {
177
+ const pool = critPool([critRow()]);
178
+ await listCriteriaForGoal(3, { pool });
179
+ const sel = find(pool.queries, CRIT_SQL_RE);
180
+ assert.match(
181
+ sel.sql,
182
+ /FILTER \(WHERE t\.status NOT IN \('shipped', 'abandoned'\)\)/,
183
+ 'an abandoned linked task must not hold the pending-review flip hostage (migration 017 family)'
184
+ );
185
+ });
186
+
176
187
  await test('listCriteriaForGoal: pending_review false with NO linked tasks (vacuous truth guard)', async () => {
177
188
  const pool = critPool([critRow({ satisfied: false, linked_tasks: 0, unshipped_tasks: 0 })]);
178
189
  const [out] = await listCriteriaForGoal(3, { pool });