@bongos/core 1.19.617 → 1.19.619
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 +64 -24
- package/.claude/skills/backlog-review/SKILL.md +175 -0
- package/docs/adr/0268-the-constitution-comes-from-two-roots.md +132 -0
- package/docs/adr/0269-the-cli-session-store-is-host-keyed-at-a-fixed-anchor.md +104 -0
- package/docs/adr/README.md +2 -0
- package/docs/copy-inventory.md +36 -33
- package/docs/copy-registry.json +61 -34
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +4 -0
- package/modules/government/board.js +58 -0
- package/modules/government/config.js +34 -9
- package/modules/hall-ui/public/government.js +54 -0
- package/modules/hall-ui/public/oversight.css +25 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/backlog-review.js +399 -0
- package/scripts/gds/build-cli-package.js +1 -1
- package/scripts/gds/cli-lib.js +110 -0
- package/scripts/gds/login.js +40 -2
- package/src/module-api.js +27 -1
- package/tests/backlog_review.mjs +183 -0
- package/tests/cli_sessions.mjs +168 -0
- package/tests/government_config_roots.mjs +159 -0
- package/tests/government_constitution_divergence.mjs +123 -0
- package/tests/module_api.mjs +1 -0
package/docs/copy-registry.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generator": "scripts/gds/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 1734,
|
|
7
|
+
"unique_ids": 1502,
|
|
8
|
+
"certain": 1734,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 163
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 78,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1261
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -241,7 +241,7 @@
|
|
|
241
241
|
"occurrences": 4,
|
|
242
242
|
"at": [
|
|
243
243
|
"modules/hall-ui/public/gate.js:219",
|
|
244
|
-
"modules/hall-ui/public/government.js:
|
|
244
|
+
"modules/hall-ui/public/government.js:1183",
|
|
245
245
|
"modules/hall-ui/public/harbor.js:337",
|
|
246
246
|
"modules/hall-ui/public/watch.js:1241"
|
|
247
247
|
]
|
|
@@ -301,7 +301,7 @@
|
|
|
301
301
|
"occurrences": 3,
|
|
302
302
|
"at": [
|
|
303
303
|
"modules/hall-ui/public/gate.js:222",
|
|
304
|
-
"modules/hall-ui/public/government.js:
|
|
304
|
+
"modules/hall-ui/public/government.js:1186",
|
|
305
305
|
"modules/hall-ui/public/harbor.js:340"
|
|
306
306
|
]
|
|
307
307
|
},
|
|
@@ -1915,7 +1915,7 @@
|
|
|
1915
1915
|
"surface": "builders-hall",
|
|
1916
1916
|
"text": ", // membership",
|
|
1917
1917
|
"file": "modules/hall-ui/public/government.js",
|
|
1918
|
-
"line":
|
|
1918
|
+
"line": 953,
|
|
1919
1919
|
"origin": "js-markup",
|
|
1920
1920
|
"confidence": "certain"
|
|
1921
1921
|
},
|
|
@@ -2369,6 +2369,15 @@
|
|
|
2369
2369
|
"origin": "js-markup",
|
|
2370
2370
|
"confidence": "certain"
|
|
2371
2371
|
},
|
|
2372
|
+
{
|
|
2373
|
+
"id": "da5368d10f85",
|
|
2374
|
+
"surface": "builders-hall",
|
|
2375
|
+
"text": "Amendment {…} passed{…}, and the constitution below is not the one it decided:",
|
|
2376
|
+
"file": "modules/hall-ui/public/government.js",
|
|
2377
|
+
"line": 647,
|
|
2378
|
+
"origin": "js-markup",
|
|
2379
|
+
"confidence": "certain"
|
|
2380
|
+
},
|
|
2372
2381
|
{
|
|
2373
2382
|
"id": "69f0ca4a4d84",
|
|
2374
2383
|
"surface": "builders-hall",
|
|
@@ -2500,7 +2509,7 @@
|
|
|
2500
2509
|
"surface": "builders-hall",
|
|
2501
2510
|
"text": "As configuration:",
|
|
2502
2511
|
"file": "modules/hall-ui/public/government.js",
|
|
2503
|
-
"line":
|
|
2512
|
+
"line": 824,
|
|
2504
2513
|
"origin": "js-markup",
|
|
2505
2514
|
"confidence": "certain"
|
|
2506
2515
|
},
|
|
@@ -2572,7 +2581,7 @@
|
|
|
2572
2581
|
"surface": "builders-hall",
|
|
2573
2582
|
"text": "Available from the {…} rank up.",
|
|
2574
2583
|
"file": "modules/hall-ui/public/government.js",
|
|
2575
|
-
"line":
|
|
2584
|
+
"line": 1186,
|
|
2576
2585
|
"origin": "js-text-assign",
|
|
2577
2586
|
"confidence": "certain"
|
|
2578
2587
|
},
|
|
@@ -2869,7 +2878,7 @@
|
|
|
2869
2878
|
"surface": "builders-hall",
|
|
2870
2879
|
"text": "Cast objection",
|
|
2871
2880
|
"file": "modules/hall-ui/public/government.js",
|
|
2872
|
-
"line":
|
|
2881
|
+
"line": 1041,
|
|
2873
2882
|
"origin": "js-markup",
|
|
2874
2883
|
"confidence": "certain"
|
|
2875
2884
|
},
|
|
@@ -2950,7 +2959,7 @@
|
|
|
2950
2959
|
"surface": "builders-hall",
|
|
2951
2960
|
"text": "Charter library",
|
|
2952
2961
|
"file": "modules/hall-ui/public/government.js",
|
|
2953
|
-
"line":
|
|
2962
|
+
"line": 794,
|
|
2954
2963
|
"origin": "js-markup",
|
|
2955
2964
|
"confidence": "certain"
|
|
2956
2965
|
},
|
|
@@ -4120,7 +4129,7 @@
|
|
|
4120
4129
|
"surface": "builders-hall",
|
|
4121
4130
|
"text": "Could not vote: {…}",
|
|
4122
4131
|
"file": "modules/hall-ui/public/government.js",
|
|
4123
|
-
"line":
|
|
4132
|
+
"line": 1069,
|
|
4124
4133
|
"origin": "js-notice",
|
|
4125
4134
|
"confidence": "certain"
|
|
4126
4135
|
},
|
|
@@ -4624,7 +4633,7 @@
|
|
|
4624
4633
|
"surface": "builders-hall",
|
|
4625
4634
|
"text": "Every closed amendment sitting, ratified and returned alike, with the rule it was decided under.",
|
|
4626
4635
|
"file": "modules/hall-ui/public/government.js",
|
|
4627
|
-
"line":
|
|
4636
|
+
"line": 834,
|
|
4628
4637
|
"origin": "js-markup",
|
|
4629
4638
|
"confidence": "certain"
|
|
4630
4639
|
},
|
|
@@ -4655,6 +4664,15 @@
|
|
|
4655
4664
|
"origin": "html-text",
|
|
4656
4665
|
"confidence": "certain"
|
|
4657
4666
|
},
|
|
4667
|
+
{
|
|
4668
|
+
"id": "47be006e5ad5",
|
|
4669
|
+
"surface": "builders-hall",
|
|
4670
|
+
"text": "Every sitting since has been decided under the constitution below, not the ratified one. Nothing is re-applied automatically — a constitution is changed by a board act, never by a page. Put the amendment back to the board, or ask an operator to check this instance's own configuration.",
|
|
4671
|
+
"file": "modules/hall-ui/public/government.js",
|
|
4672
|
+
"line": 647,
|
|
4673
|
+
"origin": "js-markup",
|
|
4674
|
+
"confidence": "certain"
|
|
4675
|
+
},
|
|
4658
4676
|
{
|
|
4659
4677
|
"id": "efafe5ce29ed",
|
|
4660
4678
|
"surface": "builders-hall",
|
|
@@ -5263,7 +5281,7 @@
|
|
|
5263
5281
|
"surface": "builders-hall",
|
|
5264
5282
|
"text": "How it has changed",
|
|
5265
5283
|
"file": "modules/hall-ui/public/government.js",
|
|
5266
|
-
"line":
|
|
5284
|
+
"line": 834,
|
|
5267
5285
|
"origin": "js-markup",
|
|
5268
5286
|
"confidence": "certain"
|
|
5269
5287
|
},
|
|
@@ -5335,7 +5353,7 @@
|
|
|
5335
5353
|
"surface": "builders-hall",
|
|
5336
5354
|
"text": "How this project is governed.",
|
|
5337
5355
|
"file": "modules/hall-ui/public/government.js",
|
|
5338
|
-
"line":
|
|
5356
|
+
"line": 1221,
|
|
5339
5357
|
"origin": "js-text-assign",
|
|
5340
5358
|
"confidence": "certain"
|
|
5341
5359
|
},
|
|
@@ -5596,7 +5614,7 @@
|
|
|
5596
5614
|
"surface": "builders-hall",
|
|
5597
5615
|
"text": "It cleared the completeness grade at {…}/100.",
|
|
5598
5616
|
"file": "modules/hall-ui/public/government.js",
|
|
5599
|
-
"line":
|
|
5617
|
+
"line": 1011,
|
|
5600
5618
|
"origin": "js-markup",
|
|
5601
5619
|
"confidence": "certain"
|
|
5602
5620
|
},
|
|
@@ -7261,7 +7279,7 @@
|
|
|
7261
7279
|
"surface": "builders-hall",
|
|
7262
7280
|
"text": "Permissions is the Archon administration surface. The Constitution and the Board Room are yours to read.",
|
|
7263
7281
|
"file": "modules/hall-ui/public/government.js",
|
|
7264
|
-
"line":
|
|
7282
|
+
"line": 1200,
|
|
7265
7283
|
"origin": "js-markup",
|
|
7266
7284
|
"confidence": "certain"
|
|
7267
7285
|
},
|
|
@@ -8071,7 +8089,7 @@
|
|
|
8071
8089
|
"surface": "builders-hall",
|
|
8072
8090
|
"text": "Restricted.",
|
|
8073
8091
|
"file": "modules/hall-ui/public/government.js",
|
|
8074
|
-
"line":
|
|
8092
|
+
"line": 1183,
|
|
8075
8093
|
"origin": "js-text-assign",
|
|
8076
8094
|
"confidence": "certain"
|
|
8077
8095
|
},
|
|
@@ -8368,7 +8386,7 @@
|
|
|
8368
8386
|
"surface": "builders-hall",
|
|
8369
8387
|
"text": "Selectable today",
|
|
8370
8388
|
"file": "modules/hall-ui/public/government.js",
|
|
8371
|
-
"line":
|
|
8389
|
+
"line": 794,
|
|
8372
8390
|
"origin": "js-markup",
|
|
8373
8391
|
"confidence": "certain"
|
|
8374
8392
|
},
|
|
@@ -9110,6 +9128,15 @@
|
|
|
9110
9128
|
"origin": "html-text",
|
|
9111
9129
|
"confidence": "certain"
|
|
9112
9130
|
},
|
|
9131
|
+
{
|
|
9132
|
+
"id": "f38140e472df",
|
|
9133
|
+
"surface": "builders-hall",
|
|
9134
|
+
"text": "The board ratified a change that is not in force.",
|
|
9135
|
+
"file": "modules/hall-ui/public/government.js",
|
|
9136
|
+
"line": 647,
|
|
9137
|
+
"origin": "js-markup",
|
|
9138
|
+
"confidence": "certain"
|
|
9139
|
+
},
|
|
9113
9140
|
{
|
|
9114
9141
|
"id": "716a303927d6",
|
|
9115
9142
|
"surface": "builders-hall",
|
|
@@ -9142,7 +9169,7 @@
|
|
|
9142
9169
|
"surface": "builders-hall",
|
|
9143
9170
|
"text": "The developmental ladder: {…}. Formality is adopted only where informal coordination breaks.",
|
|
9144
9171
|
"file": "modules/hall-ui/public/government.js",
|
|
9145
|
-
"line":
|
|
9172
|
+
"line": 801,
|
|
9146
9173
|
"origin": "js-markup",
|
|
9147
9174
|
"confidence": "certain"
|
|
9148
9175
|
},
|
|
@@ -9421,7 +9448,7 @@
|
|
|
9421
9448
|
"surface": "builders-hall",
|
|
9422
9449
|
"text": "The seven government structures, for getting a team up to speed before deciding when to flip a switch.",
|
|
9423
9450
|
"file": "modules/hall-ui/public/government.js",
|
|
9424
|
-
"line":
|
|
9451
|
+
"line": 794,
|
|
9425
9452
|
"origin": "js-markup",
|
|
9426
9453
|
"confidence": "certain"
|
|
9427
9454
|
},
|
|
@@ -9772,7 +9799,7 @@
|
|
|
9772
9799
|
"surface": "builders-hall",
|
|
9773
9800
|
"text": "This sends the idea back to its author. Say why, and which section.",
|
|
9774
9801
|
"file": "modules/hall-ui/public/government.js",
|
|
9775
|
-
"line":
|
|
9802
|
+
"line": 1041,
|
|
9776
9803
|
"origin": "js-markup",
|
|
9777
9804
|
"confidence": "certain"
|
|
9778
9805
|
},
|
|
@@ -9961,7 +9988,7 @@
|
|
|
9961
9988
|
"surface": "builders-hall",
|
|
9962
9989
|
"text": "Vote yes",
|
|
9963
9990
|
"file": "modules/hall-ui/public/government.js",
|
|
9964
|
-
"line":
|
|
9991
|
+
"line": 1041,
|
|
9965
9992
|
"origin": "js-markup",
|
|
9966
9993
|
"confidence": "certain"
|
|
9967
9994
|
},
|
|
@@ -10303,7 +10330,7 @@
|
|
|
10303
10330
|
"surface": "builders-hall",
|
|
10304
10331
|
"text": "Why — the reason the author will act on",
|
|
10305
10332
|
"file": "modules/hall-ui/public/government.js",
|
|
10306
|
-
"line":
|
|
10333
|
+
"line": 1041,
|
|
10307
10334
|
"origin": "js-markup",
|
|
10308
10335
|
"confidence": "certain"
|
|
10309
10336
|
},
|
|
@@ -10501,7 +10528,7 @@
|
|
|
10501
10528
|
"surface": "builders-hall",
|
|
10502
10529
|
"text": "You {…}. One vote per member.",
|
|
10503
10530
|
"file": "modules/hall-ui/public/government.js",
|
|
10504
|
-
"line":
|
|
10531
|
+
"line": 1113,
|
|
10505
10532
|
"origin": "js-markup",
|
|
10506
10533
|
"confidence": "certain"
|
|
10507
10534
|
},
|
|
@@ -10663,7 +10690,7 @@
|
|
|
10663
10690
|
"surface": "builders-hall",
|
|
10664
10691
|
"text": "Your own idea. You may vote, but it can’t be decided by your vote alone, and a vote on your own idea earns no karma.",
|
|
10665
10692
|
"file": "modules/hall-ui/public/government.js",
|
|
10666
|
-
"line":
|
|
10693
|
+
"line": 1039,
|
|
10667
10694
|
"origin": "js-markup",
|
|
10668
10695
|
"confidence": "certain"
|
|
10669
10696
|
},
|
|
@@ -10852,7 +10879,7 @@
|
|
|
10852
10879
|
"surface": "builders-hall",
|
|
10853
10880
|
"text": "active builder{…}: {…}",
|
|
10854
10881
|
"file": "modules/hall-ui/public/government.js",
|
|
10855
|
-
"line":
|
|
10882
|
+
"line": 794,
|
|
10856
10883
|
"origin": "js-markup",
|
|
10857
10884
|
"confidence": "certain"
|
|
10858
10885
|
},
|
|
@@ -10924,7 +10951,7 @@
|
|
|
10924
10951
|
"surface": "builders-hall",
|
|
10925
10952
|
"text": "as configuration: {…}",
|
|
10926
10953
|
"file": "modules/hall-ui/public/government.js",
|
|
10927
|
-
"line":
|
|
10954
|
+
"line": 780,
|
|
10928
10955
|
"origin": "js-markup",
|
|
10929
10956
|
"confidence": "certain"
|
|
10930
10957
|
},
|
|
@@ -11464,7 +11491,7 @@
|
|
|
11464
11491
|
"surface": "builders-hall",
|
|
11465
11492
|
"text": "means expressible through the real configuration — a rank-based membership plus a pass rule — with no new code. This project has",
|
|
11466
11493
|
"file": "modules/hall-ui/public/government.js",
|
|
11467
|
-
"line":
|
|
11494
|
+
"line": 794,
|
|
11468
11495
|
"origin": "js-markup",
|
|
11469
11496
|
"confidence": "certain"
|
|
11470
11497
|
},
|
|
@@ -11644,7 +11671,7 @@
|
|
|
11644
11671
|
"surface": "builders-hall",
|
|
11645
11672
|
"text": "objected to",
|
|
11646
11673
|
"file": "modules/hall-ui/public/government.js",
|
|
11647
|
-
"line":
|
|
11674
|
+
"line": 901,
|
|
11648
11675
|
"origin": "js-markup",
|
|
11649
11676
|
"confidence": "certain"
|
|
11650
11677
|
},
|
|
@@ -12220,7 +12247,7 @@
|
|
|
12220
12247
|
"surface": "builders-hall",
|
|
12221
12248
|
"text": "your vote",
|
|
12222
12249
|
"file": "modules/hall-ui/public/government.js",
|
|
12223
|
-
"line":
|
|
12250
|
+
"line": 896,
|
|
12224
12251
|
"origin": "js-markup",
|
|
12225
12252
|
"confidence": "certain"
|
|
12226
12253
|
},
|
|
@@ -12472,7 +12499,7 @@
|
|
|
12472
12499
|
"surface": "builders-hall",
|
|
12473
12500
|
"text": "{…} proposed",
|
|
12474
12501
|
"file": "modules/hall-ui/public/government.js",
|
|
12475
|
-
"line":
|
|
12502
|
+
"line": 698,
|
|
12476
12503
|
"origin": "js-markup",
|
|
12477
12504
|
"confidence": "certain"
|
|
12478
12505
|
},
|
|
@@ -12652,7 +12679,7 @@
|
|
|
12652
12679
|
"surface": "builders-hall",
|
|
12653
12680
|
"text": "{…} · who votes, and how it {…}",
|
|
12654
12681
|
"file": "modules/hall-ui/public/government.js",
|
|
12655
|
-
"line":
|
|
12682
|
+
"line": 1122,
|
|
12656
12683
|
"origin": "js-markup",
|
|
12657
12684
|
"confidence": "certain"
|
|
12658
12685
|
},
|
|
@@ -12724,7 +12751,7 @@
|
|
|
12724
12751
|
"surface": "builders-hall",
|
|
12725
12752
|
"text": "{…}s only",
|
|
12726
12753
|
"file": "modules/hall-ui/public/government.js",
|
|
12727
|
-
"line":
|
|
12754
|
+
"line": 1200,
|
|
12728
12755
|
"origin": "js-markup",
|
|
12729
12756
|
"confidence": "certain"
|
|
12730
12757
|
},
|
package/docs/file-map.md
CHANGED
|
@@ -376,6 +376,7 @@ tests/
|
|
|
376
376
|
<!-- BEGIN GENERATED FILE-MAP .claude/skills/ (scripts/gds/gen-file-map.js — do not hand-edit; notes live in docs/file-map.notes.json) -->
|
|
377
377
|
```
|
|
378
378
|
.claude/skills/
|
|
379
|
+
├── backlog-review/SKILL.md ← /backlog-review — daily walk of status=backlog, the pre-workable state a human must say go on: splits rows waiting on a PERSON (promote / kill / water) from rows waiting on a live dep TRIGGER (counted, never walked, migration 163) and surfaces rows stranded behind an abandoned dep; runs scripts/gds/backlog-review.js (task 1003746); Metic+
|
|
379
380
|
├── blocker-review/SKILL.md ← /blocker-review — daily review of open Bongos blockers (resolve / escalate / note); Metic+
|
|
380
381
|
├── blocker-solve/SKILL.md ← /blocker-solve N — drive ONE blocker to done: do the doable parts, hand back owner-only steps, verify, auto-resolve (auto-promotes waiters); Metic+
|
|
381
382
|
├── bug-triage/SKILL.md ← /bug-triage — daily walk through open kind=bug tasks (batch-cluster near-dups, merge / won't-fix); the idea-triage counterpart built on the new tasks.merge primitive (task 1001478); Metic+
|
|
@@ -1683,5 +1683,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1683
1683
|
landed since 1.19.615 with no explicit bump. run 34315726534. (task 1002620)
|
|
1684
1684
|
1.19.617 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1685
1685
|
landed since 1.19.616 with no explicit bump. run 34317034024. (task 1002620)
|
|
1686
|
+
1.19.618 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1687
|
+
landed since 1.19.617 with no explicit bump. run 34318552090. (task 1002620)
|
|
1688
|
+
1.19.619 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1689
|
+
landed since 1.19.618 with no explicit bump. run 34320024204. (task 1002620)
|
|
1686
1690
|
---------------------------------------------------------------------------
|
|
1687
1691
|
```
|
|
@@ -891,6 +891,55 @@ function recommendedCharter(headcount) {
|
|
|
891
891
|
return { key: 'federation', rung: '~150: adjudication separates — federate beyond', why: 'past ~150, one body cannot both govern and adjudicate; federate' };
|
|
892
892
|
}
|
|
893
893
|
|
|
894
|
+
// ── the divergence check (task 1003739, ADR 0268) ───────────────────────────
|
|
895
|
+
//
|
|
896
|
+
// WHAT THIS EXISTS TO CATCH, because it already happened and nothing noticed.
|
|
897
|
+
// On 2026-08-25 this board ratified an amendment; `applyBoardAmendment` wrote it
|
|
898
|
+
// to a path that was not the host's, the next core upgrade erased it, and the
|
|
899
|
+
// constitution went on answering the day-one monarchy. The API had BOTH facts in
|
|
900
|
+
// one payload the whole time — a passed amendment in `history`, a contradicting
|
|
901
|
+
// `board` beside it — and simply never compared them. Two weeks of sittings were
|
|
902
|
+
// decided under a rule the board had voted to replace.
|
|
903
|
+
//
|
|
904
|
+
// So this is not a fix for that path bug (the path is fixed at its source, in
|
|
905
|
+
// config.js). It is the detector for the CLASS: any future reason a ratified
|
|
906
|
+
// amendment fails to take — a failed write, a rolled-back deploy, a hand edit
|
|
907
|
+
// that reverts one — surfaces here instead of being invisible.
|
|
908
|
+
//
|
|
909
|
+
// IT REPORTS, IT NEVER HEALS. Re-applying automatically was considered and
|
|
910
|
+
// refused: the instance pack is also the file a human editing the constitution
|
|
911
|
+
// touches (config.js §applyBoardAmendment), so a silent re-apply would revert a
|
|
912
|
+
// legitimate hand edit with no way to tell the two apart — trading a visible
|
|
913
|
+
// wrong constitution for an unstoppable one. Naming the divergence is what the
|
|
914
|
+
// two weeks of silence actually lacked.
|
|
915
|
+
//
|
|
916
|
+
// Compared against the SANITIZED proposal, because that is what an apply would
|
|
917
|
+
// have written; comparing the raw proposal would report a phantom divergence on
|
|
918
|
+
// any field the sanitizer legitimately normalised.
|
|
919
|
+
function constitutionDivergence(liveBoard, history) {
|
|
920
|
+
const rows = Array.isArray(history) ? history : [];
|
|
921
|
+
// `history` arrives newest-first from listAmendmentHistory, so the first
|
|
922
|
+
// passed row is the standing decision. A returned amendment changed nothing
|
|
923
|
+
// and must not be compared — that is the whole meaning of 'returned'.
|
|
924
|
+
const last = rows.find((r) => r.outcome === 'passed' && r.proposed);
|
|
925
|
+
if (!last) return null;
|
|
926
|
+
const should = governmentConfig.sanitizeBoard(last.proposed);
|
|
927
|
+
const live = liveBoard || {};
|
|
928
|
+
const fields = [];
|
|
929
|
+
for (const key of ['membership', 'pass_rule', 'window_minutes', 'close_early_on_full_turnout']) {
|
|
930
|
+
if (should[key] !== live[key]) fields.push({ field: key, ratified: should[key], in_force: live[key] });
|
|
931
|
+
}
|
|
932
|
+
if (!fields.length) return null;
|
|
933
|
+
return Object.freeze({
|
|
934
|
+
item_id: last.item_id,
|
|
935
|
+
amendment_id: last.amendment_id,
|
|
936
|
+
closed_at: last.closed_at,
|
|
937
|
+
ratified: should,
|
|
938
|
+
in_force: Object.freeze({ ...live }),
|
|
939
|
+
fields: Object.freeze(fields),
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
|
|
894
943
|
async function constitutionView({ historyLimit = 50 } = {}) {
|
|
895
944
|
const { board: live } = loadGovernmentConfig();
|
|
896
945
|
const [rows, headcount] = await Promise.all([
|
|
@@ -915,6 +964,14 @@ async function constitutionView({ historyLimit = 50 } = {}) {
|
|
|
915
964
|
history,
|
|
916
965
|
headcount,
|
|
917
966
|
recommended: recommendedCharter(headcount),
|
|
967
|
+
// null when the constitution in force IS the one last ratified, which is
|
|
968
|
+
// the healthy answer and the one the hall renders nothing for. Computed
|
|
969
|
+
// from the history already in hand — no extra query, and no second read
|
|
970
|
+
// that could disagree with the rows shown beside it. Bounded by
|
|
971
|
+
// `historyLimit` in principle; a board with 50 amendments newer than its
|
|
972
|
+
// last passed one is not a shape this system has, and the honest failure
|
|
973
|
+
// there is a missed warning rather than a wrong one.
|
|
974
|
+
divergence: constitutionDivergence(live, history),
|
|
918
975
|
// THE CLOCK ADVICE THE AMENDMENT FORM NEEDS (task 1003733, ADR 0267 §3).
|
|
919
976
|
// A proposer choosing a pass rule has to choose a window in the same breath
|
|
920
977
|
// — a constitution is ratified whole, never by diff — and the two choices
|
|
@@ -1045,5 +1102,6 @@ module.exports = {
|
|
|
1045
1102
|
proposeAmendment,
|
|
1046
1103
|
boardRoomView,
|
|
1047
1104
|
constitutionView,
|
|
1105
|
+
constitutionDivergence,
|
|
1048
1106
|
recommendedCharter,
|
|
1049
1107
|
};
|
|
@@ -48,15 +48,34 @@ const path = require('node:path');
|
|
|
48
48
|
const api = require('../../src/module-api');
|
|
49
49
|
const catalog = require('./catalog');
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
// THE TWO FILES COME FROM TWO DIFFERENT ROOTS, and conflating them is the bug
|
|
52
|
+
// task 1003739 fixed (ADR 0268). This used to be one `path.resolve(__dirname,
|
|
53
|
+
// '..', '..')` for both, under a comment saying the roots coincided "today" —
|
|
54
|
+
// true in a single checkout, and false the day cloudbongos.com began running a
|
|
55
|
+
// PINNED core. There, __dirname is inside node_modules/@cloudbongos/core, so:
|
|
56
|
+
//
|
|
57
|
+
// • the NEUTRAL starter resolved correctly (it really is core content), and
|
|
58
|
+
// • the INSTANCE pack resolved to node_modules/@cloudbongos/core/config/
|
|
59
|
+
// government.json — a path no host authors and every `npm ci` erases.
|
|
60
|
+
//
|
|
61
|
+
// So the host's own constitution was never read, and `applyBoardAmendment`
|
|
62
|
+
// wrote a RATIFIED amendment into node_modules, where the next core upgrade
|
|
63
|
+
// deleted it. Nothing threw: both paths exist, both are writable, and the read
|
|
64
|
+
// simply fell through to the neutral monarchy. A board decided and the decision
|
|
65
|
+
// silently did not take, for two weeks.
|
|
66
|
+
//
|
|
67
|
+
// src/branding.js is the pattern, and it had it right all along: core content
|
|
68
|
+
// from resolveCoreRoot(), host content from resolveInstanceRoot(). Reached
|
|
69
|
+
// through the module doorway (ADR 0083 — a module never requires a core
|
|
70
|
+
// internal), which grew both resolvers for exactly this.
|
|
71
|
+
//
|
|
72
|
+
// RESOLVED AT LOAD, not per call, because resolveInstanceRoot() falls back to
|
|
73
|
+
// process.cwd(): resolving it lazily would let a chdir mid-process move the
|
|
74
|
+
// constitution, which is a worse failure than the one being fixed.
|
|
75
|
+
const CORE_ROOT = api.resolveCoreRoot();
|
|
76
|
+
const INSTANCE_ROOT = api.resolveInstanceRoot();
|
|
77
|
+
const NEUTRAL_PATH = path.join(CORE_ROOT, 'config', 'government.neutral.json');
|
|
78
|
+
const INSTANCE_PATH = path.join(INSTANCE_ROOT, 'config', 'government.json');
|
|
60
79
|
|
|
61
80
|
const SEEDED_RANK_ROLES = new Set(catalog.RANK_ORDER);
|
|
62
81
|
|
|
@@ -345,6 +364,12 @@ function applyBoardAmendment(proposed, { instancePath = INSTANCE_PATH } = {}) {
|
|
|
345
364
|
const instance = readJson(instancePath) || {};
|
|
346
365
|
const next = { ...instance, board: { ...sanitizeBoard(proposed) } };
|
|
347
366
|
const json = JSON.stringify(next, null, 2) + '\n';
|
|
367
|
+
// The directory may not exist yet: an instance that has never been branded
|
|
368
|
+
// and never amended its constitution has no config/ of its own, and since
|
|
369
|
+
// task 1003739 this path is the INSTANCE root rather than the checkout that
|
|
370
|
+
// always happened to have one. A ratified amendment must not be lost to
|
|
371
|
+
// ENOENT on a directory — the recursive mkdir is a no-op when it is there.
|
|
372
|
+
fs.mkdirSync(path.dirname(instancePath), { recursive: true });
|
|
348
373
|
const tmp = `${instancePath}.tmp-${process.pid}`;
|
|
349
374
|
fs.writeFileSync(tmp, json);
|
|
350
375
|
fs.renameSync(tmp, instancePath);
|
|
@@ -626,6 +626,59 @@
|
|
|
626
626
|
return `A sitting runs <strong>${fmtWindow(board.window_minutes)}</strong>${early}. An unvoted window nulls that member's vote — absence never blocks.`;
|
|
627
627
|
}
|
|
628
628
|
|
|
629
|
+
// THE WARNING THAT WAS MISSING FOR TWO WEEKS (task 1003739, ADR 0268). This
|
|
630
|
+
// page showed the constitution in force and the amendment history that
|
|
631
|
+
// contradicted it, one above the other, and said nothing — so a ratified
|
|
632
|
+
// amendment that never took looked exactly like a board nobody had amended.
|
|
633
|
+
// The server does the comparing (`divergence`); this renders it at the TOP of
|
|
634
|
+
// the panel, above the dials, because the dials are the thing it is warning
|
|
635
|
+
// about. Absent (the healthy case) renders nothing at all.
|
|
636
|
+
//
|
|
637
|
+
// It never offers a fix button. Re-applying is a board act, not a click — and
|
|
638
|
+
// the honest remedies (put the amendment back to the board, or author the
|
|
639
|
+
// instance pack) are things a person decides, so the copy names the fields
|
|
640
|
+
// and leaves the decision where it belongs.
|
|
641
|
+
function divergenceHtml(d) {
|
|
642
|
+
if (!d || !d.fields || !d.fields.length) return '';
|
|
643
|
+
const rows = d.fields.map((f) => `<li><strong>${escapeHtml(FIELD_LABELS[f.field] || f.field)}</strong>: `
|
|
644
|
+
+ `the board decided ${fieldValueHtml(f.field, f.ratified)}, `
|
|
645
|
+
+ `but ${fieldValueHtml(f.field, f.in_force)} is in force</li>`).join('');
|
|
646
|
+
const when = d.closed_at ? ` on ${escapeHtml(String(d.closed_at).slice(0, 10))}` : '';
|
|
647
|
+
return `<div class="ov-note ov-note--warn" role="status">
|
|
648
|
+
<p><strong>The board ratified a change that is not in force.</strong> Amendment ${escapeHtml(String(d.amendment_id))} passed${when}, and the constitution below is not the one it decided:</p>
|
|
649
|
+
<ul>${rows}</ul>
|
|
650
|
+
<p>Every sitting since has been decided under the constitution below, not the ratified one. Nothing is re-applied automatically — a constitution is changed by a board act, never by a page. Put the amendment back to the board, or ask an operator to check this instance's own configuration.</p>
|
|
651
|
+
</div>`;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// The four constitution fields in the words the dials use, so a warning does
|
|
655
|
+
// not become the one place on this page that leaks a raw config key.
|
|
656
|
+
const FIELD_LABELS = {
|
|
657
|
+
membership: 'Who sits on the board',
|
|
658
|
+
pass_rule: 'What counts as passing',
|
|
659
|
+
window_minutes: 'How long a sitting runs',
|
|
660
|
+
close_early_on_full_turnout: 'Closing early on full turnout',
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// One constitution VALUE, in the words a person would use — the same rule the
|
|
664
|
+
// dials keep, applied to the two halves of a divergence. Without this the
|
|
665
|
+
// warning would be the one place on the page reading "ratified as
|
|
666
|
+
// `rank:metic+`, but `first_ratifier` is in force", which is grammar for the
|
|
667
|
+
// server. The one honest exception is a predicate that cannot be parsed: there
|
|
668
|
+
// the broken string IS the actionable fact (the page's standing rule).
|
|
669
|
+
function fieldValueHtml(field, value) {
|
|
670
|
+
if (field === 'membership') {
|
|
671
|
+
const keys = membershipRankKeys(value);
|
|
672
|
+
return keys ? rankNames(keys) : `<code>${escapeHtml(String(value))}</code>`;
|
|
673
|
+
}
|
|
674
|
+
if (field === 'pass_rule') return escapeHtml(PASS_RULE_NAME[value] || String(value));
|
|
675
|
+
if (field === 'window_minutes') {
|
|
676
|
+
return value === null || value === undefined ? 'no clock' : `a sitting of ${fmtWindow(value)}`;
|
|
677
|
+
}
|
|
678
|
+
if (field === 'close_early_on_full_turnout') return value ? 'closing early on full turnout' : 'running the full window';
|
|
679
|
+
return escapeHtml(String(value));
|
|
680
|
+
}
|
|
681
|
+
|
|
629
682
|
// One dial, one row: the dial's name as the title, the live sentence as the
|
|
630
683
|
// sub line. The sentence is trusted HTML from the helpers above.
|
|
631
684
|
function dialRow(name, sentenceHtml) {
|
|
@@ -769,6 +822,7 @@
|
|
|
769
822
|
// The three dials as rows inside the Constitution panel, then the
|
|
770
823
|
// configuration they resolve from as the panel's foot line.
|
|
771
824
|
live.innerHTML = `
|
|
825
|
+
${divergenceHtml(data.divergence)}
|
|
772
826
|
<ul class="ov-rows">
|
|
773
827
|
${dialRow('Who sits on the board', membershipSentence(b))}
|
|
774
828
|
${dialRow('What counts as passing', passRuleSentence(b))}
|
|
@@ -273,6 +273,31 @@ a.fact-chip:hover { color: var(--accent-ink); border-color: var(--accent-ink); t
|
|
|
273
273
|
.btn-ghost--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--rule)); }
|
|
274
274
|
.btn-ghost--danger:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); }
|
|
275
275
|
|
|
276
|
+
/* ---- the standing note ----------------------------------------------------------
|
|
277
|
+
A condition the page must state before its own content, not a transient toast:
|
|
278
|
+
the Constitution tab's "the board ratified a change that is not in force"
|
|
279
|
+
(task 1003739). It sits ABOVE the dials because it is a warning about them.
|
|
280
|
+
|
|
281
|
+
The family marks state with colour on a hairline shape, never a filled band —
|
|
282
|
+
so this is the panel's own ground closed by a warn-tinted rule, with only the
|
|
283
|
+
leading strong line carrying the warn ink. A full danger fill would out-shout
|
|
284
|
+
the constitution it is annotating, which is the thing the reader came for. */
|
|
285
|
+
.ov-note {
|
|
286
|
+
margin: 0 0 16px;
|
|
287
|
+
padding: 12px 14px;
|
|
288
|
+
border: var(--line) solid var(--rule);
|
|
289
|
+
border-radius: var(--radius);
|
|
290
|
+
font-size: 13px;
|
|
291
|
+
line-height: 1.6;
|
|
292
|
+
max-width: var(--measure);
|
|
293
|
+
}
|
|
294
|
+
.ov-note p { margin: 0 0 8px; }
|
|
295
|
+
.ov-note p:last-child { margin-bottom: 0; }
|
|
296
|
+
.ov-note ul { margin: 0 0 8px; padding-left: 18px; }
|
|
297
|
+
.ov-note code { font-size: 12px; }
|
|
298
|
+
.ov-note--warn { border-color: color-mix(in srgb, var(--warn) 45%, var(--rule)); }
|
|
299
|
+
.ov-note--warn > p:first-child strong { color: var(--warn); }
|
|
300
|
+
|
|
276
301
|
/* ---- the foot line ----------------------------------------------------------- */
|
|
277
302
|
.ov-foot { margin: 14px 0 0; font-size: 12.5px; color: var(--ink-faint); max-width: var(--measure); }
|
|
278
303
|
/* A link in the foot line is a control at a 12.5px line box: the padding buys
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.619",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.619",
|
|
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.
|
|
3
|
+
"version": "1.19.619",
|
|
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",
|