@bongos/core 1.19.842 → 1.19.844
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 +70 -40
- package/docs/module-api-changelog.md +4 -0
- package/migrations/core_247_discipline_required.sql +58 -0
- package/modules/agents/lib/spend-ceiling.js +126 -37
- package/modules/agents/migrations/agents_004_spend_cap.sql +53 -0
- package/modules/agents/pollers/event-router.js +29 -2
- package/modules/agents/routes/agents.js +38 -1
- package/modules/agents/spawn.js +73 -1
- package/modules/copy-desk/routes/copy-desk.js +6 -0
- package/modules/economy/cost-classify.js +11 -0
- package/modules/economy/cost.js +21 -3
- package/modules/economy/reward.js +7 -0
- package/modules/hall-ui/public/goals.states.json +2 -2
- package/modules/hall-ui/public/idea.states.json +1 -1
- package/modules/hall-ui/public/ideas.states.json +1 -1
- package/modules/hall-ui/public/index.states.json +1 -1
- package/modules/hall-ui/public/pair.states.json +1 -1
- package/modules/hall-ui/public/settings.states.json +1 -1
- package/modules/hall-ui/public/task.states.json +1 -1
- package/modules/hall-ui/public/work.states.json +1 -1
- package/modules/ideas/inbox.js +4 -2
- package/modules/lifecycle/db-tasks.js +28 -2
- package/modules/lifecycle/lifecycle.js +6 -0
- package/modules/lifecycle/routes/goals.js +17 -4
- package/modules/lifecycle/routes/task-write-routes.js +25 -6
- package/modules/lifecycle/task-classifier.js +39 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/backfill-disciplines.js +144 -0
- package/scripts/gds/fitness-ratchets.js +59 -2
- package/scripts/gds/init.js +6 -1
- package/scripts/gds/triage.js +7 -0
- package/src/module-api.js +1 -1
- package/tests/agents_spend_guard.mjs +203 -2
- package/tests/hall_states_regex_escapes.mjs +98 -0
- package/tests/knip_ratchet_ambient.mjs +101 -0
- package/tests/rank_floor_wiring.mjs +4 -0
- package/tests/reward_gate.mjs +4 -4
- package/tests/task_discipline_required.mjs +163 -0
- package/tests/task_goal_required.mjs +4 -4
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.844",
|
|
6
|
+
"core_contract": "1.19.844",
|
|
7
|
+
"source_commit": "980c92b65de8a99f2772b86fb7f69e365eed4dc6",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-17T18:
|
|
9
|
+
"built_at": "2026-09-17T18:59:26.454Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 495,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2272,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2793,
|
|
20
|
+
"tree_sha256": "af2219bd2cdea4b5ebb159bd2cc60e9d015182c15121196f16e1cbb9b4ac7bf5",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2577,7 +2577,7 @@
|
|
|
2577
2577
|
{
|
|
2578
2578
|
"path": "docs/module-api-changelog.md",
|
|
2579
2579
|
"mode": "0000644",
|
|
2580
|
-
"sha256": "
|
|
2580
|
+
"sha256": "185ae1211c5c546bf5d0a57acd5c97ad56b4e98133ba2aceded36bf2cc95c4ca"
|
|
2581
2581
|
},
|
|
2582
2582
|
{
|
|
2583
2583
|
"path": "docs/modules-contract.md",
|
|
@@ -3679,6 +3679,11 @@
|
|
|
3679
3679
|
"mode": "0000644",
|
|
3680
3680
|
"sha256": "0481e0b8205c707ed002f404c801d3f2059b2b0e0fcfa9bc436968f6665a296f"
|
|
3681
3681
|
},
|
|
3682
|
+
{
|
|
3683
|
+
"path": "migrations/core_247_discipline_required.sql",
|
|
3684
|
+
"mode": "0000644",
|
|
3685
|
+
"sha256": "e8d3e3f764b61ea559df7f997758e639f8778ee24da4293908e9d45ca30a425b"
|
|
3686
|
+
},
|
|
3682
3687
|
{
|
|
3683
3688
|
"path": "modules/agents/CLAUDE.md",
|
|
3684
3689
|
"mode": "0000644",
|
|
@@ -3707,7 +3712,7 @@
|
|
|
3707
3712
|
{
|
|
3708
3713
|
"path": "modules/agents/lib/spend-ceiling.js",
|
|
3709
3714
|
"mode": "0000644",
|
|
3710
|
-
"sha256": "
|
|
3715
|
+
"sha256": "1144ee9be9cb3df57e876a868529ddbf78864fac1fdabc90ed2e010bd8d376a8"
|
|
3711
3716
|
},
|
|
3712
3717
|
{
|
|
3713
3718
|
"path": "modules/agents/lib/validate.js",
|
|
@@ -3729,6 +3734,11 @@
|
|
|
3729
3734
|
"mode": "0000644",
|
|
3730
3735
|
"sha256": "7a9763f90da032c81aa0b1a8461a2e13b58d9f529c1b857b048648feeadc6fdd"
|
|
3731
3736
|
},
|
|
3737
|
+
{
|
|
3738
|
+
"path": "modules/agents/migrations/agents_004_spend_cap.sql",
|
|
3739
|
+
"mode": "0000644",
|
|
3740
|
+
"sha256": "31a5ac35536cecd264bc989907f4cf094e050db0b8de85e7e3e7831cf7c25a9d"
|
|
3741
|
+
},
|
|
3732
3742
|
{
|
|
3733
3743
|
"path": "modules/agents/module.json",
|
|
3734
3744
|
"mode": "0000644",
|
|
@@ -3737,17 +3747,17 @@
|
|
|
3737
3747
|
{
|
|
3738
3748
|
"path": "modules/agents/pollers/event-router.js",
|
|
3739
3749
|
"mode": "0000644",
|
|
3740
|
-
"sha256": "
|
|
3750
|
+
"sha256": "ddd903f3702a3dd14e714dcf352e7c69f4bfbce5206e71892142aa8a81d44cde"
|
|
3741
3751
|
},
|
|
3742
3752
|
{
|
|
3743
3753
|
"path": "modules/agents/routes/agents.js",
|
|
3744
3754
|
"mode": "0000644",
|
|
3745
|
-
"sha256": "
|
|
3755
|
+
"sha256": "a5d80d87486e8c6e928fe4ac563643ceb5255df14fd89e7e20194e2d8196b301"
|
|
3746
3756
|
},
|
|
3747
3757
|
{
|
|
3748
3758
|
"path": "modules/agents/spawn.js",
|
|
3749
3759
|
"mode": "0000644",
|
|
3750
|
-
"sha256": "
|
|
3760
|
+
"sha256": "4e5e9ac97bd443d4091d8a6e4e246e080a2fe67793a77f4c4139b19019e37005"
|
|
3751
3761
|
},
|
|
3752
3762
|
{
|
|
3753
3763
|
"path": "modules/autonomy/CLAUDE.md",
|
|
@@ -3882,7 +3892,7 @@
|
|
|
3882
3892
|
{
|
|
3883
3893
|
"path": "modules/copy-desk/routes/copy-desk.js",
|
|
3884
3894
|
"mode": "0000644",
|
|
3885
|
-
"sha256": "
|
|
3895
|
+
"sha256": "4839f077aee504947a3bada77fd9c3df882f9005f571ed2e2367b615c111b76b"
|
|
3886
3896
|
},
|
|
3887
3897
|
{
|
|
3888
3898
|
"path": "modules/copy-desk/tests/copy_flags.mjs",
|
|
@@ -4292,7 +4302,7 @@
|
|
|
4292
4302
|
{
|
|
4293
4303
|
"path": "modules/economy/cost-classify.js",
|
|
4294
4304
|
"mode": "0000644",
|
|
4295
|
-
"sha256": "
|
|
4305
|
+
"sha256": "fefd4908bd22aa3b747a7c8b4146e5a7e53731fe92612db105ea3512ae34300c"
|
|
4296
4306
|
},
|
|
4297
4307
|
{
|
|
4298
4308
|
"path": "modules/economy/cost-summary-shape.js",
|
|
@@ -4302,7 +4312,7 @@
|
|
|
4302
4312
|
{
|
|
4303
4313
|
"path": "modules/economy/cost.js",
|
|
4304
4314
|
"mode": "0000644",
|
|
4305
|
-
"sha256": "
|
|
4315
|
+
"sha256": "6cc28fe57bb50b6c3f4b7d4485838427fa5f12b5a79848f0358fdaa2874f6c1e"
|
|
4306
4316
|
},
|
|
4307
4317
|
{
|
|
4308
4318
|
"path": "modules/economy/credits.js",
|
|
@@ -4332,7 +4342,7 @@
|
|
|
4332
4342
|
{
|
|
4333
4343
|
"path": "modules/economy/reward.js",
|
|
4334
4344
|
"mode": "0000644",
|
|
4335
|
-
"sha256": "
|
|
4345
|
+
"sha256": "c4245b1b7c361eb44e0034f19227e208b956e9eb9ac03924e22b3017dd4a343a"
|
|
4336
4346
|
},
|
|
4337
4347
|
{
|
|
4338
4348
|
"path": "modules/economy/routes/cost.js",
|
|
@@ -4952,7 +4962,7 @@
|
|
|
4952
4962
|
{
|
|
4953
4963
|
"path": "modules/hall-ui/public/goals.states.json",
|
|
4954
4964
|
"mode": "0000644",
|
|
4955
|
-
"sha256": "
|
|
4965
|
+
"sha256": "14aefbb8b94ebc3df68744052f465b63ceaf3361f98a94972781ae01bc616735"
|
|
4956
4966
|
},
|
|
4957
4967
|
{
|
|
4958
4968
|
"path": "modules/hall-ui/public/government.css",
|
|
@@ -5032,7 +5042,7 @@
|
|
|
5032
5042
|
{
|
|
5033
5043
|
"path": "modules/hall-ui/public/idea.states.json",
|
|
5034
5044
|
"mode": "0000644",
|
|
5035
|
-
"sha256": "
|
|
5045
|
+
"sha256": "2299490186e19f2369069562dc8ba8dd837a2e89bfa94c7797f350cc653659c9"
|
|
5036
5046
|
},
|
|
5037
5047
|
{
|
|
5038
5048
|
"path": "modules/hall-ui/public/ideas.css",
|
|
@@ -5052,7 +5062,7 @@
|
|
|
5052
5062
|
{
|
|
5053
5063
|
"path": "modules/hall-ui/public/ideas.states.json",
|
|
5054
5064
|
"mode": "0000644",
|
|
5055
|
-
"sha256": "
|
|
5065
|
+
"sha256": "889041e8bcff6e7fa53f6bd09d0bed670e6b58cd68cda2ff7147a1a253e33e8d"
|
|
5056
5066
|
},
|
|
5057
5067
|
{
|
|
5058
5068
|
"path": "modules/hall-ui/public/index.html",
|
|
@@ -5062,7 +5072,7 @@
|
|
|
5062
5072
|
{
|
|
5063
5073
|
"path": "modules/hall-ui/public/index.states.json",
|
|
5064
5074
|
"mode": "0000644",
|
|
5065
|
-
"sha256": "
|
|
5075
|
+
"sha256": "74b6c43bb12a85e7d944069548c3e3a5de54f37267bbdc179157e1b164101399"
|
|
5066
5076
|
},
|
|
5067
5077
|
{
|
|
5068
5078
|
"path": "modules/hall-ui/public/md-reader.js",
|
|
@@ -5112,7 +5122,7 @@
|
|
|
5112
5122
|
{
|
|
5113
5123
|
"path": "modules/hall-ui/public/pair.states.json",
|
|
5114
5124
|
"mode": "0000644",
|
|
5115
|
-
"sha256": "
|
|
5125
|
+
"sha256": "3c48d1588f8512f0f9a0d5774acdf88c9a761e6b9f5f3da2d544a32bc06b4ab8"
|
|
5116
5126
|
},
|
|
5117
5127
|
{
|
|
5118
5128
|
"path": "modules/hall-ui/public/palette.js",
|
|
@@ -5342,7 +5352,7 @@
|
|
|
5342
5352
|
{
|
|
5343
5353
|
"path": "modules/hall-ui/public/settings.states.json",
|
|
5344
5354
|
"mode": "0000644",
|
|
5345
|
-
"sha256": "
|
|
5355
|
+
"sha256": "2d9f55bb007891b5d5b4511d7f29a20d01477a8395c84c259a42a7e6157aced9"
|
|
5346
5356
|
},
|
|
5347
5357
|
{
|
|
5348
5358
|
"path": "modules/hall-ui/public/shell.js",
|
|
@@ -5407,7 +5417,7 @@
|
|
|
5407
5417
|
{
|
|
5408
5418
|
"path": "modules/hall-ui/public/task.states.json",
|
|
5409
5419
|
"mode": "0000644",
|
|
5410
|
-
"sha256": "
|
|
5420
|
+
"sha256": "1be94e892012740aae899aa28425f4be9c94a5ae777c8c77e5e1b787791f5ec9"
|
|
5411
5421
|
},
|
|
5412
5422
|
{
|
|
5413
5423
|
"path": "modules/hall-ui/public/triumph.wav",
|
|
@@ -5452,7 +5462,7 @@
|
|
|
5452
5462
|
{
|
|
5453
5463
|
"path": "modules/hall-ui/public/work.states.json",
|
|
5454
5464
|
"mode": "0000644",
|
|
5455
|
-
"sha256": "
|
|
5465
|
+
"sha256": "786373b132587480ff58452286f950d246eae430f7531f0ccf4d01f290fdc435"
|
|
5456
5466
|
},
|
|
5457
5467
|
{
|
|
5458
5468
|
"path": "modules/ideas/CLAUDE.md",
|
|
@@ -5477,7 +5487,7 @@
|
|
|
5477
5487
|
{
|
|
5478
5488
|
"path": "modules/ideas/inbox.js",
|
|
5479
5489
|
"mode": "0000644",
|
|
5480
|
-
"sha256": "
|
|
5490
|
+
"sha256": "ff6c0346215d1b88309c4c9360cd60c59299b21b45e27cce09c4b748bf3411e1"
|
|
5481
5491
|
},
|
|
5482
5492
|
{
|
|
5483
5493
|
"path": "modules/ideas/migrations/ideas_001_grades.sql",
|
|
@@ -5657,7 +5667,7 @@
|
|
|
5657
5667
|
{
|
|
5658
5668
|
"path": "modules/lifecycle/db-tasks.js",
|
|
5659
5669
|
"mode": "0000644",
|
|
5660
|
-
"sha256": "
|
|
5670
|
+
"sha256": "5d17541d837a06903be832814a74218c9fee8b9119dd870c88273743be5a5efa"
|
|
5661
5671
|
},
|
|
5662
5672
|
{
|
|
5663
5673
|
"path": "modules/lifecycle/db-versions.js",
|
|
@@ -5752,7 +5762,7 @@
|
|
|
5752
5762
|
{
|
|
5753
5763
|
"path": "modules/lifecycle/lifecycle.js",
|
|
5754
5764
|
"mode": "0000644",
|
|
5755
|
-
"sha256": "
|
|
5765
|
+
"sha256": "41ef55efff7f334ee8b19490f03944ca25f22454c718dfb81445539bc6a77aa4"
|
|
5756
5766
|
},
|
|
5757
5767
|
{
|
|
5758
5768
|
"path": "modules/lifecycle/merge-lock.js",
|
|
@@ -5877,7 +5887,7 @@
|
|
|
5877
5887
|
{
|
|
5878
5888
|
"path": "modules/lifecycle/routes/goals.js",
|
|
5879
5889
|
"mode": "0000644",
|
|
5880
|
-
"sha256": "
|
|
5890
|
+
"sha256": "17f7c0ccfc87eec24c662bf8d5599223562e773430802cd054b27a3ddadab46e"
|
|
5881
5891
|
},
|
|
5882
5892
|
{
|
|
5883
5893
|
"path": "modules/lifecycle/routes/help-requests.js",
|
|
@@ -5922,7 +5932,7 @@
|
|
|
5922
5932
|
{
|
|
5923
5933
|
"path": "modules/lifecycle/routes/task-write-routes.js",
|
|
5924
5934
|
"mode": "0000644",
|
|
5925
|
-
"sha256": "
|
|
5935
|
+
"sha256": "bb172485d8d5f84d96cdb611b0d57cd5f69ee768bdceddeb1eea42dc3f5676c1"
|
|
5926
5936
|
},
|
|
5927
5937
|
{
|
|
5928
5938
|
"path": "modules/lifecycle/routes/tasks.js",
|
|
@@ -5962,7 +5972,7 @@
|
|
|
5962
5972
|
{
|
|
5963
5973
|
"path": "modules/lifecycle/task-classifier.js",
|
|
5964
5974
|
"mode": "0000644",
|
|
5965
|
-
"sha256": "
|
|
5975
|
+
"sha256": "39047afd6440d589cee978887ce4b5fa285b00d55ed2b023546fce41e59bdc9c"
|
|
5966
5976
|
},
|
|
5967
5977
|
{
|
|
5968
5978
|
"path": "modules/lifecycle/task-list-limits.js",
|
|
@@ -7892,12 +7902,12 @@
|
|
|
7892
7902
|
{
|
|
7893
7903
|
"path": "package-lock.json",
|
|
7894
7904
|
"mode": "0000644",
|
|
7895
|
-
"sha256": "
|
|
7905
|
+
"sha256": "a6133e7d6f098080ea0b2d2d21d3bbf35c90a716dc20a4436a68c74c6416da84"
|
|
7896
7906
|
},
|
|
7897
7907
|
{
|
|
7898
7908
|
"path": "package.json",
|
|
7899
7909
|
"mode": "0000644",
|
|
7900
|
-
"sha256": "
|
|
7910
|
+
"sha256": "65a0092e9c2f20b87c7de5d89e552e005820a70cc5c00fb228d904363d842b22"
|
|
7901
7911
|
},
|
|
7902
7912
|
{
|
|
7903
7913
|
"path": "public-docs/index.html",
|
|
@@ -8064,6 +8074,11 @@
|
|
|
8064
8074
|
"mode": "0000644",
|
|
8065
8075
|
"sha256": "9c03bf914c4a1a6f5d9936bb5542a4e3952873de65b0518254808bfbd1901b40"
|
|
8066
8076
|
},
|
|
8077
|
+
{
|
|
8078
|
+
"path": "scripts/gds/backfill-disciplines.js",
|
|
8079
|
+
"mode": "0000644",
|
|
8080
|
+
"sha256": "cc9bd2d437e6a7d6eb2b48504cc7d7c44a674403a0d00625a0526719825f88d2"
|
|
8081
|
+
},
|
|
8067
8082
|
{
|
|
8068
8083
|
"path": "scripts/gds/backfill-session-earnings.js",
|
|
8069
8084
|
"mode": "0000644",
|
|
@@ -8432,7 +8447,7 @@
|
|
|
8432
8447
|
{
|
|
8433
8448
|
"path": "scripts/gds/fitness-ratchets.js",
|
|
8434
8449
|
"mode": "0000644",
|
|
8435
|
-
"sha256": "
|
|
8450
|
+
"sha256": "178d2cfb91c97c107d60cc3895ba1b3eff74c45d383646e7a64621d018c21b47"
|
|
8436
8451
|
},
|
|
8437
8452
|
{
|
|
8438
8453
|
"path": "scripts/gds/fitness.js",
|
|
@@ -8577,7 +8592,7 @@
|
|
|
8577
8592
|
{
|
|
8578
8593
|
"path": "scripts/gds/init.js",
|
|
8579
8594
|
"mode": "0000644",
|
|
8580
|
-
"sha256": "
|
|
8595
|
+
"sha256": "e1fbe45a7619cd5000b5bb34c80bbd63c2591dcc78a64188870847e5da049448"
|
|
8581
8596
|
},
|
|
8582
8597
|
{
|
|
8583
8598
|
"path": "scripts/gds/install-git-hooks.js",
|
|
@@ -9367,7 +9382,7 @@
|
|
|
9367
9382
|
{
|
|
9368
9383
|
"path": "scripts/gds/triage.js",
|
|
9369
9384
|
"mode": "0000644",
|
|
9370
|
-
"sha256": "
|
|
9385
|
+
"sha256": "a4af12591b829f9b89a5768858e8a2ba02a44d7e7ebd848e945e77f95fc55da7"
|
|
9371
9386
|
},
|
|
9372
9387
|
{
|
|
9373
9388
|
"path": "scripts/gds/turn-end-sound.js",
|
|
@@ -9827,7 +9842,7 @@
|
|
|
9827
9842
|
{
|
|
9828
9843
|
"path": "src/module-api.js",
|
|
9829
9844
|
"mode": "0000644",
|
|
9830
|
-
"sha256": "
|
|
9845
|
+
"sha256": "a97295bdd0b3f0fe6b6c903b910a2512e7fffcc4a98f6d2844096cbc6015d7a7"
|
|
9831
9846
|
},
|
|
9832
9847
|
{
|
|
9833
9848
|
"path": "src/module-loader/catalog.js",
|
|
@@ -9987,7 +10002,7 @@
|
|
|
9987
10002
|
{
|
|
9988
10003
|
"path": "tests/agents_spend_guard.mjs",
|
|
9989
10004
|
"mode": "0000644",
|
|
9990
|
-
"sha256": "
|
|
10005
|
+
"sha256": "dde31247e6cb995487e21f78d5b44bfb24bc087bd7736c5a130ed4d8822d0cb7"
|
|
9991
10006
|
},
|
|
9992
10007
|
{
|
|
9993
10008
|
"path": "tests/agents_sync.mjs",
|
|
@@ -11679,6 +11694,11 @@
|
|
|
11679
11694
|
"mode": "0000644",
|
|
11680
11695
|
"sha256": "82bf1cc68150c247c108308d360d6408511f31a7af11d5e7a4721e7ea282f2bb"
|
|
11681
11696
|
},
|
|
11697
|
+
{
|
|
11698
|
+
"path": "tests/hall_states_regex_escapes.mjs",
|
|
11699
|
+
"mode": "0000644",
|
|
11700
|
+
"sha256": "906786d9728fd1d94449674422401ef1b53a460d5c84998565b1ebeab5e47c7a"
|
|
11701
|
+
},
|
|
11682
11702
|
{
|
|
11683
11703
|
"path": "tests/hall_studio_world.mjs",
|
|
11684
11704
|
"mode": "0000644",
|
|
@@ -11989,6 +12009,11 @@
|
|
|
11989
12009
|
"mode": "0000644",
|
|
11990
12010
|
"sha256": "416d413757a13fbddb5841ea8a2c503077d7e6067bc1c58ce1f4d416b8d8487f"
|
|
11991
12011
|
},
|
|
12012
|
+
{
|
|
12013
|
+
"path": "tests/knip_ratchet_ambient.mjs",
|
|
12014
|
+
"mode": "0000644",
|
|
12015
|
+
"sha256": "b48d7f46c4518ef7da8e252d8009edd25ec5771f7be320f889f437762af72b54"
|
|
12016
|
+
},
|
|
11992
12017
|
{
|
|
11993
12018
|
"path": "tests/known_failing_ratchet.mjs",
|
|
11994
12019
|
"mode": "0000644",
|
|
@@ -12862,7 +12887,7 @@
|
|
|
12862
12887
|
{
|
|
12863
12888
|
"path": "tests/rank_floor_wiring.mjs",
|
|
12864
12889
|
"mode": "0000644",
|
|
12865
|
-
"sha256": "
|
|
12890
|
+
"sha256": "973fc0768bd52f3e25c98306c21cc6a9c6268daae033e1329c68620a847bc0a9"
|
|
12866
12891
|
},
|
|
12867
12892
|
{
|
|
12868
12893
|
"path": "tests/rank_gate.mjs",
|
|
@@ -12972,7 +12997,7 @@
|
|
|
12972
12997
|
{
|
|
12973
12998
|
"path": "tests/reward_gate.mjs",
|
|
12974
12999
|
"mode": "0000644",
|
|
12975
|
-
"sha256": "
|
|
13000
|
+
"sha256": "0cd9f335da162c3562c099a2dbe4375b009f5a8a8321f4ef84b7a577be816bdc"
|
|
12976
13001
|
},
|
|
12977
13002
|
{
|
|
12978
13003
|
"path": "tests/reward_mode_cost_plus_only.mjs",
|
|
@@ -13619,6 +13644,11 @@
|
|
|
13619
13644
|
"mode": "0000644",
|
|
13620
13645
|
"sha256": "0bc0d93696b5ce80fb4307b7991bff640316e46fd6fcdebf0ba5a62397ffbe8f"
|
|
13621
13646
|
},
|
|
13647
|
+
{
|
|
13648
|
+
"path": "tests/task_discipline_required.mjs",
|
|
13649
|
+
"mode": "0000644",
|
|
13650
|
+
"sha256": "2fda6816614259699227a21f97b81d816f20fdd654c861e24a282c1d178ddff4"
|
|
13651
|
+
},
|
|
13622
13652
|
{
|
|
13623
13653
|
"path": "tests/task_field_bounds_match_db.mjs",
|
|
13624
13654
|
"mode": "0000644",
|
|
@@ -13637,7 +13667,7 @@
|
|
|
13637
13667
|
{
|
|
13638
13668
|
"path": "tests/task_goal_required.mjs",
|
|
13639
13669
|
"mode": "0000644",
|
|
13640
|
-
"sha256": "
|
|
13670
|
+
"sha256": "073581d9a97095666ee2ed7ebb0a6b280f3e8ea1efc53015c2f77eb5d08c4975"
|
|
13641
13671
|
},
|
|
13642
13672
|
{
|
|
13643
13673
|
"path": "tests/task_lib.mjs",
|
|
@@ -2158,5 +2158,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2158
2158
|
landed since 1.19.840 with no explicit bump. run 35257419225. (task 1002620)
|
|
2159
2159
|
1.19.842 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2160
2160
|
landed since 1.19.841 with no explicit bump. run 35258288930. (task 1002620)
|
|
2161
|
+
1.19.843 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2162
|
+
landed since 1.19.842 with no explicit bump. run 35259608793. (task 1002620)
|
|
2163
|
+
1.19.844 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2164
|
+
landed since 1.19.843 with no explicit bump. run 35262101669. (task 1002620)
|
|
2161
2165
|
---------------------------------------------------------------------------
|
|
2162
2166
|
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
-- core_247_discipline_required.sql — task 1002988: every task row carries a craft.
|
|
2
|
+
--
|
|
3
|
+
-- `unclassified` is a triage state, not a craft. It was the DEFAULT on every create
|
|
4
|
+
-- vector, so it accumulated: 1,189 rows on this instance as of 2026-09-17 (1,025
|
|
5
|
+
-- real + 164 __smoke__ test rows), of which only 208 were live work. A row nobody's
|
|
6
|
+
-- craft owns is a row nobody picks up.
|
|
7
|
+
--
|
|
8
|
+
-- THE ACCEPTED SET IS FOUR, NOT THREE. The task was written on 2026-08-16 naming
|
|
9
|
+
-- {engineer, artist, ideator}. ADR 0272 (2026-09-09) postdates it and KEEPS `ui` as
|
|
10
|
+
-- a module-contributed discipline, and the owner reconfirmed that on 2026-09-17 when
|
|
11
|
+
-- abandoning the sibling task 1002987 ("Delete the ui role across the platform") for
|
|
12
|
+
-- exactly this reason. Narrowing to three would break 50+ live `ui` rows and
|
|
13
|
+
-- contradict a live ADR, so migration 176's `ui` entry survives untouched here.
|
|
14
|
+
--
|
|
15
|
+
-- WHY THE BACKFILL IS IN THE MIGRATION. The good classification is done by
|
|
16
|
+
-- scripts/gds/backfill-disciplines.js, which runs the real JS heuristic
|
|
17
|
+
-- (classifyTaskDiscipline) and cannot be expressed in SQL. But a migration that
|
|
18
|
+
-- assumes a script was run by hand is a migration that breaks the next fresh
|
|
19
|
+
-- install, the next instance, and any checkout where nobody remembered. So the
|
|
20
|
+
-- UPDATE below is the LAST-RESORT NET, not the plan: it catches whatever the script
|
|
21
|
+
-- left (or everything, if the script never ran) and parks it on 'engineer' — the
|
|
22
|
+
-- value ~99% of confidently-classified rows resolve to.
|
|
23
|
+
--
|
|
24
|
+
-- That fallback is a GUESS and is recorded as one. The owner chose it on 2026-09-17
|
|
25
|
+
-- over the alternative (backfill live work only, leave history alone), accepting
|
|
26
|
+
-- that the ~273 rows the classifier refuses get a best guess rather than a known
|
|
27
|
+
-- answer. Do not read a post-migration `discipline` on an old row as evidence that
|
|
28
|
+
-- anyone determined it.
|
|
29
|
+
--
|
|
30
|
+
-- Idempotent: safe to re-run. The UPDATE matches nothing on a second pass, and the
|
|
31
|
+
-- constraint is dropped before being re-added (the pattern of migrations 086/176).
|
|
32
|
+
-- Forward-safe: the column is unchanged and the previous release wrote only values
|
|
33
|
+
-- this constraint still accepts, EXCEPT 'unclassified' — a rolled-back deploy whose
|
|
34
|
+
-- code still defaults to 'unclassified' would have its INSERTs rejected. That is the
|
|
35
|
+
-- intended behaviour (it is the hole being closed), and it is why the application
|
|
36
|
+
-- side of this task resolves the discipline at db-tasks.createTask, the single
|
|
37
|
+
-- chokepoint every create vector passes through, rather than only at the two routes.
|
|
38
|
+
|
|
39
|
+
BEGIN;
|
|
40
|
+
|
|
41
|
+
-- 1. The last-resort net (see header). Everything the classifier could call should
|
|
42
|
+
-- already have been PATCHed by backfill-disciplines.js before this runs.
|
|
43
|
+
UPDATE tasks SET discipline = 'engineer' WHERE discipline = 'unclassified' OR discipline IS NULL;
|
|
44
|
+
|
|
45
|
+
-- 2. Narrow the accepted set: drop 'unclassified', keep 'ui' (ADR 0272).
|
|
46
|
+
ALTER TABLE tasks DROP CONSTRAINT IF EXISTS tasks_discipline_check;
|
|
47
|
+
ALTER TABLE tasks ADD CONSTRAINT tasks_discipline_check
|
|
48
|
+
CHECK (discipline IN ('engineer', 'artist', 'ideator', 'ui'));
|
|
49
|
+
|
|
50
|
+
-- builders.preferred_disciplines already excludes 'unclassified' (migration 176 —
|
|
51
|
+
-- "Never carries 'unclassified' — that's a task state, not an interest area"), so it
|
|
52
|
+
-- needs no change here. Named rather than silently skipped, so the next reader does
|
|
53
|
+
-- not have to re-check.
|
|
54
|
+
|
|
55
|
+
INSERT INTO schema_migrations (version) VALUES ('core_247_discipline_required')
|
|
56
|
+
ON CONFLICT (version) DO NOTHING;
|
|
57
|
+
|
|
58
|
+
COMMIT;
|
|
@@ -59,6 +59,18 @@
|
|
|
59
59
|
// be saved by a generous default.
|
|
60
60
|
const DEFAULT_CAP_USD = 25;
|
|
61
61
|
|
|
62
|
+
// The PER-AGENT default (task 1002499, owner decision 2026-07-05: ~$5/month per
|
|
63
|
+
// agent under the ~$30/month global brake). Deliberately much smaller than the
|
|
64
|
+
// per-payer cap above, because it bounds a different thing: that one bounds the
|
|
65
|
+
// WALLET, this one bounds ONE DEFINITION. Under a single project payer the two
|
|
66
|
+
// were the same number, which meant a runaway agent could eat the whole instance
|
|
67
|
+
// ceiling and every other agent would then be refused for a bill it did not run
|
|
68
|
+
// up. At the historian exemplar's ~$0.90 a fire, $5 is about five fires a month —
|
|
69
|
+
// enough for a routine that answers a few times a week, and not enough for a loop.
|
|
70
|
+
// A definition sets its own with agents_definitions.spend_cap_usd; an instance
|
|
71
|
+
// moves every unset one with BONGOS_AGENT_SPEND_CAP_USD.
|
|
72
|
+
const DEFAULT_AGENT_CAP_USD = 5;
|
|
73
|
+
|
|
62
74
|
// Calendar month, UTC. It matches the window agents_001's spend index was built
|
|
63
75
|
// for ("What did agents cost this month?") and the $/month cap fire-budget's
|
|
64
76
|
// header defers to — and a calendar month is the unit a bill arrives in, which is
|
|
@@ -96,12 +108,116 @@ function payerFor(spendPayer, requestedByBuilderId) {
|
|
|
96
108
|
return { payer_kind: 'project', payer_builder_id: null };
|
|
97
109
|
}
|
|
98
110
|
|
|
111
|
+
// Read one SUM(cost_usd) and return a TRUSTWORTHY number, or null for "I don't
|
|
112
|
+
// know". Shared by the two ceilings below so the fail-closed reading of a ledger
|
|
113
|
+
// is written once — but note that sharing it is not itself the guarantee: the
|
|
114
|
+
// tests exercise BOTH ceilings against a throwing pool, a NULL sum and a garbage
|
|
115
|
+
// sum independently, because a helper's documented safety property is a claim
|
|
116
|
+
// until each caller is shown to honour it.
|
|
117
|
+
//
|
|
118
|
+
// Every "I don't know" collapses to null here, and every caller turns null into a
|
|
119
|
+
// REFUSAL. The one thing this must never do is return 0 for an absent value —
|
|
120
|
+
// Number(null) is 0, and a zero reads as "has spent nothing", which is the
|
|
121
|
+
// fail-OPEN direction wearing a plausible number. That is the whole of task
|
|
122
|
+
// 1001393, so the absence is rejected before it can be coerced.
|
|
123
|
+
async function sumOrNull(pool, sql, params) {
|
|
124
|
+
if (!pool || typeof pool.query !== 'function') return null;
|
|
125
|
+
let raw;
|
|
126
|
+
try {
|
|
127
|
+
const { rows } = await pool.query(sql, params);
|
|
128
|
+
raw = rows && rows[0] ? rows[0].spent : undefined;
|
|
129
|
+
} catch (_) {
|
|
130
|
+
// THE 1001393 CASE. An unreadable ledger is the one this file exists for.
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
if (raw === null || raw === undefined) return null;
|
|
134
|
+
// NUMERIC comes back as a string from pg, so this catches a genuinely
|
|
135
|
+
// unparseable value, not the ordinary one.
|
|
136
|
+
const n = Number(raw);
|
|
137
|
+
return (Number.isFinite(n) && n >= 0) ? n : null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Summed BY PAYER, so a builder-paid instance gives each builder their own month
|
|
141
|
+
// and a project-paid one sums the instance. Rows written before agents_002 carry
|
|
142
|
+
// no payer_kind and match neither, which is the conservative direction: nobody is
|
|
143
|
+
// charged for spend that was not attributed to them.
|
|
144
|
+
const PAYER_SPEND_SQL = `SELECT COALESCE(SUM(cost_usd), 0) AS spent
|
|
145
|
+
FROM agents_runs
|
|
146
|
+
WHERE cost_usd IS NOT NULL
|
|
147
|
+
AND created_at >= $1
|
|
148
|
+
AND payer_kind = $2
|
|
149
|
+
AND payer_builder_id IS NOT DISTINCT FROM $3`;
|
|
150
|
+
|
|
151
|
+
// Summed BY AGENT NAME, across every payer. The name is the identity that
|
|
152
|
+
// survives a definition being re-synced, disabled or re-authored — agents_001
|
|
153
|
+
// built agents_runs_agent_idx (agent_name, created_at DESC) for exactly this
|
|
154
|
+
// read. Deliberately NOT keyed on definition_id: that column is ON DELETE SET
|
|
155
|
+
// NULL, so deleting a definition would zero its month and hand a fresh cap to a
|
|
156
|
+
// re-created agent of the same name.
|
|
157
|
+
const AGENT_SPEND_SQL = `SELECT COALESCE(SUM(cost_usd), 0) AS spent
|
|
158
|
+
FROM agents_runs
|
|
159
|
+
WHERE cost_usd IS NOT NULL
|
|
160
|
+
AND created_at >= $1
|
|
161
|
+
AND agent_name = $2`;
|
|
162
|
+
|
|
163
|
+
// The per-agent cap for one definition: its own spend_cap_usd (agents_004) when it
|
|
164
|
+
// declares one, else the instance default. A stored cap is schema-guaranteed
|
|
165
|
+
// positive, so anything else in that column is a row this code did not write and
|
|
166
|
+
// gets the default rather than a guess.
|
|
167
|
+
function agentCapFor(definition, fallback) {
|
|
168
|
+
const raw = definition && definition.spend_cap_usd;
|
|
169
|
+
if (raw === null || raw === undefined) return fallback;
|
|
170
|
+
const n = Number(raw);
|
|
171
|
+
return (Number.isFinite(n) && n > 0) ? n : fallback;
|
|
172
|
+
}
|
|
173
|
+
|
|
99
174
|
function createSpendCeiling({
|
|
100
175
|
capUsd = envNumber('BONGOS_AGENTS_SPEND_CAP_USD', DEFAULT_CAP_USD),
|
|
176
|
+
agentCapUsd = envNumber('BONGOS_AGENT_SPEND_CAP_USD', DEFAULT_AGENT_CAP_USD),
|
|
101
177
|
now = () => Date.now(),
|
|
102
178
|
} = {}) {
|
|
103
179
|
return {
|
|
104
180
|
capUsd,
|
|
181
|
+
agentCapUsd,
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* May THIS AGENT spend right now? (task 1002499)
|
|
185
|
+
*
|
|
186
|
+
* The sibling of check() below, and deliberately a separate call rather than
|
|
187
|
+
* a wider one: they refuse for different reasons and an operator must be able
|
|
188
|
+
* to tell "this instance's money is gone" from "this one agent has had its
|
|
189
|
+
* share". Both run on every fire, cheapest first.
|
|
190
|
+
*
|
|
191
|
+
* Records nothing, for the same reason check() does not: agents_runs is the
|
|
192
|
+
* accounting, and a ceiling that kept its own tally would be a second,
|
|
193
|
+
* disagreeing answer to what agents cost.
|
|
194
|
+
*
|
|
195
|
+
* Fails closed on every unhappy path, exactly like check(): no usable cap, no
|
|
196
|
+
* agent name, an unreadable ledger, or the cap already spent. The KNOWN
|
|
197
|
+
* OVERSHOOT in this file's header applies here too — a fire in flight carries
|
|
198
|
+
* cost_usd NULL and is invisible to this read.
|
|
199
|
+
*
|
|
200
|
+
* @returns {{ok: boolean, spentUsd: number|null, capUsd: number, remainingUsd: number, reason: string|null}}
|
|
201
|
+
*/
|
|
202
|
+
async checkAgent(pool, definition) {
|
|
203
|
+
const cap = agentCapFor(definition, Number(agentCapUsd));
|
|
204
|
+
const deny = (reason) => ({ ok: false, spentUsd: null, capUsd: cap, remainingUsd: 0, reason });
|
|
205
|
+
|
|
206
|
+
if (!Number.isFinite(cap) || cap <= 0) return deny('no_agent_spend_cap_configured');
|
|
207
|
+
const name = definition && definition.name;
|
|
208
|
+
// An unnamed definition cannot be summed, so its month is unknowable — the
|
|
209
|
+
// same "I don't know" a throwing ledger gives, and it gets the same answer.
|
|
210
|
+
if (typeof name !== 'string' || !name.trim()) return deny('no_agent_identity');
|
|
211
|
+
|
|
212
|
+
const spent = await sumOrNull(pool, AGENT_SPEND_SQL, [monthStartUtc(now()), name]);
|
|
213
|
+
if (spent === null) return deny('ledger_unreadable');
|
|
214
|
+
|
|
215
|
+
const remaining = cap - spent;
|
|
216
|
+
if (remaining <= 0) {
|
|
217
|
+
return { ok: false, spentUsd: spent, capUsd: cap, remainingUsd: 0, reason: 'agent_spend_cap_reached' };
|
|
218
|
+
}
|
|
219
|
+
return { ok: true, spentUsd: spent, capUsd: cap, remainingUsd: remaining, reason: null };
|
|
220
|
+
},
|
|
105
221
|
|
|
106
222
|
/**
|
|
107
223
|
* May this payer spend right now?
|
|
@@ -121,42 +237,12 @@ function createSpendCeiling({
|
|
|
121
237
|
if (kind !== 'project' && kind !== 'builder') return deny('no_payer');
|
|
122
238
|
const builderId = payer.payer_builder_id ?? null;
|
|
123
239
|
if (kind === 'builder' && builderId === null) return deny('no_payer');
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// month and a project-paid one sums the instance. Rows written before
|
|
131
|
-
// agents_002 carry no payer_kind and match neither, which is the
|
|
132
|
-
// conservative direction: nobody is charged for spend that was not
|
|
133
|
-
// attributed to them.
|
|
134
|
-
const { rows } = await pool.query(
|
|
135
|
-
`SELECT COALESCE(SUM(cost_usd), 0) AS spent
|
|
136
|
-
FROM agents_runs
|
|
137
|
-
WHERE cost_usd IS NOT NULL
|
|
138
|
-
AND created_at >= $1
|
|
139
|
-
AND payer_kind = $2
|
|
140
|
-
AND payer_builder_id IS NOT DISTINCT FROM $3`,
|
|
141
|
-
[since, kind, builderId],
|
|
142
|
-
);
|
|
143
|
-
const raw = rows && rows[0] ? rows[0].spent : undefined;
|
|
144
|
-
// NOT `Number(raw)` alone. Number(null) is 0, so a sum that came back as
|
|
145
|
-
// SQL NULL — a shape COALESCE should make impossible, and would therefore
|
|
146
|
-
// only appear if this query or that table changed under us — would read
|
|
147
|
-
// as "this payer has spent nothing" and admit the fire. That is the
|
|
148
|
-
// fail-OPEN direction wearing a plausible number, which is the whole
|
|
149
|
-
// failure mode task 1001393 was filed about, so an absent value is
|
|
150
|
-
// rejected BEFORE it can be coerced into a permissive one.
|
|
151
|
-
spent = (raw === null || raw === undefined) ? Number.NaN : Number(raw);
|
|
152
|
-
} catch (_) {
|
|
153
|
-
// THE 1001393 CASE. An unreadable ledger is the one this file exists for.
|
|
154
|
-
return deny('ledger_unreadable');
|
|
155
|
-
}
|
|
156
|
-
// A sum that is not a number is the same "I don't know" as a throw, and gets
|
|
157
|
-
// the same answer. NUMERIC comes back as a string from pg, so this catches a
|
|
158
|
-
// genuinely unparseable value, not the ordinary one.
|
|
159
|
-
if (!Number.isFinite(spent) || spent < 0) return deny('ledger_unreadable');
|
|
240
|
+
|
|
241
|
+
// sumOrNull carries the fail-closed reading (a missing pool, a throw, a NULL
|
|
242
|
+
// sum and an unparseable sum all become null) — see its header for why an
|
|
243
|
+
// absent value must never be coerced to 0.
|
|
244
|
+
const spent = await sumOrNull(pool, PAYER_SPEND_SQL, [monthStartUtc(now()), kind, builderId]);
|
|
245
|
+
if (spent === null) return deny('ledger_unreadable');
|
|
160
246
|
|
|
161
247
|
const remaining = cap - spent;
|
|
162
248
|
if (remaining <= 0) {
|
|
@@ -167,4 +253,7 @@ function createSpendCeiling({
|
|
|
167
253
|
};
|
|
168
254
|
}
|
|
169
255
|
|
|
170
|
-
module.exports = {
|
|
256
|
+
module.exports = {
|
|
257
|
+
createSpendCeiling, payerFor, monthStartUtc, agentCapFor,
|
|
258
|
+
DEFAULT_CAP_USD, DEFAULT_AGENT_CAP_USD,
|
|
259
|
+
};
|