@bongos/core 1.19.708 → 1.19.709

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,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.708",
6
- "core_contract": "1.19.708",
7
- "source_commit": "42f201a980e11b8b417c2afa527230ed1ab05775",
5
+ "core_version": "1.19.709",
6
+ "core_contract": "1.19.709",
7
+ "source_commit": "e931b6db287584c48e0e0fbb4f302198e7ad7ae1",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-13T06:14:54.451Z",
9
+ "built_at": "2026-09-13T06:55:41.726Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 477,
13
13
  "agent_docs_stubbed": 24,
14
- "functional_verbatim": 2138,
14
+ "functional_verbatim": 2140,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2639,
20
- "tree_sha256": "369223629e32da5858cc92a00ab2643eaa169ca59d1d89d6ab77ab8c50e3fd57",
19
+ "file_count": 2641,
20
+ "tree_sha256": "38d60d3f6875f0ae881aeb4942d3a09b3aa7f9d0df66d7ec712d0ead28d6b3dd",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2802,7 +2802,7 @@
2802
2802
  {
2803
2803
  "path": "docs/module-api-changelog.md",
2804
2804
  "mode": "0000644",
2805
- "sha256": "fd622c24f29995f2c8ebdd272cf5784f05285bbafc6c3da490308ed5e1730ba7"
2805
+ "sha256": "ebca814eb4d786c2958e44b166b23986ef624b9145bc9c5a6b5a0068ef05a411"
2806
2806
  },
2807
2807
  {
2808
2808
  "path": "docs/modules-contract.md",
@@ -3859,6 +3859,11 @@
3859
3859
  "mode": "0000644",
3860
3860
  "sha256": "03df9104a0309011df1b6e96627c62cc6612f34b654134306d13fd5c89b8f038"
3861
3861
  },
3862
+ {
3863
+ "path": "migrations/core_239_claimable_respects_blocker_status.sql",
3864
+ "mode": "0000644",
3865
+ "sha256": "75a3d09bce8a40b51015054b4f4c77706ce76e7f8cb3fc4a54048c9bd33814b8"
3866
+ },
3862
3867
  {
3863
3868
  "path": "modules/agents/lib/validate.js",
3864
3869
  "mode": "0000644",
@@ -7782,12 +7787,12 @@
7782
7787
  {
7783
7788
  "path": "package-lock.json",
7784
7789
  "mode": "0000644",
7785
- "sha256": "4f9b97401aebd998dd23bf3ec95a333fd6c596c0ea5551dc02dc6e863ad92742"
7790
+ "sha256": "9e9bbca8f0efb3bed421d30ad97be1b18edf775e80cbc07590a583b2452ea609"
7786
7791
  },
7787
7792
  {
7788
7793
  "path": "package.json",
7789
7794
  "mode": "0000644",
7790
- "sha256": "9002e7fc0a70168d56042d71c7905aa045b541dbc36d61e88180a8dfb1e0f559"
7795
+ "sha256": "eb6d8897c8a65579eaee354a12435b412606029b3f182f192cf5628941580075"
7791
7796
  },
7792
7797
  {
7793
7798
  "path": "public-docs/index.html",
@@ -9567,7 +9572,7 @@
9567
9572
  {
9568
9573
  "path": "src/module-api.js",
9569
9574
  "mode": "0000644",
9570
- "sha256": "98205e89563b71f2e6ddbe44a9639464bdc589c20f3f0a8de12a2b968ac06569"
9575
+ "sha256": "5359ee8368381b16bb0d6e18a13e390dd9366b3a3cdc3965e749cb5746f5cc4f"
9571
9576
  },
9572
9577
  {
9573
9578
  "path": "src/module-loader/catalog.js",
@@ -10159,6 +10164,11 @@
10159
10164
  "mode": "0000644",
10160
10165
  "sha256": "6e5ff3acb835fd6219136ee2a5e144841076a82733915cc7111688f707464442"
10161
10166
  },
10167
+ {
10168
+ "path": "tests/claimable_blocker_status.mjs",
10169
+ "mode": "0000644",
10170
+ "sha256": "6bb10ab7ee346bbe673055e46e9fc53865540157c48c203e57c11ad51ba44336"
10171
+ },
10162
10172
  {
10163
10173
  "path": "tests/claims_bind_session.mjs",
10164
10174
  "mode": "0000644",
@@ -1875,5 +1875,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
1875
1875
  landed since 1.19.706 with no explicit bump. run 34741980536. (task 1002620)
1876
1876
  1.19.708 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1877
1877
  landed since 1.19.707 with no explicit bump. run 34742245823. (task 1002620)
1878
+ 1.19.709 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1879
+ landed since 1.19.708 with no explicit bump. run 34743928060. (task 1002620)
1878
1880
  ---------------------------------------------------------------------------
1879
1881
  ```
@@ -0,0 +1,67 @@
1
+ -- core_239 — claimable_tasks must filter on the blocker being OPEN, not on the
2
+ -- link row existing (task 1003863).
3
+ --
4
+ -- THE DEFECT. core_231's claimable_tasks excludes a task if ANY task_blockers row
5
+ -- exists for it, with no check on the blocker's status:
6
+ --
7
+ -- AND NOT EXISTS (SELECT 1 FROM task_blockers tb WHERE tb.task_id = t.id)
8
+ --
9
+ -- Nothing ever deletes that link: there is no `DELETE FROM task_blockers` in
10
+ -- migrations/ or modules/. So when core_223's trg_promote_tasks_on_blocker_resolve
11
+ -- flips a task 'blocked' -> 'ready' on its last blocker resolving, the link row
12
+ -- survives and this view keeps hiding the task. A task that has ever been blocked
13
+ -- is promoted to ready and then never appears in /builder-start again.
14
+ --
15
+ -- It stays claimable BY ID — modules/lifecycle/db-claims.js checks b.status='open'
16
+ -- correctly (task 1002650) — so the row is workable but unfindable. Same family as
17
+ -- bug 1003553, inverted: there the feed advertises what the claim path refuses;
18
+ -- here the claim path accepts what the feed hides.
19
+ --
20
+ -- LATENT, NOT HARMLESS. Every blocker on the instance was status='open' when this
21
+ -- was written, so the bad branch had never executed. Eleven tasks were linked to
22
+ -- open blockers at that moment, and each would have vanished from the queue the
23
+ -- moment its blocker resolved — including task 1001440 behind blocker 1000144.
24
+ --
25
+ -- THE LEFT JOIN IS LOAD-BEARING. Legacy markdown-anchor links carry
26
+ -- task_blockers.blocker_ref with NO blockers row (core_223's header, same scope as
27
+ -- 176). An inner join would drop those rows from the NOT EXISTS and thereby
28
+ -- UNHIDE their tasks — a silent widening of the feed. `LEFT JOIN` plus
29
+ -- `b.id IS NULL OR b.status = 'open'` keeps an unresolvable legacy link blocking,
30
+ -- which is exactly today's behaviour. Fail closed on anything we cannot resolve.
31
+ --
32
+ -- Scope: this view only. The OTHER gap in the same view — it never filters
33
+ -- unshipped dependencies either (finding #60 of task 1000379) — is a separate
34
+ -- clause with a separate fix and is deliberately NOT bundled here.
35
+ --
36
+ -- Idempotent: DROP VIEW IF EXISTS + CREATE, the same shape core_231 used. The
37
+ -- column list is `t.*` plus the calibrated-minutes column, byte-identical to
38
+ -- core_231 so the wildcard re-expands to every current column (core_227's header
39
+ -- records six columns that silently never reached the feed by diverging here).
40
+
41
+ BEGIN;
42
+
43
+ DROP VIEW IF EXISTS claimable_tasks;
44
+ CREATE VIEW claimable_tasks AS
45
+ SELECT t.*,
46
+ pms_calibrated_minutes(t.est_minutes, t.kind) AS est_minutes_calibrated
47
+ FROM tasks t
48
+ WHERE t.status = 'ready'
49
+ AND t.title NOT LIKE '\_\_smoke\_\_%' ESCAPE '\'
50
+ AND NOT EXISTS (
51
+ SELECT 1
52
+ FROM task_blockers tb
53
+ LEFT JOIN blockers b ON b.id = tb.blocker_id
54
+ WHERE tb.task_id = t.id
55
+ AND (b.id IS NULL OR b.status = 'open')
56
+ )
57
+ AND NOT EXISTS (
58
+ SELECT 1 FROM claims c
59
+ JOIN tasks t2 ON t2.id = c.task_id
60
+ WHERE c.released_at IS NULL
61
+ AND t2.touches && t.touches
62
+ );
63
+
64
+ COMMIT;
65
+
66
+ INSERT INTO schema_migrations (version) VALUES ('core_239_claimable_respects_blocker_status')
67
+ ON CONFLICT (version) DO NOTHING;
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.708",
3
+ "version": "1.19.709",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.708",
9
+ "version": "1.19.709",
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.708",
3
+ "version": "1.19.709",
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
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.708'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.709'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -0,0 +1,98 @@
1
+ // tests/claimable_blocker_status.mjs
2
+ //
3
+ // Task 1003863 — claimable_tasks must hide a task only while a blocker on it is
4
+ // still OPEN, not forever because a link row exists. Static shape guard for
5
+ // core_239, in the style of blocker_resolve_guard.mjs: no live DB, assert the SQL
6
+ // TEXT so a future edit cannot silently drop the fix or "simplify" the LEFT JOIN
7
+ // back into the bug. Runtime behaviour is proven against real Postgres at deploy.
8
+ //
9
+ // THE BUG THIS PINS. core_231's view excluded a task if ANY task_blockers row
10
+ // existed, and nothing ever deletes that row. core_223's trigger flips the task
11
+ // 'blocked' -> 'ready' when its last blocker resolves, so the task became ready
12
+ // and permanently invisible to /builder-start — workable by id, unfindable in the
13
+ // feed. Latent when found (every blocker was open), but eleven tasks were one
14
+ // resolve away from vanishing.
15
+ //
16
+ // Run: node tests/claimable_blocker_status.mjs
17
+
18
+ import { strict as assert } from 'node:assert';
19
+ import { readFileSync, readdirSync } from 'node:fs';
20
+ import { fileURLToPath } from 'node:url';
21
+ import { dirname, join } from 'node:path';
22
+ import { makeRunner } from './helpers.mjs';
23
+
24
+ const __dirname = dirname(fileURLToPath(import.meta.url));
25
+ const repoRoot = join(__dirname, '..');
26
+ const read = (...p) => readFileSync(join(repoRoot, ...p), 'utf8');
27
+
28
+ const MIG = 'core_239_claimable_respects_blocker_status.sql';
29
+ const mig = read('migrations', MIG);
30
+ // Comments carry the rationale; strip them when asserting on the executable body
31
+ // so a claim in prose can never stand in for the real SQL.
32
+ const body = mig.replace(/^\s*--.*$/gm, '').replace(/\s+/g, ' ');
33
+
34
+ const { test, summary } = makeRunner();
35
+
36
+ await test('239 replaces claimable_tasks, in core_231-s drop-and-create shape', () => {
37
+ assert.match(body, /DROP VIEW IF EXISTS claimable_tasks;/);
38
+ assert.match(body, /CREATE VIEW claimable_tasks AS/);
39
+ });
40
+
41
+ await test('the blocker clause filters on the blocker being OPEN, not on the link existing', () => {
42
+ assert.match(body, /FROM task_blockers tb/, 'it still looks at the link table');
43
+ assert.match(body, /blockers b ON b\.id = tb\.blocker_id/, 'and resolves the blocker itself');
44
+ assert.match(body, /b\.status = 'open'/, 'so a RESOLVED blocker stops hiding the task');
45
+ });
46
+
47
+ await test('the join is a LEFT JOIN with the fail-closed arm — an inner join would UNHIDE legacy links', () => {
48
+ // Legacy markdown-anchor links carry task_blockers.blocker_ref with no blockers
49
+ // row (core_223 header, same scope as 176). An inner join drops them from the
50
+ // NOT EXISTS and silently widens the feed. This is the one line most likely to
51
+ // be "tidied" later, so it is asserted from both directions.
52
+ assert.match(body, /LEFT JOIN blockers b/, 'LEFT, so an unresolvable link survives the join');
53
+ assert.match(body, /b\.id IS NULL OR b\.status = 'open'/, 'and an unresolvable link still blocks');
54
+ assert.doesNotMatch(
55
+ body,
56
+ /(?<!LEFT )JOIN blockers b ON b\.id = tb\.blocker_id/,
57
+ 'a bare inner JOIN onto blockers would unhide every legacy link'
58
+ );
59
+ });
60
+
61
+ await test('the view-s OTHER guards survive the rewrite', () => {
62
+ // A view rewrite replaces the whole definition, so dropping a sibling clause is
63
+ // a silent regression with no error anywhere. Each of these was its own fix.
64
+ assert.match(body, /t\.status = 'ready'/, 'only ready tasks are claimable');
65
+ assert.match(body, /NOT LIKE '\\_\\_smoke\\_\\_%'/, 'smoke rows stay out of the feed');
66
+ assert.match(body, /c\.released_at IS NULL/, 'and the touches-overlap parallel-safety guard holds');
67
+ assert.match(body, /t2\.touches && t\.touches/, 'against other live claims');
68
+ });
69
+
70
+ await test('the column list stays the t.* wildcard (core_227-s silent-column lesson)', () => {
71
+ // core_227's header records six columns that never reached the feed because a
72
+ // rewrite spelled the list out instead of re-expanding it.
73
+ assert.match(body, /SELECT t\.\*,/, 'the wildcard re-expands to every current column');
74
+ assert.match(body, /pms_calibrated_minutes\(t\.est_minutes, t\.kind\) AS est_minutes_calibrated/);
75
+ });
76
+
77
+ await test('239 is the LAST definition of claimable_tasks in migrations/', () => {
78
+ // If a later migration redefines the view, it inherits whatever that one says —
79
+ // which is exactly how 177 silently dropped migration 176's blocker clause from
80
+ // task_is_fully_unblocked. Catch the next one at the source.
81
+ const defs = readdirSync(join(repoRoot, 'migrations'))
82
+ .filter((f) => f.endsWith('.sql'))
83
+ .filter((f) => /CREATE VIEW claimable_tasks/.test(read('migrations', f)))
84
+ .sort();
85
+ assert.ok(defs.includes(MIG), 'this migration defines the view');
86
+ assert.equal(defs[defs.length - 1], MIG,
87
+ `a later migration redefines claimable_tasks and must carry the blocker-status clause: ${defs.join(', ')}`);
88
+ });
89
+
90
+ await test('the feed and the claim path enforce the SAME rule', () => {
91
+ // The whole bug class is the two gates disagreeing. db-claims.js got this right
92
+ // for the by-id path in task 1002650; this pins that they still match.
93
+ const claims = read('modules', 'lifecycle', 'db-claims.js');
94
+ assert.match(claims, /b\.status = 'open'/,
95
+ 'the by-id claim path checks blocker STATUS, and the view must not be laxer or stricter');
96
+ });
97
+
98
+ summary();