@bongos/core 1.19.290 → 1.19.292
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 +25 -15
- package/migrations/core_223_blocker_resolve_respects_other_open_blockers.sql +74 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +8 -0
- package/package-lock.json +121 -2
- package/package.json +3 -2
- package/scripts/gds/package-core.js +7 -4
- package/src/module-api.js +5 -1
- package/tests/blocker_resolve_guard.mjs +96 -0
- package/tests/cutover_codemod.mjs +1 -1
- package/tests/package_core.mjs +6 -6
- package/tests/pruned_lockfile_integrity.mjs +2 -2
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.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.292",
|
|
6
|
+
"core_contract": "1.19.292",
|
|
7
|
+
"source_commit": "57bc876c876c64bf07069a3646e253006a917fc6",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-
|
|
9
|
+
"built_at": "2026-08-22T15:49:03.336Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 320,
|
|
13
13
|
"agent_docs_stubbed": 22,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1391,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1733,
|
|
20
|
+
"tree_sha256": "ca47008669375b8c24cbecd5f6142a05b189fce9c02e8c3d2bdc29a2da237f87",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -2434,6 +2434,11 @@
|
|
|
2434
2434
|
"mode": "0000644",
|
|
2435
2435
|
"sha256": "9939ee2c641caa8ae4f5055c60e3208a6ed4e379b1bfd1aaa67247a8ed4a4fd8"
|
|
2436
2436
|
},
|
|
2437
|
+
{
|
|
2438
|
+
"path": "migrations/core_223_blocker_resolve_respects_other_open_blockers.sql",
|
|
2439
|
+
"mode": "0000644",
|
|
2440
|
+
"sha256": "8fc38c0353811699e19d861321d4fca8fe8fd5cca1f66ff96312d7f44fb0290e"
|
|
2441
|
+
},
|
|
2437
2442
|
{
|
|
2438
2443
|
"path": "modules/autonomy/CLAUDE.md",
|
|
2439
2444
|
"mode": "0000644",
|
|
@@ -2797,7 +2802,7 @@
|
|
|
2797
2802
|
{
|
|
2798
2803
|
"path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
|
|
2799
2804
|
"mode": "0000644",
|
|
2800
|
-
"sha256": "
|
|
2805
|
+
"sha256": "7faac7fc48e714c76c2b19feaaf9c730b2c9b10ab71ddc5697c21497d02f10ef"
|
|
2801
2806
|
},
|
|
2802
2807
|
{
|
|
2803
2808
|
"path": "modules/dev-box/app/test/unit.mjs",
|
|
@@ -4522,12 +4527,12 @@
|
|
|
4522
4527
|
{
|
|
4523
4528
|
"path": "package-lock.json",
|
|
4524
4529
|
"mode": "0000644",
|
|
4525
|
-
"sha256": "
|
|
4530
|
+
"sha256": "417cc856bf6eca0ed8c214b12624c6bc7567bb96b07a5999ebf5b72ab6b4ca05"
|
|
4526
4531
|
},
|
|
4527
4532
|
{
|
|
4528
4533
|
"path": "package.json",
|
|
4529
4534
|
"mode": "0000644",
|
|
4530
|
-
"sha256": "
|
|
4535
|
+
"sha256": "06e136123405fad423973fd79250b7baae689b122c0975e41f369ae87cdfe9e9"
|
|
4531
4536
|
},
|
|
4532
4537
|
{
|
|
4533
4538
|
"path": "public-docs/index.html",
|
|
@@ -5437,7 +5442,7 @@
|
|
|
5437
5442
|
{
|
|
5438
5443
|
"path": "scripts/gds/package-core.js",
|
|
5439
5444
|
"mode": "0000644",
|
|
5440
|
-
"sha256": "
|
|
5445
|
+
"sha256": "2fe4490f873e02e2ade253ff8e63ec70c555ea08b01ab27856deee8af2eb93dc"
|
|
5441
5446
|
},
|
|
5442
5447
|
{
|
|
5443
5448
|
"path": "scripts/gds/paste-token.js",
|
|
@@ -6172,7 +6177,7 @@
|
|
|
6172
6177
|
{
|
|
6173
6178
|
"path": "src/module-api.js",
|
|
6174
6179
|
"mode": "0000644",
|
|
6175
|
-
"sha256": "
|
|
6180
|
+
"sha256": "a00525dccc2576669f8793418e40f7232dd2f8662163900142b561ce3c93a2db"
|
|
6176
6181
|
},
|
|
6177
6182
|
{
|
|
6178
6183
|
"path": "src/module-loader/catalog.js",
|
|
@@ -6449,6 +6454,11 @@
|
|
|
6449
6454
|
"mode": "0000644",
|
|
6450
6455
|
"sha256": "0ade9bbcb1782ffa2dd01799002dee7d88f6b17dab8ee418fe58e1a06ffb925e"
|
|
6451
6456
|
},
|
|
6457
|
+
{
|
|
6458
|
+
"path": "tests/blocker_resolve_guard.mjs",
|
|
6459
|
+
"mode": "0000644",
|
|
6460
|
+
"sha256": "3a85b9e9bee4258aa64d26ca025d52c605f5f33938d3fdd5a31c393f1ec3f249"
|
|
6461
|
+
},
|
|
6452
6462
|
{
|
|
6453
6463
|
"path": "tests/bongos_box_connect.mjs",
|
|
6454
6464
|
"mode": "0000644",
|
|
@@ -6792,7 +6802,7 @@
|
|
|
6792
6802
|
{
|
|
6793
6803
|
"path": "tests/cutover_codemod.mjs",
|
|
6794
6804
|
"mode": "0000644",
|
|
6795
|
-
"sha256": "
|
|
6805
|
+
"sha256": "827dbb19b8be01a5658b3079620ab977af211a7aaf5b7f82a488bb9715d720fb"
|
|
6796
6806
|
},
|
|
6797
6807
|
{
|
|
6798
6808
|
"path": "tests/dead_deps.mjs",
|
|
@@ -7872,7 +7882,7 @@
|
|
|
7872
7882
|
{
|
|
7873
7883
|
"path": "tests/package_core.mjs",
|
|
7874
7884
|
"mode": "0000644",
|
|
7875
|
-
"sha256": "
|
|
7885
|
+
"sha256": "9bdebe777e63bb2269e528a7ee19194a73610ab9226f59a18fec6fbe18c7a98e"
|
|
7876
7886
|
},
|
|
7877
7887
|
{
|
|
7878
7888
|
"path": "tests/parallel-load.mjs",
|
|
@@ -8027,7 +8037,7 @@
|
|
|
8027
8037
|
{
|
|
8028
8038
|
"path": "tests/pruned_lockfile_integrity.mjs",
|
|
8029
8039
|
"mode": "0000644",
|
|
8030
|
-
"sha256": "
|
|
8040
|
+
"sha256": "7c24744feee780786c82ac2ed76779e22ccde35e0c4c7ab5894ca4c4bce4bd90"
|
|
8031
8041
|
},
|
|
8032
8042
|
{
|
|
8033
8043
|
"path": "tests/public_landing_projects.mjs",
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
-- core_223_blocker_resolve_respects_other_open_blockers.sql — resolving ONE of a
|
|
2
|
+
-- task's blockers must not release it while others are still open (task 1003149).
|
|
3
|
+
--
|
|
4
|
+
-- Bug: 009's resolve trigger promotes on ANY resolve, with no check for the
|
|
5
|
+
-- task's OTHER blockers:
|
|
6
|
+
--
|
|
7
|
+
-- UPDATE tasks SET status = 'ready'
|
|
8
|
+
-- WHERE id IN (SELECT task_id FROM task_blockers WHERE blocker_id = NEW.id)
|
|
9
|
+
-- AND status = 'blocked';
|
|
10
|
+
--
|
|
11
|
+
-- So a task blocked by N blockers becomes claimable the moment the FIRST one
|
|
12
|
+
-- resolves, with N-1 owner decisions still outstanding.
|
|
13
|
+
--
|
|
14
|
+
-- It is the ASYMMETRY that makes this a bug rather than a policy. Migration 176
|
|
15
|
+
-- (V4.R15 / task 953) fixed exactly this hazard on the DEPENDENCY promotion path
|
|
16
|
+
-- by folding a "no open blocker" check into task_is_fully_unblocked, and its own
|
|
17
|
+
-- header defers to this trigger — "009's blocker-resolve trigger is what should
|
|
18
|
+
-- release it, not a dep ship". But 009 releases on the first resolve, not the
|
|
19
|
+
-- last. One path fail-closes; the other did not. This aligns them.
|
|
20
|
+
--
|
|
21
|
+
-- Live at the time of writing: task 1000594 was blocked by BOTH blocker 1000065
|
|
22
|
+
-- and blocker 1000093 — duplicates covering the same two owner decisions — so
|
|
23
|
+
-- closing either as a duplicate would have released the task with the decision
|
|
24
|
+
-- still open. That is what made a routine duplicate-close unsafe, and there is
|
|
25
|
+
-- no unlink route to detach one first.
|
|
26
|
+
--
|
|
27
|
+
-- `b.id <> NEW.id` IS LOAD-BEARING, NOT COSMETIC. This is a BEFORE UPDATE
|
|
28
|
+
-- trigger, so the blockers row for NEW.id still reads status = 'open' in the
|
|
29
|
+
-- table while the function body runs. Without the exclusion the guard would see
|
|
30
|
+
-- the blocker being resolved as still open and NOTHING would ever promote —
|
|
31
|
+
-- turning an over-eager release into a permanent stall. For the same reason this
|
|
32
|
+
-- cannot simply call task_is_fully_unblocked(): that predicate reads the live
|
|
33
|
+
-- table and would always be false here.
|
|
34
|
+
--
|
|
35
|
+
-- Deliberately NOT also checking dependencies. A task left in 'blocked' because
|
|
36
|
+
-- its deps are unshipped would be STRANDED: the dep-promotion triggers (163)
|
|
37
|
+
-- only ever flip 'backlog' -> 'ready', never 'blocked' -> 'ready', so no path
|
|
38
|
+
-- would release it. Blocker-clearing and dep-clearing stay separate concerns.
|
|
39
|
+
--
|
|
40
|
+
-- Legacy markdown-anchor links (task_blockers.blocker_ref with no blocker_id
|
|
41
|
+
-- row) are not tracked here and behave exactly as before — same scope as 176.
|
|
42
|
+
--
|
|
43
|
+
-- Idempotent: pure CREATE OR REPLACE of the function. The trigger created in 009
|
|
44
|
+
-- resolves the function by name, so it needs no redefinition.
|
|
45
|
+
|
|
46
|
+
BEGIN;
|
|
47
|
+
|
|
48
|
+
CREATE OR REPLACE FUNCTION trg_promote_tasks_on_blocker_resolve()
|
|
49
|
+
RETURNS TRIGGER AS $$
|
|
50
|
+
BEGIN
|
|
51
|
+
IF NEW.status = 'resolved' AND (OLD.status IS NULL OR OLD.status <> 'resolved') THEN
|
|
52
|
+
IF NEW.resolved_at IS NULL THEN
|
|
53
|
+
NEW.resolved_at := now();
|
|
54
|
+
END IF;
|
|
55
|
+
|
|
56
|
+
UPDATE tasks
|
|
57
|
+
SET status = 'ready', updated_at = now()
|
|
58
|
+
WHERE id IN (SELECT task_id FROM task_blockers WHERE blocker_id = NEW.id)
|
|
59
|
+
AND status = 'blocked'
|
|
60
|
+
-- … and no OTHER blocker on this task is still open (task 1003149).
|
|
61
|
+
AND NOT EXISTS (
|
|
62
|
+
SELECT 1
|
|
63
|
+
FROM task_blockers tb
|
|
64
|
+
JOIN blockers b ON b.id = tb.blocker_id
|
|
65
|
+
WHERE tb.task_id = tasks.id
|
|
66
|
+
AND b.id <> NEW.id
|
|
67
|
+
AND b.status = 'open'
|
|
68
|
+
);
|
|
69
|
+
END IF;
|
|
70
|
+
RETURN NEW;
|
|
71
|
+
END;
|
|
72
|
+
$$ LANGUAGE plpgsql;
|
|
73
|
+
|
|
74
|
+
COMMIT;
|
|
@@ -278,6 +278,8 @@ function createClient(opts = {}) {
|
|
|
278
278
|
postDiscordChannelsReconcile: (args) => request("POST", "/discord/channels/reconcile", { hasBody: true }, args),
|
|
279
279
|
},
|
|
280
280
|
"doneWhen": {
|
|
281
|
+
// PATCH /done-when/{criterionId} — rank: metic+archon — PATCH /done-when/:criterionId
|
|
282
|
+
patchDoneWhenCriterionId: (args) => request("PATCH", "/done-when/{criterionId}", { hasBody: true }, args),
|
|
281
283
|
// POST /done-when/{criterionId}/satisfy — rank: metic+archon — POST /done-when/:criterionId/satisfy
|
|
282
284
|
postDoneWhenCriterionIdSatisfy: (args) => request("POST", "/done-when/{criterionId}/satisfy", { hasBody: true }, args),
|
|
283
285
|
// POST /done-when/{criterionId}/unsatisfy — rank: metic+archon — POST /done-when/:criterionId/unsatisfy
|
|
@@ -308,6 +310,8 @@ function createClient(opts = {}) {
|
|
|
308
310
|
postGoals: (args) => request("POST", "/goals", { hasBody: true }, args),
|
|
309
311
|
// GET /goals/{id} — rank: any-builder — GET /goals/:id
|
|
310
312
|
getGoalsId: (args) => request("GET", "/goals/{id}", { hasBody: false }, args),
|
|
313
|
+
// PATCH /goals/{id} — rank: any-builder — PATCH /goals/:id
|
|
314
|
+
patchGoalsId: (args) => request("PATCH", "/goals/{id}", { hasBody: true }, args),
|
|
311
315
|
// POST /goals/{id}/archive — rank: metic+archon — POST /goals/:id/archive
|
|
312
316
|
postGoalsIdArchive: (args) => request("POST", "/goals/{id}/archive", { hasBody: true }, args),
|
|
313
317
|
// GET /goals/{id}/conflicts — rank: any-builder — GET /goals/:id/conflicts
|
|
@@ -350,6 +354,8 @@ function createClient(opts = {}) {
|
|
|
350
354
|
getGoalsInbox: (args) => request("GET", "/goals/inbox", { hasBody: false }, args),
|
|
351
355
|
// POST /goals/reorder — rank: metic+archon — POST /goals/reorder
|
|
352
356
|
postGoalsReorder: (args) => request("POST", "/goals/reorder", { hasBody: true }, args),
|
|
357
|
+
// GET /goals/suggestions — rank: any-builder — GET /goals/suggestions
|
|
358
|
+
getGoalsSuggestions: (args) => request("GET", "/goals/suggestions", { hasBody: false }, args),
|
|
353
359
|
},
|
|
354
360
|
"government": {
|
|
355
361
|
// GET /government/assignments — rank: archon — GET /government/assignments
|
|
@@ -396,6 +402,8 @@ function createClient(opts = {}) {
|
|
|
396
402
|
getInboxId: (args) => request("GET", "/inbox/{id}", { hasBody: false }, args),
|
|
397
403
|
// PATCH /inbox/{id} — rank: metic+archon — PATCH /inbox/:id
|
|
398
404
|
patchInboxId: (args) => request("PATCH", "/inbox/{id}", { hasBody: true }, args),
|
|
405
|
+
// PATCH /inbox/{id}/body — rank: any-builder — PATCH /inbox/:id/body
|
|
406
|
+
patchInboxIdBody: (args) => request("PATCH", "/inbox/{id}/body", { hasBody: true }, args),
|
|
399
407
|
// POST /inbox/{id}/ratify — rank: metic+archon — POST /inbox/:id/ratify
|
|
400
408
|
postInboxIdRatify: (args) => request("POST", "/inbox/{id}/ratify", { hasBody: true }, args),
|
|
401
409
|
// POST /inbox/{id}/resubmit — rank: any-builder — POST /inbox/:id/resubmit
|
package/package-lock.json
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.292",
|
|
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.292",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
|
13
13
|
"pg": "^8.20.0"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
+
"node_modules/@pinojs/redact": {
|
|
17
|
+
"version": "0.4.0",
|
|
18
|
+
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
|
|
19
|
+
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
|
|
20
|
+
"license": "MIT"
|
|
21
|
+
},
|
|
16
22
|
"node_modules/accepts": {
|
|
17
23
|
"version": "1.3.8",
|
|
18
24
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
|
@@ -32,6 +38,15 @@
|
|
|
32
38
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
|
33
39
|
"license": "MIT"
|
|
34
40
|
},
|
|
41
|
+
"node_modules/atomic-sleep": {
|
|
42
|
+
"version": "1.0.0",
|
|
43
|
+
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
|
44
|
+
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=8.0.0"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
35
50
|
"node_modules/body-parser": {
|
|
36
51
|
"version": "1.20.5",
|
|
37
52
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
|
|
@@ -539,6 +554,15 @@
|
|
|
539
554
|
"url": "https://github.com/sponsors/ljharb"
|
|
540
555
|
}
|
|
541
556
|
},
|
|
557
|
+
"node_modules/on-exit-leak-free": {
|
|
558
|
+
"version": "2.1.2",
|
|
559
|
+
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
|
560
|
+
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
|
|
561
|
+
"license": "MIT",
|
|
562
|
+
"engines": {
|
|
563
|
+
"node": ">=14.0.0"
|
|
564
|
+
}
|
|
565
|
+
},
|
|
542
566
|
"node_modules/on-finished": {
|
|
543
567
|
"version": "2.4.1",
|
|
544
568
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
|
@@ -655,6 +679,43 @@
|
|
|
655
679
|
"split2": "^4.1.0"
|
|
656
680
|
}
|
|
657
681
|
},
|
|
682
|
+
"node_modules/pino": {
|
|
683
|
+
"version": "9.14.0",
|
|
684
|
+
"resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz",
|
|
685
|
+
"integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==",
|
|
686
|
+
"license": "MIT",
|
|
687
|
+
"dependencies": {
|
|
688
|
+
"@pinojs/redact": "^0.4.0",
|
|
689
|
+
"atomic-sleep": "^1.0.0",
|
|
690
|
+
"on-exit-leak-free": "^2.1.0",
|
|
691
|
+
"pino-abstract-transport": "^2.0.0",
|
|
692
|
+
"pino-std-serializers": "^7.0.0",
|
|
693
|
+
"process-warning": "^5.0.0",
|
|
694
|
+
"quick-format-unescaped": "^4.0.3",
|
|
695
|
+
"real-require": "^0.2.0",
|
|
696
|
+
"safe-stable-stringify": "^2.3.1",
|
|
697
|
+
"sonic-boom": "^4.0.1",
|
|
698
|
+
"thread-stream": "^3.0.0"
|
|
699
|
+
},
|
|
700
|
+
"bin": {
|
|
701
|
+
"pino": "bin.js"
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
"node_modules/pino-abstract-transport": {
|
|
705
|
+
"version": "2.0.0",
|
|
706
|
+
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
|
|
707
|
+
"integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
|
|
708
|
+
"license": "MIT",
|
|
709
|
+
"dependencies": {
|
|
710
|
+
"split2": "^4.0.0"
|
|
711
|
+
}
|
|
712
|
+
},
|
|
713
|
+
"node_modules/pino-std-serializers": {
|
|
714
|
+
"version": "7.1.0",
|
|
715
|
+
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
|
|
716
|
+
"integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
|
|
717
|
+
"license": "MIT"
|
|
718
|
+
},
|
|
658
719
|
"node_modules/postgres-array": {
|
|
659
720
|
"version": "2.0.0",
|
|
660
721
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
|
@@ -694,6 +755,22 @@
|
|
|
694
755
|
"node": ">=0.10.0"
|
|
695
756
|
}
|
|
696
757
|
},
|
|
758
|
+
"node_modules/process-warning": {
|
|
759
|
+
"version": "5.1.0",
|
|
760
|
+
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz",
|
|
761
|
+
"integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==",
|
|
762
|
+
"funding": [
|
|
763
|
+
{
|
|
764
|
+
"type": "github",
|
|
765
|
+
"url": "https://github.com/sponsors/fastify"
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
"type": "opencollective",
|
|
769
|
+
"url": "https://opencollective.com/fastify"
|
|
770
|
+
}
|
|
771
|
+
],
|
|
772
|
+
"license": "MIT"
|
|
773
|
+
},
|
|
697
774
|
"node_modules/proxy-addr": {
|
|
698
775
|
"version": "2.0.7",
|
|
699
776
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
|
@@ -722,6 +799,12 @@
|
|
|
722
799
|
"url": "https://github.com/sponsors/ljharb"
|
|
723
800
|
}
|
|
724
801
|
},
|
|
802
|
+
"node_modules/quick-format-unescaped": {
|
|
803
|
+
"version": "4.0.4",
|
|
804
|
+
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
|
805
|
+
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
|
806
|
+
"license": "MIT"
|
|
807
|
+
},
|
|
725
808
|
"node_modules/range-parser": {
|
|
726
809
|
"version": "1.2.1",
|
|
727
810
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
|
@@ -746,6 +829,15 @@
|
|
|
746
829
|
"node": ">= 0.8"
|
|
747
830
|
}
|
|
748
831
|
},
|
|
832
|
+
"node_modules/real-require": {
|
|
833
|
+
"version": "0.2.0",
|
|
834
|
+
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
|
835
|
+
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
|
|
836
|
+
"license": "MIT",
|
|
837
|
+
"engines": {
|
|
838
|
+
"node": ">= 12.13.0"
|
|
839
|
+
}
|
|
840
|
+
},
|
|
749
841
|
"node_modules/safe-buffer": {
|
|
750
842
|
"version": "5.2.1",
|
|
751
843
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
|
@@ -766,6 +858,15 @@
|
|
|
766
858
|
],
|
|
767
859
|
"license": "MIT"
|
|
768
860
|
},
|
|
861
|
+
"node_modules/safe-stable-stringify": {
|
|
862
|
+
"version": "2.5.0",
|
|
863
|
+
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
|
864
|
+
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
|
865
|
+
"license": "MIT",
|
|
866
|
+
"engines": {
|
|
867
|
+
"node": ">=10"
|
|
868
|
+
}
|
|
869
|
+
},
|
|
769
870
|
"node_modules/safer-buffer": {
|
|
770
871
|
"version": "2.1.2",
|
|
771
872
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
@@ -895,6 +996,15 @@
|
|
|
895
996
|
"url": "https://github.com/sponsors/ljharb"
|
|
896
997
|
}
|
|
897
998
|
},
|
|
999
|
+
"node_modules/sonic-boom": {
|
|
1000
|
+
"version": "4.2.1",
|
|
1001
|
+
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
|
|
1002
|
+
"integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
|
|
1003
|
+
"license": "MIT",
|
|
1004
|
+
"dependencies": {
|
|
1005
|
+
"atomic-sleep": "^1.0.0"
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
898
1008
|
"node_modules/split2": {
|
|
899
1009
|
"version": "4.2.0",
|
|
900
1010
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
|
@@ -913,6 +1023,15 @@
|
|
|
913
1023
|
"node": ">= 0.8"
|
|
914
1024
|
}
|
|
915
1025
|
},
|
|
1026
|
+
"node_modules/thread-stream": {
|
|
1027
|
+
"version": "3.2.0",
|
|
1028
|
+
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz",
|
|
1029
|
+
"integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==",
|
|
1030
|
+
"license": "MIT",
|
|
1031
|
+
"dependencies": {
|
|
1032
|
+
"real-require": "^0.2.0"
|
|
1033
|
+
}
|
|
1034
|
+
},
|
|
916
1035
|
"node_modules/toidentifier": {
|
|
917
1036
|
"version": "1.0.1",
|
|
918
1037
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.292",
|
|
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",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"express": "^4.21.2",
|
|
28
|
-
"pg": "^8.20.0"
|
|
28
|
+
"pg": "^8.20.0",
|
|
29
|
+
"pino": "^9.14.0"
|
|
29
30
|
},
|
|
30
31
|
"overrides": {
|
|
31
32
|
"undici": "^6.27.0"
|
|
@@ -252,10 +252,13 @@ function isNpmStripped(pathRel) {
|
|
|
252
252
|
// git) and staged in their place. Everything else still ships through the
|
|
253
253
|
// normal isPublishable()/processCoreFile path.
|
|
254
254
|
//
|
|
255
|
-
// Core deps are express + pg (+ an undici pin) — ADR 0108 §Context.
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
|
|
255
|
+
// Core deps are express + pg + pino (+ an undici pin) — ADR 0108 §Context.
|
|
256
|
+
// pino joined the core boot path with the structured logger (task 1003130:
|
|
257
|
+
// platform-server → serve-internal → routes → middleware/audit → logger); a
|
|
258
|
+
// published core without it crashes MODULE_NOT_FOUND at startup (task 1003160).
|
|
259
|
+
// discord.js (+ its undici requirement) is an OPTIONAL MODULE dependency, not
|
|
260
|
+
// core; a consumer that enables the discord module installs it itself.
|
|
261
|
+
const CORE_DEP_NAMES = ['express', 'pg', 'pino'];
|
|
259
262
|
|
|
260
263
|
// Pure — takes the source package.json's `dependencies`/`overrides` as plain
|
|
261
264
|
// objects so this is unit-testable with no disk I/O. Throws if a required core
|
package/src/module-api.js
CHANGED
|
@@ -1007,8 +1007,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
1007
1007
|
// landed since 1.19.288 with no explicit bump. run 32549912116. (task 1002620)
|
|
1008
1008
|
// 1.19.290 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1009
1009
|
// landed since 1.19.289 with no explicit bump. run 32552538814. (task 1002620)
|
|
1010
|
+
// 1.19.291 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1011
|
+
// landed since 1.19.290 with no explicit bump. run 32580353000. (task 1002620)
|
|
1012
|
+
// 1.19.292 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1013
|
+
// landed since 1.19.291 with no explicit bump. run 32582800234. (task 1002620)
|
|
1010
1014
|
// ---------------------------------------------------------------------------
|
|
1011
|
-
const CORE_VERSION = '1.19.
|
|
1015
|
+
const CORE_VERSION = '1.19.292'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
1012
1016
|
|
|
1013
1017
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
1014
1018
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// tests/blocker_resolve_guard.mjs
|
|
2
|
+
//
|
|
3
|
+
// Task 1003149 — resolving ONE of a task's blockers must not release it while
|
|
4
|
+
// others are still open. Static shape guard for core_223, in the style of
|
|
5
|
+
// dependencies_migration.mjs: no live DB, assert the SQL TEXT so a future edit
|
|
6
|
+
// cannot silently drop the guard or "simplify" it back into the bug. The runtime
|
|
7
|
+
// behaviour is proven against real Postgres at deploy; this pins the contract.
|
|
8
|
+
//
|
|
9
|
+
// Run: node tests/blocker_resolve_guard.mjs
|
|
10
|
+
|
|
11
|
+
import { strict as assert } from 'node:assert';
|
|
12
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
16
|
+
import { makeRunner } from './helpers.mjs';
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const repoRoot = join(__dirname, '..');
|
|
20
|
+
const read = (...p) => readFileSync(join(repoRoot, ...p), 'utf8');
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
|
|
23
|
+
const MIG = 'core_223_blocker_resolve_respects_other_open_blockers.sql';
|
|
24
|
+
const mig = read('migrations', MIG);
|
|
25
|
+
const migN = mig.replace(/\s+/g, ' ');
|
|
26
|
+
// Comments carry the load-bearing rationale; strip them when asserting on the
|
|
27
|
+
// executable body so a claim in prose can never stand in for the real SQL.
|
|
28
|
+
const body = mig.replace(/^\s*--.*$/gm, '').replace(/\s+/g, ' ');
|
|
29
|
+
|
|
30
|
+
const { test, summary } = makeRunner();
|
|
31
|
+
|
|
32
|
+
await test('223 replaces the resolve trigger function (and only replaces it)', () => {
|
|
33
|
+
assert.match(mig, /CREATE OR REPLACE FUNCTION trg_promote_tasks_on_blocker_resolve\(\)/);
|
|
34
|
+
// The trigger from 009 binds by name, so redefining it here would be noise —
|
|
35
|
+
// and a DROP TRIGGER would leave a window where resolves promote nothing.
|
|
36
|
+
assert.doesNotMatch(body, /DROP TRIGGER/i);
|
|
37
|
+
assert.doesNotMatch(body, /CREATE TRIGGER/i);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await test('the promote UPDATE is guarded by a NOT EXISTS over the task\'s OTHER open blockers', () => {
|
|
41
|
+
assert.match(body, /UPDATE tasks SET status = 'ready'/);
|
|
42
|
+
assert.match(body, /AND status = 'blocked'/);
|
|
43
|
+
assert.match(body, /AND NOT EXISTS \( SELECT 1 FROM task_blockers tb JOIN blockers b ON b\.id = tb\.blocker_id/);
|
|
44
|
+
assert.match(body, /WHERE tb\.task_id = tasks\.id/);
|
|
45
|
+
assert.match(body, /AND b\.status = 'open'/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await test("`b.id <> NEW.id` is present — without it a BEFORE trigger stalls forever", () => {
|
|
49
|
+
// The blockers row for NEW.id still reads 'open' while a BEFORE UPDATE trigger
|
|
50
|
+
// body runs, so dropping this exclusion would mean NOTHING ever promotes —
|
|
51
|
+
// trading an over-eager release for a permanent stall. This is the single
|
|
52
|
+
// most important character sequence in the migration.
|
|
53
|
+
assert.match(body, /AND b\.id <> NEW\.id/);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await test('223 does NOT also gate on dependencies — that would strand blocked tasks', () => {
|
|
57
|
+
// The dep-promotion triggers (163) only flip backlog -> ready, never
|
|
58
|
+
// blocked -> ready, so a task held in 'blocked' for unshipped deps would have
|
|
59
|
+
// no release path at all.
|
|
60
|
+
assert.doesNotMatch(body, /task_is_fully_unblocked/);
|
|
61
|
+
assert.doesNotMatch(body, /FROM dependencies/);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await test('the resolved_at capture from 009 is preserved', () => {
|
|
65
|
+
assert.match(body, /IF NEW\.resolved_at IS NULL THEN NEW\.resolved_at := now\(\);/);
|
|
66
|
+
assert.match(body, /IF NEW\.status = 'resolved' AND \(OLD\.status IS NULL OR OLD\.status <> 'resolved'\) THEN/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await test('223 is transactional and idempotent (pure CREATE OR REPLACE)', () => {
|
|
70
|
+
assert.match(migN, /BEGIN;.*COMMIT;/);
|
|
71
|
+
assert.doesNotMatch(body, /ALTER TABLE/i);
|
|
72
|
+
assert.doesNotMatch(body, /DROP FUNCTION/i);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await test('009 remains the only OTHER definition — 223 is the last word', () => {
|
|
76
|
+
const defs = readdirSync(join(repoRoot, 'migrations'))
|
|
77
|
+
.filter((f) => f.endsWith('.sql'))
|
|
78
|
+
.filter((f) => /FUNCTION trg_promote_tasks_on_blocker_resolve/.test(read('migrations', f)))
|
|
79
|
+
.sort();
|
|
80
|
+
assert.deepEqual(defs, ['009_pms_v2_blockers_table.sql', MIG]);
|
|
81
|
+
// Ordering matters: migrate.sh applies in filename order, so a lower-numbered
|
|
82
|
+
// redefinition landing later would silently reinstate the bug.
|
|
83
|
+
assert.ok(MIG > '009_pms_v2_blockers_table.sql', 'core_223 must sort after 009');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await test('the JS resolve path still only OBSERVES — promotion stays in the trigger', () => {
|
|
87
|
+
const src = read('modules', 'ideas', 'blockers.js');
|
|
88
|
+
// resolveBlocker flips the blocker and then reads back which tasks are ready;
|
|
89
|
+
// if it ever started UPDATE-ing tasks.status itself, this guard would be
|
|
90
|
+
// bypassable and the fix would need to live in two places.
|
|
91
|
+
assert.doesNotMatch(src, /UPDATE\s+tasks[\s\S]{0,200}?status\s*=\s*'ready'/);
|
|
92
|
+
const blockers = require('../modules/ideas/blockers.js');
|
|
93
|
+
assert.equal(typeof blockers.resolveBlocker, 'function');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
summary('blocker_resolve_guard');
|
|
@@ -174,7 +174,7 @@ t('planCodemod rewrites host files, skips core files, reports unmapped', () => {
|
|
|
174
174
|
|
|
175
175
|
t('every CORE_INTERNAL_MAP target is exported by the core package (a rewrite never outruns an export)', () => {
|
|
176
176
|
const { buildPackageJson } = require('../scripts/gds/package-core.js');
|
|
177
|
-
const pkg = buildPackageJson({ version: '0.0.0-test', sourceDeps: { express: '^4.0.0', pg: '^8.0.0' } });
|
|
177
|
+
const pkg = buildPackageJson({ version: '0.0.0-test', sourceDeps: { express: '^4.0.0', pg: '^8.0.0', pino: '^9.0.0' } });
|
|
178
178
|
for (const mapped of Object.values(CORE_INTERNAL_MAP)) {
|
|
179
179
|
assert.ok(pkg.exports[`./${mapped}`], `@bongos/core must export ./${mapped} (targeted by the codemod)`);
|
|
180
180
|
}
|
package/tests/package_core.mjs
CHANGED
|
@@ -360,15 +360,15 @@ t('TAR_ROOT is the npm-required "package/" (not the ARTIFACT_NAME dir)', () => {
|
|
|
360
360
|
assert.equal(TAR_ROOT, 'package/');
|
|
361
361
|
});
|
|
362
362
|
|
|
363
|
-
t('buildPackageJson: prunes to express+pg, keeps the source ranges + the undici override', () => {
|
|
363
|
+
t('buildPackageJson: prunes to express+pg+pino, keeps the source ranges + the undici override', () => {
|
|
364
364
|
const pkg = buildPackageJson({
|
|
365
365
|
version: '1.14.0',
|
|
366
|
-
sourceDeps: { express: '^4.21.2', pg: '^8.20.0', phaser: '^3.87.0', 'discord.js': '^14.26.4' },
|
|
366
|
+
sourceDeps: { express: '^4.21.2', pg: '^8.20.0', pino: '^9.14.0', phaser: '^3.87.0', 'discord.js': '^14.26.4' },
|
|
367
367
|
sourceOverrides: { undici: '^6.27.0' },
|
|
368
368
|
});
|
|
369
369
|
assert.equal(pkg.name, '@bongos/core');
|
|
370
370
|
assert.equal(pkg.version, '1.14.0');
|
|
371
|
-
assert.deepEqual(pkg.dependencies, { express: '^4.21.2', pg: '^8.20.0' });
|
|
371
|
+
assert.deepEqual(pkg.dependencies, { express: '^4.21.2', pg: '^8.20.0', pino: '^9.14.0' });
|
|
372
372
|
assert.equal(pkg.dependencies.phaser, undefined);
|
|
373
373
|
assert.equal(pkg.dependencies['discord.js'], undefined);
|
|
374
374
|
assert.deepEqual(pkg.overrides, { undici: '^6.27.0' });
|
|
@@ -400,7 +400,7 @@ t('publishArgs: private (restricted) publish, dry-run unless live (task 2089 / A
|
|
|
400
400
|
});
|
|
401
401
|
|
|
402
402
|
t('buildPackageJson: omits `overrides` when the source has none', () => {
|
|
403
|
-
const pkg = buildPackageJson({ version: '1.0.0', sourceDeps: { express: '^4.0.0', pg: '^8.0.0' } });
|
|
403
|
+
const pkg = buildPackageJson({ version: '1.0.0', sourceDeps: { express: '^4.0.0', pg: '^8.0.0', pino: '^9.0.0' } });
|
|
404
404
|
assert.equal(pkg.overrides, undefined);
|
|
405
405
|
});
|
|
406
406
|
|
|
@@ -408,8 +408,8 @@ t('buildPackageJson: throws when a required core dep is missing from the source'
|
|
|
408
408
|
assert.throws(() => buildPackageJson({ version: '1.0.0', sourceDeps: { express: '^4.0.0' } }), /missing required core dependency "pg"/);
|
|
409
409
|
});
|
|
410
410
|
|
|
411
|
-
t('CORE_DEP_NAMES is exactly [express, pg]', () => {
|
|
412
|
-
assert.deepEqual(CORE_DEP_NAMES, ['express', 'pg']);
|
|
411
|
+
t('CORE_DEP_NAMES is exactly [express, pg, pino]', () => {
|
|
412
|
+
assert.deepEqual(CORE_DEP_NAMES, ['express', 'pg', 'pino']);
|
|
413
413
|
});
|
|
414
414
|
|
|
415
415
|
// A tiny synthetic lockfile fixture with a THREE-level chain (root -> a -> b) and
|
|
@@ -71,8 +71,8 @@ t('pruned lockfile: integrity hashes match the SOURCE lockfile verbatim (not rew
|
|
|
71
71
|
assert.equal(mismatched.length, 0, `integrity rewritten for: ${mismatched.slice(0, 10).join(', ')}`);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
t('CORE_DEP_NAMES: the pin domain is exactly the runtime core deps (express + pg)', () => {
|
|
75
|
-
assert.ok(Array.isArray(CORE_DEP_NAMES) && CORE_DEP_NAMES.includes('express') && CORE_DEP_NAMES.includes('pg'));
|
|
74
|
+
t('CORE_DEP_NAMES: the pin domain is exactly the runtime core deps (express + pg + pino)', () => {
|
|
75
|
+
assert.ok(Array.isArray(CORE_DEP_NAMES) && CORE_DEP_NAMES.includes('express') && CORE_DEP_NAMES.includes('pg') && CORE_DEP_NAMES.includes('pino'));
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
console.log(`\npruned_lockfile_integrity: ${passed} passed, ${failed} failed`);
|