@bongos/core 1.19.365 → 1.19.367

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.
Files changed (34) hide show
  1. package/.bongos-core.json +93 -28
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +8 -0
  4. package/clients/bongos-client/index.cjs +8 -0
  5. package/clients/bongos-client/index.d.ts +9 -1
  6. package/clients/bongos-client/index.mjs +8 -0
  7. package/docs/adr/0192-a-category-orients-and-authorises-nothing.md +97 -0
  8. package/docs/adr/README.md +1 -0
  9. package/docs/api/openapi.json +117 -1
  10. package/docs/api-reference.md +11 -3
  11. package/docs/file-map.md +13 -1
  12. package/migrations/core_226_work_categories.sql +100 -0
  13. package/modules/builder-settings/event-sound-prefs.js +14 -4
  14. package/modules/dev-box/app/src/vendor/bongos-client.cjs +8 -0
  15. package/modules/hall-ui/public/alert.wav +0 -0
  16. package/modules/hall-ui/public/fanfare.wav +0 -0
  17. package/modules/hall-ui/public/ship.wav +0 -0
  18. package/modules/hall-ui/public/triumph.wav +0 -0
  19. package/modules/lifecycle/db-goals.js +5 -5
  20. package/modules/lifecycle/goal-category.js +273 -0
  21. package/modules/lifecycle/routes/goals.js +38 -2
  22. package/package-lock.json +2 -2
  23. package/package.json +1 -1
  24. package/scripts/gds/category-advisory-guard.js +153 -0
  25. package/scripts/gds/fitness.js +6 -1
  26. package/scripts/gds/gen-sounds.js +176 -0
  27. package/scripts/gds/ship-io.js +16 -2
  28. package/scripts/gds/turn-end-sound.js +74 -0
  29. package/src/bongos/route-rank-check.js +11 -0
  30. package/src/module-api.js +5 -1
  31. package/tests/category_advisory_guard.mjs +135 -0
  32. package/tests/goal_category.mjs +199 -0
  33. package/tests/goal_tier.mjs +7 -5
  34. package/tests/sound_player.mjs +345 -0
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.365",
6
- "core_contract": "1.19.365",
7
- "source_commit": "cf24b657bacb6c291ab9351c73cfd93be421c62f",
5
+ "core_version": "1.19.367",
6
+ "core_contract": "1.19.367",
7
+ "source_commit": "ff41cbee283990f68a2c9bf200a4c325dcfaafb8",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-25T20:07:12.580Z",
9
+ "built_at": "2026-08-25T21:38:46.917Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 331,
12
+ "docs_redacted": 332,
13
13
  "agent_docs_stubbed": 22,
14
- "functional_verbatim": 1529,
14
+ "functional_verbatim": 1541,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1882,
20
- "tree_sha256": "7ecb76bb4cadcc01fbe7adb2a6301d5dd509e1d98ab5ea776486fb7499fc877b",
19
+ "file_count": 1895,
20
+ "tree_sha256": "a59dcfb6a265f28e9abf4bcd3428af1117f21091d32a5e4ea19e882d689eb0e4",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -347,12 +347,12 @@
347
347
  {
348
348
  "path": "clients/bongos-client/README.md",
349
349
  "mode": "0000644",
350
- "sha256": "b94bec08062d2f3374043a6ce93bca31b27073e51da47f9477e67e2602565701"
350
+ "sha256": "fc26e581e9ba06ec9fd798e982539f0bd8dc4c27a5f6c248b395f62214daaf78"
351
351
  },
352
352
  {
353
353
  "path": "clients/bongos-client/bongos-client.global.js",
354
354
  "mode": "0000644",
355
- "sha256": "827ffa492b7035169b0734807208d45dc898d86b7d52570b55f7050d83e0aba3"
355
+ "sha256": "e50696e5b7b9c1abab280665dd57ab6f8f8883d3bfd0f2701a45ee7d9a6fb728"
356
356
  },
357
357
  {
358
358
  "path": "clients/bongos-client/examples/hello-world.mjs",
@@ -362,17 +362,17 @@
362
362
  {
363
363
  "path": "clients/bongos-client/index.cjs",
364
364
  "mode": "0000644",
365
- "sha256": "7f601b37bb8acad773f55b5e5200cf1efeb40cdcc2694570b58c336cdcf9b3ad"
365
+ "sha256": "b13e62181adbc47a9ba6d3349a4abcb63fca123f3b0202414a1f7a2ba5e7d088"
366
366
  },
367
367
  {
368
368
  "path": "clients/bongos-client/index.d.ts",
369
369
  "mode": "0000644",
370
- "sha256": "3834aed054469417cecbd8c1786cbc5400ec1a0e7871ac3bc41c8044f2f97aae"
370
+ "sha256": "f918564f73e753583651a8d963824c981cd82dc02d1e0d809d245e541219513e"
371
371
  },
372
372
  {
373
373
  "path": "clients/bongos-client/index.mjs",
374
374
  "mode": "0000644",
375
- "sha256": "d3f455fe0a335606c10ec92fc151f4767bf37d9c40c423f7635c642dd5b1e298"
375
+ "sha256": "d4f2e6c3b376c35679416efcd30bbe54bae396cbaa8ee1e4078c56b6175da3d4"
376
376
  },
377
377
  {
378
378
  "path": "clients/bongos-client/package.json",
@@ -1449,20 +1449,25 @@
1449
1449
  "mode": "0000644",
1450
1450
  "sha256": "0ae7f6a69d9006c54210cea4f6a846445a6181f148bcb4de979011b46cca5188"
1451
1451
  },
1452
+ {
1453
+ "path": "docs/adr/0192-a-category-orients-and-authorises-nothing.md",
1454
+ "mode": "0000644",
1455
+ "sha256": "4cb1023731d70965f544bfcde1a9bfa3165e2bc96e0825c7e2c3b5a02c464a63"
1456
+ },
1452
1457
  {
1453
1458
  "path": "docs/adr/README.md",
1454
1459
  "mode": "0000644",
1455
- "sha256": "cba01a64b2f86f9e10e6a7306bf23d3fbebe16b2e5d7ddd04f68a1113a3ae525"
1460
+ "sha256": "804999c67653b53c81b68d97fe8be11137c6f19fb3048899d847844095467b11"
1456
1461
  },
1457
1462
  {
1458
1463
  "path": "docs/api-reference.md",
1459
1464
  "mode": "0000644",
1460
- "sha256": "d076deeab4cc5690b2fd8081b3ea885d2e6c414267ff4cacb43d71e9b3d92d8c"
1465
+ "sha256": "544b25f700d89d7f51dd84cb4faa740bc1c9bb4bf52853470e7d645040215033"
1461
1466
  },
1462
1467
  {
1463
1468
  "path": "docs/api/openapi.json",
1464
1469
  "mode": "0000644",
1465
- "sha256": "88484f046d52c6ff605fbecd5c32d14542279f487586b8c371de3ce6de50e812"
1470
+ "sha256": "ada26795f34a9ce26711b788abc87bf277c475aa14ee7bb1f92e9376f1e50cc8"
1466
1471
  },
1467
1472
  {
1468
1473
  "path": "docs/architecture.md",
@@ -1602,7 +1607,7 @@
1602
1607
  {
1603
1608
  "path": "docs/file-map.md",
1604
1609
  "mode": "0000644",
1605
- "sha256": "1cda168698104ff12fbe75a28a5f76fd11a2d7311c8ba32ba985874dbff65570"
1610
+ "sha256": "8c92a7b61b7e90dff48de1d2e9444d6f460e1d71c41c45f78554deef4c1cb30b"
1606
1611
  },
1607
1612
  {
1608
1613
  "path": "docs/handoff-template.md",
@@ -2509,6 +2514,11 @@
2509
2514
  "mode": "0000644",
2510
2515
  "sha256": "1913405bdb9de48ac3ca7ad6705cf44244d20c77d3a6f73700f88e717fb84043"
2511
2516
  },
2517
+ {
2518
+ "path": "migrations/core_226_work_categories.sql",
2519
+ "mode": "0000644",
2520
+ "sha256": "322578d3d22b87eb07c4b9b4b521c66b0c986a937c3657d0b90123f73867461d"
2521
+ },
2512
2522
  {
2513
2523
  "path": "modules/agents/migrations/agents_001_definitions.sql",
2514
2524
  "mode": "0000644",
@@ -2572,7 +2582,7 @@
2572
2582
  {
2573
2583
  "path": "modules/builder-settings/event-sound-prefs.js",
2574
2584
  "mode": "0000644",
2575
- "sha256": "546e849963a471d1adb417fbbb52d9d0c303098079fbfeb6f8ebdd9173e5b1e7"
2585
+ "sha256": "a4115ffcd3b298959b4ea1b13a0c5bfd645c603ad2dd4361d1340ab645dae435"
2576
2586
  },
2577
2587
  {
2578
2588
  "path": "modules/builder-settings/interaction-prefs.js",
@@ -2887,7 +2897,7 @@
2887
2897
  {
2888
2898
  "path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
2889
2899
  "mode": "0000644",
2890
- "sha256": "7f601b37bb8acad773f55b5e5200cf1efeb40cdcc2694570b58c336cdcf9b3ad"
2900
+ "sha256": "b13e62181adbc47a9ba6d3349a4abcb63fca123f3b0202414a1f7a2ba5e7d088"
2891
2901
  },
2892
2902
  {
2893
2903
  "path": "modules/dev-box/app/test/unit.mjs",
@@ -3384,6 +3394,11 @@
3384
3394
  "mode": "0000644",
3385
3395
  "sha256": "fd1220e4b2716912a03c78b23a7d0c78e4a5f4e325547cdd5207487147aad82c"
3386
3396
  },
3397
+ {
3398
+ "path": "modules/hall-ui/public/alert.wav",
3399
+ "mode": "0000644",
3400
+ "sha256": "d175874039e054abf211670908b33080438ffdf7d3a38b9f9a715c6d066fc21a"
3401
+ },
3387
3402
  {
3388
3403
  "path": "modules/hall-ui/public/atlas.css",
3389
3404
  "mode": "0000644",
@@ -3509,6 +3524,11 @@
3509
3524
  "mode": "0000644",
3510
3525
  "sha256": "098fcc0a3933b6d76086f92840d3b3ddb324de7b4f25cb475d23ad4a346723df"
3511
3526
  },
3527
+ {
3528
+ "path": "modules/hall-ui/public/fanfare.wav",
3529
+ "mode": "0000644",
3530
+ "sha256": "9885b45c517fb078c30673e3fb89faceae4cb3191096e35d8b248ef1d253f9e4"
3531
+ },
3512
3532
  {
3513
3533
  "path": "modules/hall-ui/public/gate.css",
3514
3534
  "mode": "0000644",
@@ -3804,6 +3824,11 @@
3804
3824
  "mode": "0000644",
3805
3825
  "sha256": "8d07f24b287a54fdff6d340972d8cc80e37f8fffb4169834fe051a40e6a196ac"
3806
3826
  },
3827
+ {
3828
+ "path": "modules/hall-ui/public/ship.wav",
3829
+ "mode": "0000644",
3830
+ "sha256": "733c8db2c083c7e7595799a7941f13c9940a5615fde1f23e28d03e2aabce1f3c"
3831
+ },
3807
3832
  {
3808
3833
  "path": "modules/hall-ui/public/style.css",
3809
3834
  "mode": "0000644",
@@ -3829,6 +3854,11 @@
3829
3854
  "mode": "0000644",
3830
3855
  "sha256": "f2c4c154920e8f1019fb1628e187201e6e582ec61b04fa9fb9620be097f44b62"
3831
3856
  },
3857
+ {
3858
+ "path": "modules/hall-ui/public/triumph.wav",
3859
+ "mode": "0000644",
3860
+ "sha256": "0d17be2800724ca4130197f222222fcdad167bde24c068739f5ad4bfb6d6ccd8"
3861
+ },
3832
3862
  {
3833
3863
  "path": "modules/hall-ui/public/watch.css",
3834
3864
  "mode": "0000644",
@@ -3987,7 +4017,7 @@
3987
4017
  {
3988
4018
  "path": "modules/lifecycle/db-goals.js",
3989
4019
  "mode": "0000644",
3990
- "sha256": "b9d1768f8e271ad5c3595728f2433c27f44c841ab1c6be7d24086d1f6c8c7691"
4020
+ "sha256": "85a87c86496a9f022983a61e7f3642c8f0029c0b07467e27109095a6b98fce61"
3991
4021
  },
3992
4022
  {
3993
4023
  "path": "modules/lifecycle/db-grade.js",
@@ -4059,6 +4089,11 @@
4059
4089
  "mode": "0000644",
4060
4090
  "sha256": "3eb9d818e0e0d45a896a5c0a515842b5f5a01e102d02f8a9a605c271088c8e5d"
4061
4091
  },
4092
+ {
4093
+ "path": "modules/lifecycle/goal-category.js",
4094
+ "mode": "0000644",
4095
+ "sha256": "6bc1bc0152900b49d79a6c09560020dc96ff5fae6c78d7dbb84d72dfed1953d2"
4096
+ },
4062
4097
  {
4063
4098
  "path": "modules/lifecycle/goal-conflicts.js",
4064
4099
  "mode": "0000644",
@@ -4167,7 +4202,7 @@
4167
4202
  {
4168
4203
  "path": "modules/lifecycle/routes/goals.js",
4169
4204
  "mode": "0000644",
4170
- "sha256": "bfc87f9887cfe03abfdec0a9a3139917099922fc37d56fb0fcb4ec47980fb53b"
4205
+ "sha256": "5e01d0da935758aa24c8f13d2f4e97d9f561c2b1044b326fb4184c5dbc4bf6fb"
4171
4206
  },
4172
4207
  {
4173
4208
  "path": "modules/lifecycle/routes/help-requests.js",
@@ -4857,12 +4892,12 @@
4857
4892
  {
4858
4893
  "path": "package-lock.json",
4859
4894
  "mode": "0000644",
4860
- "sha256": "0c5e3a6968b8cea0f192e4b7afabd3d56ebdfb4c195caa6b2e2c83488f747994"
4895
+ "sha256": "390ce17a741752f8e4ee90ad06761bf3618a389e4d5e4f2fbaa8292355686b1e"
4861
4896
  },
4862
4897
  {
4863
4898
  "path": "package.json",
4864
4899
  "mode": "0000644",
4865
- "sha256": "690bded1bc02daf39aab34106ac5430080ed1125978e8e4d641b60c9fd465dd9"
4900
+ "sha256": "3a8cef238a15b3d12a6f49fc9825ccf6723581e0c2764acd58f19a772cb10814"
4866
4901
  },
4867
4902
  {
4868
4903
  "path": "public-docs/index.html",
@@ -5329,6 +5364,11 @@
5329
5364
  "mode": "0000644",
5330
5365
  "sha256": "946fa97d020eb481b7da559f0b5c99bfa16dc24b680a01e0ff3d3faeaf5bd701"
5331
5366
  },
5367
+ {
5368
+ "path": "scripts/gds/category-advisory-guard.js",
5369
+ "mode": "0000644",
5370
+ "sha256": "008fc7268a1e58b7e87b5454d0ca698e25cba3229172031587e934fe73fc33da"
5371
+ },
5332
5372
  {
5333
5373
  "path": "scripts/gds/cf-tunnel.js",
5334
5374
  "mode": "0000644",
@@ -5552,7 +5592,7 @@
5552
5592
  {
5553
5593
  "path": "scripts/gds/fitness.js",
5554
5594
  "mode": "0000644",
5555
- "sha256": "e38069a6eee1072438d745634cdae405c57c20b8261f133d4afa0440b1d3d545"
5595
+ "sha256": "95f116e70ba6d6e890705ff862a5c5011002c42a58ba60553c50ec2d656dcf3b"
5556
5596
  },
5557
5597
  {
5558
5598
  "path": "scripts/gds/gate-review.js",
@@ -5624,6 +5664,11 @@
5624
5664
  "mode": "0000644",
5625
5665
  "sha256": "9b936143ae25442fefb52b98949bc33d245d4b3894d778c51926e9da75c92357"
5626
5666
  },
5667
+ {
5668
+ "path": "scripts/gds/gen-sounds.js",
5669
+ "mode": "0000644",
5670
+ "sha256": "321fa3e48fbf0987a133d232b811feea84af8ee7b6d121b1b2a5c5c527841413"
5671
+ },
5627
5672
  {
5628
5673
  "path": "scripts/gds/git-merge-regen.js",
5629
5674
  "mode": "0000644",
@@ -6197,7 +6242,7 @@
6197
6242
  {
6198
6243
  "path": "scripts/gds/ship-io.js",
6199
6244
  "mode": "0000644",
6200
- "sha256": "dc863484634845b84913532db0606a64006f6a506b97b94ea131dfdc1c1ed245"
6245
+ "sha256": "d881d3de837a0d13f2fcaddac7aec17969e8c2d395db238fe9bf257a0925deff"
6201
6246
  },
6202
6247
  {
6203
6248
  "path": "scripts/gds/ship-land.js",
@@ -6324,6 +6369,11 @@
6324
6369
  "mode": "0000644",
6325
6370
  "sha256": "831de1914642a7dcf7c591c9cc69b37c917122e04428cb1e28fc00b07170b603"
6326
6371
  },
6372
+ {
6373
+ "path": "scripts/gds/turn-end-sound.js",
6374
+ "mode": "0000644",
6375
+ "sha256": "cc6e8640376ada7ceff6a896007b3257c8ea4d943a900eae28433cd41bdd139d"
6376
+ },
6327
6377
  {
6328
6378
  "path": "scripts/gds/update-channel.js",
6329
6379
  "mode": "0000644",
@@ -6592,7 +6642,7 @@
6592
6642
  {
6593
6643
  "path": "src/bongos/route-rank-check.js",
6594
6644
  "mode": "0000644",
6595
- "sha256": "f2d893e0de84433cc99411e59aba98c1ca392fa8d5bd24449e1e9200f06d9ff8"
6645
+ "sha256": "b387d8af3a47e209139f40437c5cd855f58d4e37871869efb23069d575ddd041"
6596
6646
  },
6597
6647
  {
6598
6648
  "path": "src/bongos/routes.js",
@@ -6727,7 +6777,7 @@
6727
6777
  {
6728
6778
  "path": "src/module-api.js",
6729
6779
  "mode": "0000644",
6730
- "sha256": "29fe80fe994a985cfbc64db7d7835b09485ed7dceae578fab86d0fcb99eccc46"
6780
+ "sha256": "4a12d31fe4599fab8030fbebb69bb6618756e7c6239b081fbd489b821e04e9c6"
6731
6781
  },
6732
6782
  {
6733
6783
  "path": "src/module-loader/catalog.js",
@@ -7164,6 +7214,11 @@
7164
7214
  "mode": "0000644",
7165
7215
  "sha256": "de9d8f8c1daab1e48122febd16699ac519890b6e1296edb7966d835434851786"
7166
7216
  },
7217
+ {
7218
+ "path": "tests/category_advisory_guard.mjs",
7219
+ "mode": "0000644",
7220
+ "sha256": "8fd7b066c264ddaeccaa2dfa9a3c1fe6204a943933a1161c6a235f45b40ab121"
7221
+ },
7167
7222
  {
7168
7223
  "path": "tests/cf_ruleset_preflight.mjs",
7169
7224
  "mode": "0000644",
@@ -7689,6 +7744,11 @@
7689
7744
  "mode": "0000644",
7690
7745
  "sha256": "7488d41a0d962f681f9482e4c06f518f38be824e8fa936b5bd7b1c1b70838c3d"
7691
7746
  },
7747
+ {
7748
+ "path": "tests/goal_category.mjs",
7749
+ "mode": "0000644",
7750
+ "sha256": "19e6987712231ac54e58c4b6658d12bb7df3d6681344bf358c38c1655243f058"
7751
+ },
7692
7752
  {
7693
7753
  "path": "tests/goal_cli.mjs",
7694
7754
  "mode": "0000644",
@@ -7747,7 +7807,7 @@
7747
7807
  {
7748
7808
  "path": "tests/goal_tier.mjs",
7749
7809
  "mode": "0000644",
7750
- "sha256": "e6a3114339989123d20f42f120f9fed52690fbc0230b10a99fa1fd0453acbfd2"
7810
+ "sha256": "7d8c1b8300c99be22b42ff25e8ce25e25b5af28cd29c314f9aee7ddee11fc0d5"
7751
7811
  },
7752
7812
  {
7753
7813
  "path": "tests/goal_wall_proof.mjs",
@@ -9199,6 +9259,11 @@
9199
9259
  "mode": "0000644",
9200
9260
  "sha256": "f9b1a7021c96c02a5eb667b2d10003b4c7e256fcaafbbc228b2585c710f1d162"
9201
9261
  },
9262
+ {
9263
+ "path": "tests/sound_player.mjs",
9264
+ "mode": "0000644",
9265
+ "sha256": "42398e730349be5f10e07eb3ec42aa47c9a024371f2f95f96802b990ff0e6f80"
9266
+ },
9202
9267
  {
9203
9268
  "path": "tests/sound_prefs.mjs",
9204
9269
  "mode": "0000644",
@@ -5,7 +5,7 @@ A **generated**, zero-dependency typed client for the Bongos API — produced fr
5
5
  by hand; it regenerates when the spec changes, so it can never drift from the routes.
6
6
 
7
7
  - API version: **v1** (served at `/api/bongos/v1`)
8
- - 315 operations across 51 resource groups
8
+ - 318 operations across 52 resource groups
9
9
 
10
10
  ## Install
11
11
 
@@ -323,6 +323,8 @@ function createClient(opts = {}) {
323
323
  patchGoalsId: (args) => request("PATCH", "/goals/{id}", { hasBody: true }, args),
324
324
  // POST /goals/{id}/archive — rank: metic+archon — POST /goals/:id/archive
325
325
  postGoalsIdArchive: (args) => request("POST", "/goals/{id}/archive", { hasBody: true }, args),
326
+ // PATCH /goals/{id}/category — rank: any-builder — PATCH /goals/:id/category
327
+ patchGoalsIdCategory: (args) => request("PATCH", "/goals/{id}/category", { hasBody: true }, args),
326
328
  // GET /goals/{id}/conflicts — rank: any-builder — GET /goals/:id/conflicts
327
329
  getGoalsIdConflicts: (args) => request("GET", "/goals/{id}/conflicts", { hasBody: false }, args),
328
330
  // POST /goals/{id}/conflicts/resolve — rank: any-builder — POST /goals/:id/conflicts/resolve
@@ -816,6 +818,12 @@ function createClient(opts = {}) {
816
818
  // GET /versions/progress — rank: public — GET /versions/progress
817
819
  getVersionsProgress: (args) => request("GET", "/versions/progress", { hasBody: false }, args),
818
820
  },
821
+ "workCategories": {
822
+ // GET /work-categories — rank: any-builder — GET /work-categories
823
+ getWorkCategories: (args) => request("GET", "/work-categories", { hasBody: false }, args),
824
+ // POST /work-categories — rank: metic+archon — POST /work-categories
825
+ postWorkCategories: (args) => request("POST", "/work-categories", { hasBody: true }, args),
826
+ },
819
827
  };
820
828
  }
821
829
 
@@ -322,6 +322,8 @@ function createClient(opts = {}) {
322
322
  patchGoalsId: (args) => request("PATCH", "/goals/{id}", { hasBody: true }, args),
323
323
  // POST /goals/{id}/archive — rank: metic+archon — POST /goals/:id/archive
324
324
  postGoalsIdArchive: (args) => request("POST", "/goals/{id}/archive", { hasBody: true }, args),
325
+ // PATCH /goals/{id}/category — rank: any-builder — PATCH /goals/:id/category
326
+ patchGoalsIdCategory: (args) => request("PATCH", "/goals/{id}/category", { hasBody: true }, args),
325
327
  // GET /goals/{id}/conflicts — rank: any-builder — GET /goals/:id/conflicts
326
328
  getGoalsIdConflicts: (args) => request("GET", "/goals/{id}/conflicts", { hasBody: false }, args),
327
329
  // POST /goals/{id}/conflicts/resolve — rank: any-builder — POST /goals/:id/conflicts/resolve
@@ -815,6 +817,12 @@ function createClient(opts = {}) {
815
817
  // GET /versions/progress — rank: public — GET /versions/progress
816
818
  getVersionsProgress: (args) => request("GET", "/versions/progress", { hasBody: false }, args),
817
819
  },
820
+ "workCategories": {
821
+ // GET /work-categories — rank: any-builder — GET /work-categories
822
+ getWorkCategories: (args) => request("GET", "/work-categories", { hasBody: false }, args),
823
+ // POST /work-categories — rank: metic+archon — POST /work-categories
824
+ postWorkCategories: (args) => request("POST", "/work-categories", { hasBody: true }, args),
825
+ },
818
826
  };
819
827
  }
820
828
 
@@ -251,7 +251,7 @@ export interface PostGoalsIdTransferRequest { builder_id?: StringifiableId }
251
251
  export interface PostGoalsIdTransferResponse { ok: boolean; goal: unknown; changed: boolean }
252
252
  export interface PostGoalsReorderRequest { version_id: string; ordered_ids: number[] }
253
253
  export interface PostGoalsReorderResponse { ok: boolean; goals: unknown }
254
- export interface PostGoalsRequest { version_id: string; title: string; subtitle?: string; description?: string; scope_modules?: string[]; succeeds_goal_id?: number }
254
+ export interface PostGoalsRequest { version_id: string; title: string; subtitle?: string; description?: string; scope_modules?: string[]; succeeds_goal_id?: number; category_id?: number }
255
255
  export interface PostGoalsResponse { ok: boolean; goal: unknown; succeeds_goal_id: unknown }
256
256
  export interface PostGovernmentAssignmentsRequest { builder_id: number; rank_key: string }
257
257
  export interface PostGovernmentBoardItemsItemIdVotesRequest { direction: string; reason_md?: string; section_key?: string }
@@ -609,6 +609,8 @@ export interface BongosClient {
609
609
  patchGoalsId(args?: RequestArgs): Promise<ApiResponse>;
610
610
  /** POST /goals/{id}/archive — rank: metic+archon */
611
611
  postGoalsIdArchive(args?: RequestArgs): Promise<PostGoalsIdArchiveResponse>;
612
+ /** PATCH /goals/{id}/category — rank: any-builder */
613
+ patchGoalsIdCategory(args?: RequestArgs): Promise<ApiResponse>;
612
614
  /** GET /goals/{id}/conflicts — rank: any-builder */
613
615
  getGoalsIdConflicts(args?: RequestArgs): Promise<GetGoalsIdConflictsResponse>;
614
616
  /** POST /goals/{id}/conflicts/resolve — rank: any-builder */
@@ -1102,6 +1104,12 @@ export interface BongosClient {
1102
1104
  /** GET /versions/progress — rank: public */
1103
1105
  getVersionsProgress(args?: RequestArgs): Promise<GetVersionsProgressResponse>;
1104
1106
  };
1107
+ "workCategories": {
1108
+ /** GET /work-categories — rank: any-builder */
1109
+ getWorkCategories(args?: RequestArgs): Promise<ApiResponse>;
1110
+ /** POST /work-categories — rank: metic+archon */
1111
+ postWorkCategories(args?: RequestArgs): Promise<ApiResponse>;
1112
+ };
1105
1113
  }
1106
1114
  export declare function createClient(opts?: ClientOptions): BongosClient;
1107
1115
  export default createClient;
@@ -319,6 +319,8 @@ export function createClient(opts = {}) {
319
319
  patchGoalsId: (args) => request("PATCH", "/goals/{id}", { hasBody: true }, args),
320
320
  // POST /goals/{id}/archive — rank: metic+archon — POST /goals/:id/archive
321
321
  postGoalsIdArchive: (args) => request("POST", "/goals/{id}/archive", { hasBody: true }, args),
322
+ // PATCH /goals/{id}/category — rank: any-builder — PATCH /goals/:id/category
323
+ patchGoalsIdCategory: (args) => request("PATCH", "/goals/{id}/category", { hasBody: true }, args),
322
324
  // GET /goals/{id}/conflicts — rank: any-builder — GET /goals/:id/conflicts
323
325
  getGoalsIdConflicts: (args) => request("GET", "/goals/{id}/conflicts", { hasBody: false }, args),
324
326
  // POST /goals/{id}/conflicts/resolve — rank: any-builder — POST /goals/:id/conflicts/resolve
@@ -812,6 +814,12 @@ export function createClient(opts = {}) {
812
814
  // GET /versions/progress — rank: public — GET /versions/progress
813
815
  getVersionsProgress: (args) => request("GET", "/versions/progress", { hasBody: false }, args),
814
816
  },
817
+ "workCategories": {
818
+ // GET /work-categories — rank: any-builder — GET /work-categories
819
+ getWorkCategories: (args) => request("GET", "/work-categories", { hasBody: false }, args),
820
+ // POST /work-categories — rank: metic+archon — POST /work-categories
821
+ postWorkCategories: (args) => request("POST", "/work-categories", { hasBody: true }, args),
822
+ },
815
823
  };
816
824
  }
817
825
 
@@ -0,0 +1,97 @@
1
+ # 0192 — A work category orients a human and authorises nothing
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-08-25
5
+ - **Tasks:** [#1003275](https://cloudbongos.com/builders#/task/1003275) (this decision + the storage/API), with the hall surfaces in [#1003276](https://cloudbongos.com/builders#/task/1003276) and the assignment path in [#1003277](https://cloudbongos.com/builders#/task/1003277).
6
+ - **Goal:** [#1000072](https://cloudbongos.com/builders#/goal/1000072) — Human legibility, criterion `legibility-categories`.
7
+ - **Constrains, does not amend:** [ADR 0086](<redacted>.md) — the real work hierarchy. This decision adds a label *beside* that hierarchy and forbids it from behaving like part of it.
8
+
9
+ ## 1. The problem
10
+
11
+ A person scanning the goal board cannot tell at a glance which goals are about
12
+ money, which are about the walls, and which are about the hall. Each goal's own
13
+ words say so — but only if you read all thirty-eight of them. The owner asked for
14
+ one word per goal that answers it, and named the set: Builders Hall, Context
15
+ Management, Government (Decision Making), Modules, Security, Economics, Legal,
16
+ User/ABI Experience.
17
+
18
+ That is the easy half. The hard half is the constraint the owner attached to it,
19
+ twice: this organisation "has no hierarchical purpose", and these categories have
20
+ "no effect on the creation and agentic management of these elements".
21
+
22
+ ## 2. Why the constraint needs a decision record and not a comment
23
+
24
+ The system **already has** a hierarchy — Project → Version → Goal → Criteria →
25
+ Task ([ADR 0086](<redacted>.md)) — and that one is
26
+ load-bearing: the `scope_modules` wall, per-task rank floors, and goal membership
27
+ all refuse real work. Goal 1000072's own prose predicts what happens next: *"A
28
+ second thing that LOOKS like a hierarchy will be mistaken for one within a
29
+ release."*
30
+
31
+ The mistake does not arrive as a decision anybody announces. It arrives as one
32
+ plausible line inside a gate — *skip Legal goals in the claimable feed*, *require
33
+ Archon for Security ones* — and from that moment work is refused because of a
34
+ label nobody meant as a permission. Nothing in a comment can refuse that line.
35
+
36
+ ## 3. Decision
37
+
38
+ **A category is advisory. It orients a human and steers Claude; it authorises
39
+ nothing.**
40
+
41
+ Concretely:
42
+
43
+ 1. **No decision surface reads it.** Not authority, not claim eligibility, not the
44
+ claimable feed, not dependency satisfaction. Enforced by a fitness check,
45
+ `scripts/gds/category-advisory-guard.js`, which scans every such file with
46
+ comments stripped — so a gate may *cite* this rule but may never branch on it.
47
+ 2. **The guard cannot pass vacuously.** It also asserts the category surface still
48
+ exists (migration, module, route). A guard whose subject was renamed away
49
+ passes silently and reads as proof — the assurance-of-assurance finding from
50
+ the [ADR 0022](0022-secrets-policy.md) audit that produced
51
+ `control-manifest.js`.
52
+ 3. **Uncategorised is a legal state.** `goals.category_id` is nullable with no
53
+ default, and no write requires it. The criterion is explicit that Claude
54
+ failing to place a goal is *"a designed prompt, not an error"*: the human is
55
+ asked to name a new category, and asking is not refusing.
56
+ 4. **A task stores no category.** It inherits its goal's through `tasks.goal_id`.
57
+ The criterion's own wording is "every goal (and task, **by inheritance**)", and
58
+ a stored copy would be a second answer to one question with no way to know
59
+ which is right.
60
+ 5. **The set is data, not an enum.** A runtime-extensible set cannot be a `CHECK`
61
+ constraint or a pg enum without a migration per addition — and it must not be a
62
+ free-text column, or two spellings of "Security" stop it being a set at all.
63
+ Retiring is `active = false`, never a `DELETE`, so a goal already carrying a
64
+ category keeps rendering its label.
65
+ 6. **The rank pins are a tripwire.** `GET /work-categories` is any-builder (a
66
+ vocabulary a client cannot read is a vocabulary a client hard-codes),
67
+ `POST /work-categories` sits at the same permission as authoring a goal
68
+ (extending the project's words is a planning act), and
69
+ `PATCH /goals/:id/category` rides the same owner|manager|Archon in-handler gate
70
+ as the prose edit. If one of these ever needs a *wider* gate than its
71
+ neighbours, that is the signal the label has grown into a permission.
72
+
73
+ ## 4. What was deliberately not built
74
+
75
+ - **No per-category page or filtered navigation view.** A chip is a label; a
76
+ drill-down destination is a tier. Building the destination is how an advisory
77
+ label becomes the second hierarchy in practice regardless of what the code
78
+ gates on.
79
+ - **No category on a task, no category-scoped permissions, no category-ordered
80
+ work feed.**
81
+
82
+ ## 5. If this ever needs to change
83
+
84
+ Making a category gate something is an **owner decision** and needs its own ADR
85
+ before the guard is relaxed. The guard's failure message says so, because the
86
+ person who trips it is the person who needs to read it.
87
+
88
+ ## 6. Consequences
89
+
90
+ - A human can scan the goal board by kind, which is the whole point of goal
91
+ 1000072.
92
+ - One more fitness check to maintain, and a `GATE_FILES` list that must grow when
93
+ a new decision surface lands. That maintenance *is* the mechanism — a new gate
94
+ either gets added to the list or is not a gate.
95
+ - The false-positive posture is deliberate: the scanner errs toward keeping text
96
+ when comment-stripping is ambiguous, because a guard that fails loudly is safer
97
+ than one that fails quietly.
@@ -268,5 +268,6 @@ This keeps the decision history honest and traceable.
268
268
  | 0190 | [**A changed project setting reaches a running instance by an in-place env patch and a restart — never a template re-write** ([task 1003140](https://cloudbongos.com/builders#/task/1003140), goal 1000058). Task 1003139 made a NEW instance read `platform_visibility` / `joinability` from the web.env the runner writes; a RUNNING instance loads its branding pack once, memoized and deep-frozen for the process lifetime, so a changed value could not reach it at all — the settings were write-only state on the platform. Decision: `PATCH /provisioning/instances/:id/settings` writes the platform row first, then for an ACTIVE instance on a shape the runner can reach enqueues a `settings-apply` intent (migration provisioning_010); the control-plane runner reads the instance's web.env as it stands, upserts ONLY the two settings lines (`upsertEnvVars`), and RESTARTS the service. The in-place patch is the load-bearing choice: the natural move — re-running `webEnvBody` as domain-attach does — regenerates the file from the row, and the row does not know the OAuth client secret or the App private key that `placeManifestCreds` placed later, so a template re-write silently blanks sign-in on the very instance the owner just touched. `SETTINGS_APPLY_FROM = {active}` because a pre-active instance gets the same values at standup and a torn-down one has nothing to restart (the provision intent cannot stand in — it excludes `active`, task 1002691); `SETTINGS_APPLY_SHAPES = {co-tenant, standalone}` because a dedicated droplet has no post-hoc exec path, and a push that cannot land is refused at the route AND the runner rather than reported. The response is a receipt, not a claim: it says the restart is coming and that the instance keeps its current setting until then, and the read-back lives on the instance's own public `GET /instance` manifest, whose `brand.project` now carries the resolved policy (`clientBranding` allow-list widened deliberately). Rejected: the template re-write; re-provisioning as the push; skipping the restart (impossible while the pack is memoized); claiming the new state on PATCH return.](<redacted>.md) | provisioning / settings channel |
269
269
 
270
270
  | 0191 | [**Majority, and an author who may vote but never decide alone** ([task 1003273](https://cloudbongos.com/builders#/task/1003273); diagnosis and companion in [task 1003271](https://cloudbongos.com/builders#/task/1003271)). Supersedes ADR 0175 §3's pass-rule set and §§7/11's flat anti-self rule. The owner reported voting as broken; nothing was broken. Nine items sat open under `first_ratifier` with four archons active — any one of three non-authors could have ratified any of them in a click — but every item was authored by the one person looking at them, and ADR 0175's anti-self rule was enforced by rendering NO BALLOT, so from the owner's seat the feature did not exist. The lesson: a rule enforced by removing the control is indistinguishable from a broken feature. Two decisions. (1) `majority` joins PASS_RULES: strictly more than half of the resolved MEMBERSHIP must vote yes — of the membership, not of the votes cast, so low turnout cannot quietly pass things; a tie is not a majority. The three rules are NOT a ladder of strictness, they differ on WHAT SILENCE MEANS — `first_ratifier` and `consent` pass an unvoted item, `majority` returns it — which is the axis that matters and now the comment in config.js. (2) An author MAY vote and may NEVER DECIDE ALONE: the rule moved from refusing the cast to the two places something is actually decided — `shouldCloseAfterVote` needs a non-author vote under every rule, and the majority tally needs a non-author YES. Their yes still counts toward the numerator; their `no` is excluded from the objection test (else silence would be an author's safest act); a self-cast earns no karma. The ADR 0175 §7 amendment carve-out survives as the author rule's SCOPE, not an exception: `authorRuleApplies` is full_idea-only, so a sole Archon still ratifies their own amendment. Also records why the CLOCK is a consequence of majority and not an independent fix — `tallyOutcome` passes on ZERO votes, so adding a deadline under `first_ratifier` would have closed all nine items as ratified-unread, paying karma and credits on ideas nobody read; `BOARD_DEFAULTS.window_minutes` therefore stays `null` in the neutral core. States the cost plainly: three affirmative votes are needed on a four-member board, and a solo instance still cannot ratify its own ideas. Discord stays outbound-only (ADR 0175 §8 re-affirmed).](<redacted>.md) | government / board room |
271
+ | 0192 | [**A work category orients a human and authorises nothing** ([task 1003275](https://cloudbongos.com/builders#/task/1003275), goal 1000072, criterion `legibility-categories`). A goal gains a human CATEGORY from an owner-named set of eight (Builders Hall, Context Management, Government (Decision Making), Modules, Security, Economics, Legal, User/ABI Experience) so a person scanning the board can tell at a glance which goals are about money, which about the walls, which about the hall — today each goal's own words say so, but only if you read all thirty-eight. The DECISION is the constraint, not the feature: the owner said twice that this organisation has no hierarchical purpose and no effect on the agentic management of the work, and the system ALREADY has a load-bearing hierarchy (ADR 0086 — the scope_modules wall, rank floors, goal membership). The goal's own prose predicts the failure: a second thing that looks like a hierarchy gets mistaken for one within a release, and the mistake arrives as ONE plausible line in a gate ('skip Legal goals in the feed'), after which work is refused because of a label nobody meant as a permission. So: no authority, claim-eligibility, feed or dependency surface may read a category — enforced by the `scripts/gds/category-advisory-guard.js` fitness check, which scans every decision surface with comments stripped (a gate may CITE the rule, never branch on it) AND asserts the feature still exists, so a renamed-away subject fails loudly instead of passing vacuously (the ADR 0022 assurance-of-assurance finding). Uncategorised is LEGAL — nullable, no default, required by no write — because the criterion is explicit that Claude failing to place a goal is a designed prompt, not an error. A task stores no category and INHERITS its goal's through `tasks.goal_id`; a stored copy would be a second answer to one question. The set is a table, not an enum (runtime-extensible without a migration per addition) and retiring is `active = false`, never a DELETE, so a goal keeps rendering a label someone tidied away. The three route-rank pins are a tripwire: if one ever needs a WIDER gate than its neighbours, the label has grown into a permission. Deliberately NOT built: a per-category page or filtered navigation view — a chip is a label, a drill-down destination is a tier. Making a category gate anything is an owner decision and needs its own ADR first.](<redacted>.md) | GDS / goals |
271
272
 
272
273
  > ⚠️ **Numbering collision (known):** two ADRs share `0008`. The numbers were assigned in parallel sessions before the lock could be enforced. The files are kept under their existing filenames to preserve git history, but the table disambiguates them as `0008-a` / `0008-b`. New ADRs should resume at `0040` to avoid further collisions. (`0024` is also a real on-disk duplicate: the cloneable-repo/local-first-memory ADR and the multi-agent-system-architecture ADR were both numbered `0024` in parallel; both keep their filenames, disambiguated as `0024-a` / `0024-b` like `0008`. Likewise `0025` ended up with two on-disk files — the offsite-backup ADR and the structured-criterion↔task-link ADR — both kept under their existing filenames and disambiguated as `0025-a` / `0025-b`; separately, the BFG ADR had also claimed `0025` on 2026-05-30 and yielded to `0026`. A third happened 2026-06-06: `0036` was claimed in parallel by the builders-subdomain ADR ([#726](https://example.com/builders#/task/726)) and this Discord-channels ADR ([#727](https://example.com/builders#/task/727)); the Discord-channels ADR yielded to `0037`. A fourth landed 2026-06-10: `0044` was claimed in parallel by the palette-replacement ADR and the per-box game-preview ADR ([#897](https://example.com/builders#/task/897)); both keep their filenames, disambiguated as `0044-a` / `0044-b` like `0008`. A fifth was caught pre-land 2026-06-13: `0050` was claimed in parallel by the device-flow-admission ADR ([#1000](https://example.com/builders#/task/1000)) and the sandbox-for-everyone ADR ([#1001](https://example.com/builders#/task/1001)); the latter yielded to `0052` before merging, so there is no `0050` duplicate. A sixth was caught pre-land 2026-06-21: the tool-agnostic-design-layer ADR ([#1375](https://example.com/builders#/task/1375)) first collided at `0079` with the value-gated-autonomous-engagement ADR, then at `0080` with the llm-cache-api-transport ADR (both part of the 0075–0080 LLM-cost-reduction series, landed on `main` in parallel); it yielded to `0081` before merging, so there is no `0079`/`0080` design-layer duplicate. A seventh landed 2026-06-23: `0084` was claimed in parallel by the auto-derived-per-task-rank-floor ADR and the remove-default-spend-cap ADR (task 1499); the rank-floor ADR landed on `main` first, so the spend-cap ADR yielded to `0085`, leaving no `0084` duplicate. An eighth landed 2026-06-27: `0095` was claimed in parallel by the borrowed-memory-and-retrieval-concepts ADR (PR #459, landed first) and the cross-agent-context-management ADR ([#1651](https://example.com/builders#/task/1651), PR #460); both keep their filenames, disambiguated as `0095-a` / `0095-b` like `0008`. These two are companions — `0095-a` adopts the memory/retrieval concepts and explicitly defers the grep-vs-semantic question to `0095-b`.)