@bongos/core 1.19.919 → 1.19.920

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 (36) hide show
  1. package/.bongos-core.json +46 -41
  2. package/docs/module-api-changelog.md +2 -0
  3. package/modules/hall-ui/public/atlas.html +1 -1
  4. package/modules/hall-ui/public/blockers.html +1 -1
  5. package/modules/hall-ui/public/board-room.html +1 -1
  6. package/modules/hall-ui/public/collab.html +1 -1
  7. package/modules/hall-ui/public/copy-desk.html +1 -1
  8. package/modules/hall-ui/public/deploy.html +1 -1
  9. package/modules/hall-ui/public/diagrams.html +1 -1
  10. package/modules/hall-ui/public/drachmae.html +1 -1
  11. package/modules/hall-ui/public/gate.html +1 -1
  12. package/modules/hall-ui/public/goals.html +1 -1
  13. package/modules/hall-ui/public/government.html +1 -1
  14. package/modules/hall-ui/public/idea.html +1 -1
  15. package/modules/hall-ui/public/ideas.html +1 -1
  16. package/modules/hall-ui/public/index.html +1 -1
  17. package/modules/hall-ui/public/modules.html +1 -1
  18. package/modules/hall-ui/public/people.html +1 -1
  19. package/modules/hall-ui/public/primer.html +1 -1
  20. package/modules/hall-ui/public/profile.html +1 -1
  21. package/modules/hall-ui/public/project-settings.html +1 -1
  22. package/modules/hall-ui/public/ranks.html +1 -1
  23. package/modules/hall-ui/public/roadmap.html +1 -1
  24. package/modules/hall-ui/public/roster.html +1 -1
  25. package/modules/hall-ui/public/sessions.html +1 -1
  26. package/modules/hall-ui/public/settings.html +1 -1
  27. package/modules/hall-ui/public/studio.html +1 -1
  28. package/modules/hall-ui/public/task.html +1 -1
  29. package/modules/hall-ui/public/watch.html +1 -1
  30. package/modules/hall-ui/public/work.html +1 -1
  31. package/package-lock.json +2 -2
  32. package/package.json +1 -1
  33. package/src/bongos/routes/me.js +9 -1
  34. package/src/module-api.js +1 -1
  35. package/tests/core_update_banner.mjs +28 -0
  36. package/tests/nav_permission_atoms.mjs +110 -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.919",
6
- "core_contract": "1.19.919",
7
- "source_commit": "bc2cae9667f75f9a2df301fc776a5e0b79451325",
5
+ "core_version": "1.19.920",
6
+ "core_contract": "1.19.920",
7
+ "source_commit": "f4047b39eee17385567cbe07c45821af322b8f51",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-20T19:06:48.075Z",
9
+ "built_at": "2026-09-20T19:16:17.763Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 508,
13
13
  "agent_docs_stubbed": 26,
14
- "functional_verbatim": 2302,
14
+ "functional_verbatim": 2303,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2836,
20
- "tree_sha256": "fcb4d5d17d74106fc9ba7d49143956885bb9c5dadc48c2e1a3aa39ebcc76a851",
19
+ "file_count": 2837,
20
+ "tree_sha256": "0aa0c6f824621e1dd477c005962f961b6c9e71756b59f6bf383b122152964b5b",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2662,7 +2662,7 @@
2662
2662
  {
2663
2663
  "path": "docs/module-api-changelog.md",
2664
2664
  "mode": "0000644",
2665
- "sha256": "e15b7bc78c0b64fc27725df643ec05f20e047322623ac182f3614fea341521bb"
2665
+ "sha256": "760c792a9c7d16c1b9fc1b77eaf915baf0d9e652d04c84571118c521bbde0700"
2666
2666
  },
2667
2667
  {
2668
2668
  "path": "docs/modules-contract.md",
@@ -4627,7 +4627,7 @@
4627
4627
  {
4628
4628
  "path": "modules/hall-ui/public/atlas.html",
4629
4629
  "mode": "0000644",
4630
- "sha256": "5be54f6e574fd171cfccad405e006873ff110206777c95bd6f482befe0e9c600"
4630
+ "sha256": "7a18c8a53bf17b4eb5fecdd108fefa518d917a297651dae32bda31ef650f0d0c"
4631
4631
  },
4632
4632
  {
4633
4633
  "path": "modules/hall-ui/public/atlas.js",
@@ -4647,7 +4647,7 @@
4647
4647
  {
4648
4648
  "path": "modules/hall-ui/public/blockers.html",
4649
4649
  "mode": "0000644",
4650
- "sha256": "efe8931f650fcb1e98194f34cf0f4cc11e570e565c51bae2223beacb4307ae94"
4650
+ "sha256": "a41140a699e8d0a12cdeb22804f21a33c96ae0467f71260eb6a06ddce26618ee"
4651
4651
  },
4652
4652
  {
4653
4653
  "path": "modules/hall-ui/public/blockers.js",
@@ -4667,7 +4667,7 @@
4667
4667
  {
4668
4668
  "path": "modules/hall-ui/public/board-room.html",
4669
4669
  "mode": "0000644",
4670
- "sha256": "362381c40f1011455cab89d473108eb3f41747fe46cde62d932744fc2d2512f0"
4670
+ "sha256": "52ac2cac716ea87e028437d132d2b088527aea365ed8093381c40e194d014d80"
4671
4671
  },
4672
4672
  {
4673
4673
  "path": "modules/hall-ui/public/board-room.js",
@@ -4707,7 +4707,7 @@
4707
4707
  {
4708
4708
  "path": "modules/hall-ui/public/collab.html",
4709
4709
  "mode": "0000644",
4710
- "sha256": "9876929d0ce989d12dadcdfa6da5cd93ca660e0b291a24a467a920632245dd65"
4710
+ "sha256": "3255ef7a523ec831ef1fded9c9ef18fcc6298dc81a5c6510233cc42a247af613"
4711
4711
  },
4712
4712
  {
4713
4713
  "path": "modules/hall-ui/public/collab.js",
@@ -4727,7 +4727,7 @@
4727
4727
  {
4728
4728
  "path": "modules/hall-ui/public/copy-desk.html",
4729
4729
  "mode": "0000644",
4730
- "sha256": "96b2dac4c916f1912bd5b5b954bbdea598581e4213cc905ea2ee4db7583d115d"
4730
+ "sha256": "16eba28547728612dcbcb9563021cf01a6ef5fe6a207fd01cf8744017fac3f67"
4731
4731
  },
4732
4732
  {
4733
4733
  "path": "modules/hall-ui/public/copy-desk.js",
@@ -4767,7 +4767,7 @@
4767
4767
  {
4768
4768
  "path": "modules/hall-ui/public/deploy.html",
4769
4769
  "mode": "0000644",
4770
- "sha256": "1dabc13d4c3dc59e10918348e0607ab7162522ba91d0269e3d4db3fbf5f0001e"
4770
+ "sha256": "939d7544560d07848172df49700bdbb6f13d9e8ccb04040cf3f85a2eafd1f9bf"
4771
4771
  },
4772
4772
  {
4773
4773
  "path": "modules/hall-ui/public/deploy.js",
@@ -4792,7 +4792,7 @@
4792
4792
  {
4793
4793
  "path": "modules/hall-ui/public/diagrams.html",
4794
4794
  "mode": "0000644",
4795
- "sha256": "8586c32beb1d116a7d37505328b077efc3499315c73b02727d28b9f82cd78eb3"
4795
+ "sha256": "85d09cb964c7938e7634e8ec15bb31229ec2ae3bfbe6c3152ded85b10e5f2122"
4796
4796
  },
4797
4797
  {
4798
4798
  "path": "modules/hall-ui/public/diagrams.states.json",
@@ -4807,7 +4807,7 @@
4807
4807
  {
4808
4808
  "path": "modules/hall-ui/public/drachmae.html",
4809
4809
  "mode": "0000644",
4810
- "sha256": "89bd28f00b4f4849b8789f8d5b4d5eb50705fddeb65f1675bb0d88fc43fd1859"
4810
+ "sha256": "357f766069e13f836e5d229451b0644579bb7d5f2db9228317a513dd46024508"
4811
4811
  },
4812
4812
  {
4813
4813
  "path": "modules/hall-ui/public/drachmae.js",
@@ -4832,7 +4832,7 @@
4832
4832
  {
4833
4833
  "path": "modules/hall-ui/public/gate.html",
4834
4834
  "mode": "0000644",
4835
- "sha256": "0b07c5a358d50b8537e303fc8fc84a2ae5100c09df7af1e15b96caa5a677aa76"
4835
+ "sha256": "b8581d242c8b228070b719020222a86c6049de4004881d059636fa27c6740213"
4836
4836
  },
4837
4837
  {
4838
4838
  "path": "modules/hall-ui/public/gate.js",
@@ -4872,7 +4872,7 @@
4872
4872
  {
4873
4873
  "path": "modules/hall-ui/public/goals.html",
4874
4874
  "mode": "0000644",
4875
- "sha256": "ecbd2c50f6dc00f9243417f27ed40603498cd7a8178e6701a1151d9188baff2c"
4875
+ "sha256": "47a148b1c67e6d81fcd032b21dbd988fffbff6602a34c92c69cfb0114411e867"
4876
4876
  },
4877
4877
  {
4878
4878
  "path": "modules/hall-ui/public/goals.js",
@@ -4892,7 +4892,7 @@
4892
4892
  {
4893
4893
  "path": "modules/hall-ui/public/government.html",
4894
4894
  "mode": "0000644",
4895
- "sha256": "83338cc027af8f146960f58c3398dd28c0218c9671aad2327eef6d81588f85b8"
4895
+ "sha256": "b275b437bbc25dec17f2ff33d3d4673dae4bc9e3bf6a08db538405267303d731"
4896
4896
  },
4897
4897
  {
4898
4898
  "path": "modules/hall-ui/public/government.js",
@@ -4932,7 +4932,7 @@
4932
4932
  {
4933
4933
  "path": "modules/hall-ui/public/idea.html",
4934
4934
  "mode": "0000644",
4935
- "sha256": "4edb9800c4b7ae528f66dfcfb89f4d8ee765b1923303d29c57984cde933986a1"
4935
+ "sha256": "495cabec6824445cb19363870f7893bc70c82e8ef6427c6a0acb10a9099cf384"
4936
4936
  },
4937
4937
  {
4938
4938
  "path": "modules/hall-ui/public/idea.js",
@@ -4952,7 +4952,7 @@
4952
4952
  {
4953
4953
  "path": "modules/hall-ui/public/ideas.html",
4954
4954
  "mode": "0000644",
4955
- "sha256": "c59e5f293b4dcabd01e2cd17c983b42184bb17468654888f4adf2243a506623b"
4955
+ "sha256": "7519ec60796cc13455a706b68a28df4906f5eb97c6c383402602d6724dc109c3"
4956
4956
  },
4957
4957
  {
4958
4958
  "path": "modules/hall-ui/public/ideas.js",
@@ -4967,7 +4967,7 @@
4967
4967
  {
4968
4968
  "path": "modules/hall-ui/public/index.html",
4969
4969
  "mode": "0000644",
4970
- "sha256": "a1cb9e9d3378c6a8c0e78add088bc8f8a705da26b83a6f6ebef8d5136f44b6c9"
4970
+ "sha256": "cb27c9d4a53bfc99e31cf235f7809330c762145228492cb21cfa1a7df6bb0b69"
4971
4971
  },
4972
4972
  {
4973
4973
  "path": "modules/hall-ui/public/index.states.json",
@@ -4987,7 +4987,7 @@
4987
4987
  {
4988
4988
  "path": "modules/hall-ui/public/modules.html",
4989
4989
  "mode": "0000644",
4990
- "sha256": "493de4531267d2b0b0ce268cc5998e33d54481442af619771f05dc27182e6f18"
4990
+ "sha256": "ad62097f5917f2b7bf5d97506f59f684525c4704b99c05dc0b34225d248270ee"
4991
4991
  },
4992
4992
  {
4993
4993
  "path": "modules/hall-ui/public/modules.js",
@@ -5022,7 +5022,7 @@
5022
5022
  {
5023
5023
  "path": "modules/hall-ui/public/people.html",
5024
5024
  "mode": "0000644",
5025
- "sha256": "8df57c42bbcf1309bce63228b63c6f2ab2f8fbf70cfd6dd9e4228b4d5b7e09b4"
5025
+ "sha256": "8e4eb3cc38902efc9df68fb59ff3d89b73c8544e64f2d1ff6de758a40ca58de1"
5026
5026
  },
5027
5027
  {
5028
5028
  "path": "modules/hall-ui/public/people.js",
@@ -5042,7 +5042,7 @@
5042
5042
  {
5043
5043
  "path": "modules/hall-ui/public/primer.html",
5044
5044
  "mode": "0000644",
5045
- "sha256": "988261501fd425e7e94cd8d236ffbab7a42d6d73f65bac6b33a6b8bf4f7fe7a1"
5045
+ "sha256": "7f4a81acd3cfb689cb928916cd22cc2f059865f0adf1c0d86b5bd24ebee6cae4"
5046
5046
  },
5047
5047
  {
5048
5048
  "path": "modules/hall-ui/public/primer.js",
@@ -5082,7 +5082,7 @@
5082
5082
  {
5083
5083
  "path": "modules/hall-ui/public/profile.html",
5084
5084
  "mode": "0000644",
5085
- "sha256": "b169bf612448988b8f114c2b1702463b49f643afe20b29cbb43a7b5bcb18b549"
5085
+ "sha256": "a8bea342986ab3fd751ce8a6c026ddbd63d3982ee1e588611e506e26d6822198"
5086
5086
  },
5087
5087
  {
5088
5088
  "path": "modules/hall-ui/public/profile.js",
@@ -5102,7 +5102,7 @@
5102
5102
  {
5103
5103
  "path": "modules/hall-ui/public/project-settings.html",
5104
5104
  "mode": "0000644",
5105
- "sha256": "d96bd79671787688cbfb58633236b6287a06356767bc53a8b9f96294e2850335"
5105
+ "sha256": "dbc1290d64354c12931779db84645637b118356df8a5173006c9fe19865e7116"
5106
5106
  },
5107
5107
  {
5108
5108
  "path": "modules/hall-ui/public/project-settings.js",
@@ -5112,7 +5112,7 @@
5112
5112
  {
5113
5113
  "path": "modules/hall-ui/public/ranks.html",
5114
5114
  "mode": "0000644",
5115
- "sha256": "07396f9135db32359405d5c290549d0d6fb3d2c3a799408c1a268afbdd72ba3c"
5115
+ "sha256": "96fc146b06eed3cf008474d4a749ce153d44e6ffdc339cbfb0f24e80cf3473ea"
5116
5116
  },
5117
5117
  {
5118
5118
  "path": "modules/hall-ui/public/ranks.js",
@@ -5142,7 +5142,7 @@
5142
5142
  {
5143
5143
  "path": "modules/hall-ui/public/roadmap.html",
5144
5144
  "mode": "0000644",
5145
- "sha256": "f4d80840fc87ca128a193d4a68bfe8c8cce1df25c5b490ee7cc5166ab1a1b381"
5145
+ "sha256": "e23079e6c99511315ff6cc5be3332177e0f9d2ce02834b71f7cb0c800ed0bd6b"
5146
5146
  },
5147
5147
  {
5148
5148
  "path": "modules/hall-ui/public/roadmap.js",
@@ -5162,7 +5162,7 @@
5162
5162
  {
5163
5163
  "path": "modules/hall-ui/public/roster.html",
5164
5164
  "mode": "0000644",
5165
- "sha256": "4edbfb63eb08a0c0e96efec59f6559b62bc4bed24eaebe3ef4169382a8e1124c"
5165
+ "sha256": "efdbdf33e10be17474c5d46e633865834b41abfbc54f02a0bca9fe54d8642024"
5166
5166
  },
5167
5167
  {
5168
5168
  "path": "modules/hall-ui/public/roster.js",
@@ -5182,7 +5182,7 @@
5182
5182
  {
5183
5183
  "path": "modules/hall-ui/public/sessions.html",
5184
5184
  "mode": "0000644",
5185
- "sha256": "3a3a17d9623e601c2c45d9c87f34ad3b29c7ba495658dda9900086ecd47a64de"
5185
+ "sha256": "2627f0cdd7c7bda698f1f4c37e5acfa0278e665dcbf1bd189d538fc111c15e2e"
5186
5186
  },
5187
5187
  {
5188
5188
  "path": "modules/hall-ui/public/sessions.js",
@@ -5222,7 +5222,7 @@
5222
5222
  {
5223
5223
  "path": "modules/hall-ui/public/settings.html",
5224
5224
  "mode": "0000644",
5225
- "sha256": "6c3e1dd29a8822091e3dee20598578712bf74d67936465fd9d3c15a5855a996e"
5225
+ "sha256": "03608a940bab5b723ee848d05c355f61d1d129efe0c7a1f68e2577d640de8b68"
5226
5226
  },
5227
5227
  {
5228
5228
  "path": "modules/hall-ui/public/settings.js",
@@ -5252,7 +5252,7 @@
5252
5252
  {
5253
5253
  "path": "modules/hall-ui/public/studio.html",
5254
5254
  "mode": "0000644",
5255
- "sha256": "ea0051dba4a0e4e2ebde1ab1dcaeaedac697d41f89df35526286beb018553025"
5255
+ "sha256": "5dcde0e7fc83c9d0f861530bbfa094977d3e10a2efd53c0d7eea7b43d8036a53"
5256
5256
  },
5257
5257
  {
5258
5258
  "path": "modules/hall-ui/public/studio.js",
@@ -5287,7 +5287,7 @@
5287
5287
  {
5288
5288
  "path": "modules/hall-ui/public/task.html",
5289
5289
  "mode": "0000644",
5290
- "sha256": "5d8a37f1d69506824339063f9f9f3b1af1f9569ca28738e638eaa98a19e86ea8"
5290
+ "sha256": "e239a66b0dacef8799eb0402a26d4938fb58c9ea019bcf5c7ede01286ae67fff"
5291
5291
  },
5292
5292
  {
5293
5293
  "path": "modules/hall-ui/public/task.js",
@@ -5312,7 +5312,7 @@
5312
5312
  {
5313
5313
  "path": "modules/hall-ui/public/watch.html",
5314
5314
  "mode": "0000644",
5315
- "sha256": "9e1dff41ebd8f0872d45057edf378e3e587ae68499973cc8bafa97bb92c4d61d"
5315
+ "sha256": "c235d89f45fb12cfbefe6cf22000de2804f3b4c9887419c981d220695985188f"
5316
5316
  },
5317
5317
  {
5318
5318
  "path": "modules/hall-ui/public/watch.js",
@@ -5332,7 +5332,7 @@
5332
5332
  {
5333
5333
  "path": "modules/hall-ui/public/work.html",
5334
5334
  "mode": "0000644",
5335
- "sha256": "f8e72cd4d322554ac6ebdf49dcb48b001e61a09bdbfb62390b409fc0c867b0dd"
5335
+ "sha256": "819ca20923783af7b7ecae16b0a44e42f460d0f7af5179f78f9ee5f41220574e"
5336
5336
  },
5337
5337
  {
5338
5338
  "path": "modules/hall-ui/public/work.js",
@@ -7872,12 +7872,12 @@
7872
7872
  {
7873
7873
  "path": "package-lock.json",
7874
7874
  "mode": "0000644",
7875
- "sha256": "c74ec04aa7a18c9c94e42afecaac34a114331a3769e1df4af00e192555155f66"
7875
+ "sha256": "c4064939e0c8b7f6452d0348427109332caa61e8759e40a60ded3dc4f704602a"
7876
7876
  },
7877
7877
  {
7878
7878
  "path": "package.json",
7879
7879
  "mode": "0000644",
7880
- "sha256": "fc839d817a279254882dd33532bbbb2831174de15f28b3c72a3830021e8ded5f"
7880
+ "sha256": "840b50ec98565b70f1550ea34aa8813bf5e8cbb8ae7dece18ed1e45abe6ad608"
7881
7881
  },
7882
7882
  {
7883
7883
  "path": "public-docs/index.html",
@@ -9812,7 +9812,7 @@
9812
9812
  {
9813
9813
  "path": "src/bongos/routes/me.js",
9814
9814
  "mode": "0000644",
9815
- "sha256": "4be2b550184af2541f20c5b1a40cce4b61beb2eb676d4de7d2c05b0d9a92dfd4"
9815
+ "sha256": "d53906b0df21f0b645bc0553d2913e8b60bec2b4cbe7bb1f9c5a71ea8de27f44"
9816
9816
  },
9817
9817
  {
9818
9818
  "path": "src/bongos/routes/modules.js",
@@ -9877,7 +9877,7 @@
9877
9877
  {
9878
9878
  "path": "src/module-api.js",
9879
9879
  "mode": "0000644",
9880
- "sha256": "e5fb0cc646c7821da2c99ceb43ee9a9214399fb81b1ee26e1c9bc84ff5bff380"
9880
+ "sha256": "111908611a709491751fd92695758cca7125efc22bf5e68e908a8c4f45cf6301"
9881
9881
  },
9882
9882
  {
9883
9883
  "path": "src/module-loader/catalog.js",
@@ -10782,7 +10782,7 @@
10782
10782
  {
10783
10783
  "path": "tests/core_update_banner.mjs",
10784
10784
  "mode": "0000644",
10785
- "sha256": "734be5047155f3feed777a416adb4eeadec171da9922bbde4cb1739ac7265f8c"
10785
+ "sha256": "9f2cdc1780a59602454b42f59be746fc7af246a7e3e62ade83774aebf63fe4a7"
10786
10786
  },
10787
10787
  {
10788
10788
  "path": "tests/core_upgrade_door.mjs",
@@ -12479,6 +12479,11 @@
12479
12479
  "mode": "0000644",
12480
12480
  "sha256": "03b61cb8de6713b03fe17b2b3c5d70af7c511b33b17a42295f49c36e27771470"
12481
12481
  },
12482
+ {
12483
+ "path": "tests/nav_permission_atoms.mjs",
12484
+ "mode": "0000644",
12485
+ "sha256": "c965c4d56b00ec2cdf476ca045c8e28120ca16b57f26006a9017350b44014e92"
12486
+ },
12482
12487
  {
12483
12488
  "path": "tests/needs_rebase.mjs",
12484
12489
  "mode": "0000644",
@@ -2312,5 +2312,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
2312
2312
  landed since 1.19.917 with no explicit bump. run 35529342372. (task 1002620)
2313
2313
  1.19.919 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2314
2314
  landed since 1.19.918 with no explicit bump. run 35531198454. (task 1002620)
2315
+ 1.19.920 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2316
+ landed since 1.19.919 with no explicit bump. run 35531720944. (task 1002620)
2315
2317
  ---------------------------------------------------------------------------
2316
2318
  ```
@@ -91,7 +91,7 @@
91
91
 
92
92
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
93
93
 
94
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
94
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
95
95
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
96
96
  <script src="/builders/palette.js" defer></script>
97
97
  <script src="/builders/atlas.js?v=2026-09-02-index" defer></script>
@@ -50,7 +50,7 @@
50
50
 
51
51
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
52
52
  chrome exists before the page script runs. -->
53
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
53
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
54
54
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
55
55
  <script src="/builders/palette.js" defer></script>
56
56
  <!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
@@ -60,7 +60,7 @@
60
60
 
61
61
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
62
62
 
63
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
63
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
64
64
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
65
65
  <script src="/builders/palette.js" defer></script>
66
66
  <script src="/builders/dom-utils.js?v=2026-06-16-board"></script>
@@ -105,7 +105,7 @@
105
105
 
106
106
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
107
107
 
108
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
108
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
109
109
  <script src="/builders/palette.js" defer></script>
110
110
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
111
111
  <script src="/builders/collab-lib.js?v=2026-09-19-agewords"></script>
@@ -177,7 +177,7 @@
177
177
 
178
178
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
179
179
 
180
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
180
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
181
181
  <script src="/builders/palette.js" defer></script>
182
182
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
183
183
  <script src="/builders/hall-kit.js"></script>
@@ -55,7 +55,7 @@
55
55
 
56
56
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
57
57
 
58
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
58
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
59
59
  <script src="/builders/palette.js" defer></script>
60
60
  <script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
61
61
  <script src="/builders/hall-kit.js"></script>
@@ -97,7 +97,7 @@
97
97
 
98
98
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
99
99
  time so the chrome exists before the deferred page scripts execute. -->
100
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
100
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
101
101
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
102
102
  <script src="/builders/palette.js" defer></script>
103
103
  <script src="/builders/diagram-viewer.js?v=2026-06-14-diagviewer-1" defer></script>
@@ -98,7 +98,7 @@
98
98
 
99
99
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
100
100
 
101
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
101
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
102
102
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
103
103
  <script src="/builders/palette.js" defer></script>
104
104
  <script src="/builders/md-reader.js?v=2026-09-02-mdreader-2"></script>
@@ -76,7 +76,7 @@
76
76
  <div id="gate-modal-root"></div>
77
77
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
78
78
 
79
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
79
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
80
80
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
81
81
  <script src="/builders/palette.js" defer></script>
82
82
  <script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
@@ -220,7 +220,7 @@
220
220
 
221
221
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
222
222
 
223
- <script src="/builders/shell.js?v=2026-07-03-goals1"></script>
223
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
224
224
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
225
225
  <script src="/builders/palette.js" defer></script>
226
226
  <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
@@ -134,7 +134,7 @@
134
134
 
135
135
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
136
136
 
137
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
137
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
138
138
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
139
139
  <script src="/builders/palette.js" defer></script>
140
140
  <script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
@@ -52,7 +52,7 @@
52
52
 
53
53
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
54
54
 
55
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
55
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
56
56
  <script src="/builders/palette.js" defer></script>
57
57
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib" defer></script>
58
58
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N overlay, so a ref
@@ -192,7 +192,7 @@
192
192
 
193
193
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
194
194
 
195
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
195
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
196
196
  <script src="/builders/palette.js" defer></script>
197
197
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
198
198
  <!-- task 1003069 (R08) — the quick-filing form's routed result links a task
@@ -102,7 +102,7 @@
102
102
  <script src="/builders/brand-apply.js?v=2026-06-27-r75" defer></script>
103
103
  <!-- The app shell (sidebar + top bar + view router). Dependency-free; loaded
104
104
  BEFORE builders.js so the chrome ids exist when events are wired. -->
105
- <script src="/builders/shell.js?v=2026-07-02-shell1" defer></script>
105
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1" defer></script>
106
106
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
107
107
  <script src="/builders/palette.js" defer></script>
108
108
  <!-- task 2166: the shared component kit (window.OTBKit) — pager/ledger/rail.
@@ -80,7 +80,7 @@
80
80
 
81
81
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
82
82
 
83
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
83
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
84
84
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
85
85
  <script src="/builders/palette.js" defer></script>
86
86
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
@@ -55,7 +55,7 @@
55
55
 
56
56
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
57
57
  chrome exists before the page script runs. -->
58
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
58
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
59
59
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
60
60
  <script src="/builders/palette.js" defer></script>
61
61
  <!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
@@ -115,7 +115,7 @@
115
115
 
116
116
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
117
117
 
118
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
118
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
119
119
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
120
120
  <script src="/builders/palette.js" defer></script>
121
121
  <!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
@@ -187,7 +187,7 @@
187
187
 
188
188
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
189
189
  chrome exists before the page script runs. -->
190
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
190
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
191
191
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
192
192
  <script src="/builders/palette.js" defer></script>
193
193
  <!-- shared frontend toolkit (window.OTB) — loaded before profile.js, which pulls from it. -->
@@ -58,7 +58,7 @@
58
58
 
59
59
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
60
60
 
61
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
61
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
62
62
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
63
63
  <script src="/builders/palette.js" defer></script>
64
64
  <script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
@@ -68,7 +68,7 @@
68
68
 
69
69
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
70
70
  chrome exists before the page script runs. -->
71
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
71
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
72
72
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
73
73
  <script src="/builders/palette.js" defer></script>
74
74
  <!-- shared frontend toolkit (window.OTB) — loaded before ranks.js, which pulls from it. -->
@@ -41,7 +41,7 @@
41
41
 
42
42
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
43
43
 
44
- <script src="/builders/shell.js?v=2026-07-03-roadmap1"></script>
44
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
45
45
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
46
46
  <script src="/builders/palette.js" defer></script>
47
47
  <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
@@ -50,7 +50,7 @@
50
50
 
51
51
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
52
52
  chrome exists before the page script runs. -->
53
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
53
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
54
54
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
55
55
  <script src="/builders/palette.js" defer></script>
56
56
  <!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
@@ -99,7 +99,7 @@
99
99
 
100
100
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
101
101
  chrome exists before the page script runs. -->
102
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
102
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
103
103
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
104
104
  <script src="/builders/palette.js" defer></script>
105
105
  <!-- shared frontend toolkit (window.OTB) — loaded BEFORE sessions.js so it can pull from it. -->
@@ -416,7 +416,7 @@
416
416
 
417
417
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
418
418
  chrome exists before the page scripts run. -->
419
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
419
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
420
420
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
421
421
  <script src="/builders/palette.js" defer></script>
422
422
  <!-- shared frontend toolkit (window.OTB) — loaded before consuming IIFEs. -->
@@ -66,7 +66,7 @@
66
66
 
67
67
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
68
68
 
69
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
69
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
70
70
  <script src="/builders/palette.js" defer></script>
71
71
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
72
72
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
@@ -57,7 +57,7 @@
57
57
 
58
58
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
59
59
  time so the chrome exists before the deferred page scripts execute. -->
60
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
60
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
61
61
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
62
62
  <script src="/builders/palette.js" defer></script>
63
63
  <!-- shared frontend toolkit (window.OTB) — loaded FIRST so task.js can pull from it. -->
@@ -162,7 +162,7 @@
162
162
 
163
163
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
164
164
  chrome exists before the page script runs. -->
165
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
165
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
166
166
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
167
167
  <script src="/builders/palette.js" defer></script>
168
168
  <!-- shared frontend toolkit (window.OTB) — loaded before watch.js, which pulls from it. -->
@@ -151,7 +151,7 @@
151
151
 
152
152
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
153
153
  time so the chrome exists before the deferred page scripts execute. -->
154
- <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
154
+ <script src="/builders/shell.js?v=2026-09-20-coreupdate1"></script>
155
155
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
156
156
  <script src="/builders/palette.js" defer></script>
157
157
  <!-- shared frontend toolkit (window.OTB) — loaded FIRST so work.js can pull from it. -->
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.919",
3
+ "version": "1.19.920",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.919",
9
+ "version": "1.19.920",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.919",
3
+ "version": "1.19.920",
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",
@@ -119,7 +119,15 @@ const discordIsLinked = (builderId) =>
119
119
  // COSMETIC ONLY (ADR 0016). This decides who is OFFERED a link. The page gate
120
120
  // (auth.js PAGE_GATES) and each data route's requirePermission are the wall, and
121
121
  // they run regardless of what this says.
122
- const NAV_PERMISSION_ATOMS = Object.freeze(['board.vote.cast']);
122
+ // `core.pin.move` (task 1004118). The core-update banner in shell.js gates on it,
123
+ // and task 1004045 shipped that gate WITHOUT adding the atom here — so the value was
124
+ // `undefined` for every builder, the guard always bailed, and the banner could not
125
+ // render for anyone, on any instance. The authority was never the problem: the same
126
+ // account that saw no banner is answered 200 by a `core.pin.move`-gated route. Only
127
+ // this projection omitted it. Adding an atom here is the deliberate act this comment
128
+ // already described; tests/nav_permission_atoms.mjs now fails if shell.js gates on
129
+ // one that is missing, so the act cannot be skipped again.
130
+ const NAV_PERMISSION_ATOMS = Object.freeze(['board.vote.cast', 'core.pin.move']);
123
131
 
124
132
  // navPermissionsFor(builderId) → { '<atom>': boolean } over NAV_PERMISSION_ATOMS,
125
133
  // or null when authority could not be established (government off, port missing,
package/src/module-api.js CHANGED
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.919'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.920'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -306,3 +306,31 @@ test('the banner is asked for ONCE, not on every access settle', async () => {
306
306
  const first = b.requested.filter((r) => r.endsWith('/core-update')).length;
307
307
  assert.equal(first, 1);
308
308
  });
309
+
310
+ // ── the chain, with the map the SERVER would really send (task 1004118) ───────
311
+ //
312
+ // Every case above hands applyAccess a permissions map written here. That is exactly
313
+ // how task 1004045's banner shipped dead: it gated on `core.pin.move`, /me resolved
314
+ // only NAV_PERMISSION_ATOMS, the atom was not in it, and the guard bailed for every
315
+ // builder on every instance — while this suite stayed green throughout.
316
+ //
317
+ // So this case does NOT write the map. It DERIVES it from me.js's own declaration,
318
+ // the way the server does, and grants everything in it. If the atom the banner needs
319
+ // is not part of that projection, there is nothing here to grant and the banner
320
+ // cannot render — which is the real bug, reproduced as a failure.
321
+ test('the banner renders from the projection /me ACTUALLY sends, not one written by this test', async () => {
322
+ const meSrc = fs.readFileSync(path.join(ROOT, 'src/bongos/routes/me.js'), 'utf8');
323
+ const m = meSrc.match(/NAV_PERMISSION_ATOMS = Object\.freeze\(\[([^\]]*)\]\)/);
324
+ assert.ok(m, 'NAV_PERMISSION_ATOMS is readable');
325
+ const atoms = m[1].split(',').map((x) => x.trim().replace(/^'|'$/g, '')).filter(Boolean);
326
+ // A builder who holds everything the projection can report — the most generous real
327
+ // answer the server is capable of giving.
328
+ const permissions = Object.fromEntries(atoms.map((a) => [a, true]));
329
+ const b = boot({ permissions, update: BEHIND });
330
+ await new Promise((r) => setTimeout(r, 0));
331
+ assert.ok(
332
+ b.banner(),
333
+ 'the banner did not render for a builder holding EVERY atom /me can report — so the atom it ' +
334
+ 'gates on is outside NAV_PERMISSION_ATOMS and the feature is dead for everyone (task 1004118)',
335
+ );
336
+ });
@@ -0,0 +1,110 @@
1
+ // tests/nav_permission_atoms.mjs — the hall shell may only gate on a permission the
2
+ // server actually reports, and shell.js may not change behind its own cache-bust
3
+ // (task 1004118).
4
+ //
5
+ // WHY THIS FILE EXISTS. Task 1004045 added a banner gated on `core.pin.move`, and
6
+ // `/me` resolves ONLY the keys in NAV_PERMISSION_ATOMS — which did not include it. The
7
+ // value was `undefined` for every builder, the guard always bailed, and the banner
8
+ // could not render for ANYONE, on any instance. Its own test suite passed throughout,
9
+ // because every case called applyAccess({ permissions: { 'core.pin.move': true } }) —
10
+ // injecting the very map whose real shape was the defect. A renderer proven correct
11
+ // against an input the server never produces is proven of nothing.
12
+ //
13
+ // So this asserts the CONTRACT BETWEEN TWO FILES, which is the only place the bug was
14
+ // visible, and it reads both rather than trusting either.
15
+ import assert from 'node:assert/strict';
16
+ import { test } from 'node:test';
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import crypto from 'node:crypto';
20
+ import { fileURLToPath } from 'node:url';
21
+
22
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
23
+ const SHELL_PATH = path.join(ROOT, 'modules/hall-ui/public/shell.js');
24
+ const SHELL = fs.readFileSync(SHELL_PATH, 'utf8');
25
+ const ME = fs.readFileSync(path.join(ROOT, 'src/bongos/routes/me.js'), 'utf8');
26
+
27
+ // The atoms /me will actually resolve, read out of its own declaration.
28
+ function navAtoms() {
29
+ const m = ME.match(/NAV_PERMISSION_ATOMS = Object\.freeze\(\[([^\]]*)\]\)/);
30
+ assert.ok(m, 'NAV_PERMISSION_ATOMS is declared where this test can read it');
31
+ return m[1].split(',').map((s) => s.trim().replace(/^'|'$/g, '')).filter(Boolean);
32
+ }
33
+
34
+ // Every atom the shell gates on, in BOTH the shapes it uses: a nav item's
35
+ // `gate: 'perm:<atom>'`, and a direct read of the resolved map.
36
+ function shellAtoms() {
37
+ const out = new Set();
38
+ for (const m of SHELL.matchAll(/gate: *'perm:([^']+)'/g)) out.add(m[1]);
39
+ for (const m of SHELL.matchAll(/perms\['([^']+)'\]/g)) out.add(m[1]);
40
+ for (const m of SHELL.matchAll(/permissions\['([^']+)'\]/g)) out.add(m[1]);
41
+ return [...out];
42
+ }
43
+
44
+ test('every permission the shell gates on is one /me actually reports', () => {
45
+ const served = navAtoms();
46
+ const needed = shellAtoms();
47
+ assert.ok(needed.length, 'the shell gates on at least one atom (else this test is vacuous)');
48
+ for (const atom of needed) {
49
+ assert.ok(
50
+ served.includes(atom),
51
+ `shell.js gates on '${atom}' but /me does not resolve it — NAV_PERMISSION_ATOMS carries [${served.join(', ')}]. ` +
52
+ 'A gate on an atom the server never sends is not a gate, it is an OFF switch: the value is undefined for ' +
53
+ "every builder, so the feature is dead for everyone. Add the atom to NAV_PERMISSION_ATOMS in src/bongos/routes/me.js.",
54
+ );
55
+ }
56
+ });
57
+
58
+ test('the shell gates on nothing the catalog does not define', () => {
59
+ // The other direction: a typo'd atom would sit in NAV_PERMISSION_ATOMS resolving
60
+ // false forever, which looks exactly like "you do not hold it".
61
+ const catalog = fs.readFileSync(path.join(ROOT, 'modules/government/catalog.js'), 'utf8');
62
+ for (const atom of shellAtoms()) {
63
+ assert.ok(catalog.includes(`'${atom}'`), `'${atom}' is a real permission in the government catalog`);
64
+ }
65
+ });
66
+
67
+ test('the hall-preview fixture carries every atom /me resolves', () => {
68
+ // modules/hall-ui/CLAUDE.md makes this a required step — "add the key to
69
+ // scripts/hall-preview/fixtures/me.json when you add an atom, or the harness renders
70
+ // the item's absent state forever" — and nothing enforced it, so this task missed it
71
+ // exactly as the doc predicted. A design harness that silently shows the wrong state
72
+ // is worse than one that is obviously broken, because it is trusted.
73
+ const fixture = JSON.parse(fs.readFileSync(path.join(ROOT, 'scripts/hall-preview/fixtures/me.json'), 'utf8'));
74
+ const perms = fixture.permissions || {};
75
+ for (const atom of navAtoms()) {
76
+ assert.ok(
77
+ atom in perms,
78
+ `'${atom}' is resolved by /me but missing from the hall-preview fixture — the harness will render its ` +
79
+ 'absent state forever. Add it to scripts/hall-preview/fixtures/me.json.',
80
+ );
81
+ }
82
+ });
83
+
84
+ // ── the cache-bust ────────────────────────────────────────────────────────────
85
+ //
86
+ // shell.js is requested with a `?v=` query by every hall page. Task 1004045 changed
87
+ // the file and left the query alone, so a returning browser kept the cached copy —
88
+ // a fix that reaches nobody looks identical to a fix that does not work.
89
+
90
+ const SHELL_SHA = 'd91b8e5fad83c8e2';
91
+
92
+ test('shell.js has not changed without its cache-bust being bumped', () => {
93
+ const actual = crypto.createHash('sha256').update(SHELL).digest('hex').slice(0, 16);
94
+ assert.equal(
95
+ actual, SHELL_SHA,
96
+ `shell.js changed. Bump the ?v= on every hall page that loads it, then set SHELL_SHA in this file to '${actual}'. ` +
97
+ 'Without the bump the edit reaches only first-time visitors.',
98
+ );
99
+ });
100
+
101
+ test('every hall page busts shell.js with the SAME string', () => {
102
+ // Divergent strings are how one of them goes stale: three different queries for one
103
+ // file were live before this task, and only the majority ever got bumped.
104
+ const dir = path.join(ROOT, 'modules/hall-ui/public');
105
+ const versions = new Set();
106
+ for (const f of fs.readdirSync(dir).filter((f) => f.endsWith('.html'))) {
107
+ for (const m of fs.readFileSync(path.join(dir, f), 'utf8').matchAll(/shell\.js\?v=([^"]+)/g)) versions.add(m[1]);
108
+ }
109
+ assert.equal(versions.size, 1, `one cache-bust for one file — found: [${[...versions].join(', ')}]`);
110
+ });