@bongos/core 1.20.25 → 1.20.26
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 +39 -29
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +2 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/api/openapi.json +35 -1
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +18 -18
- package/docs/copy-registry.json +20 -20
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-readings.json +14 -14
- package/modules/discord/board-broadcast.js +1 -0
- package/modules/government/board.js +204 -2
- package/modules/government/db.js +42 -1
- package/modules/government/migrations/government_020_genesis_stage_subject.sql +55 -0
- package/modules/government/routes/government.js +20 -0
- package/modules/hall-ui/public/board-room.js +5 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/bongos/route-rank-check.js +3 -0
- package/src/module-api.js +1 -1
- package/tests/government_board_genesis_stage.mjs +305 -0
- package/tests/government_seed.mjs +1 -0
package/docs/copy-registry.json
CHANGED
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
"text": "Restricted.",
|
|
266
266
|
"occurrences": 4,
|
|
267
267
|
"at": [
|
|
268
|
-
"modules/hall-ui/public/board-room.js:
|
|
268
|
+
"modules/hall-ui/public/board-room.js:526",
|
|
269
269
|
"modules/hall-ui/public/gate.js:234",
|
|
270
270
|
"modules/hall-ui/public/government.js:879",
|
|
271
271
|
"modules/hall-ui/public/watch.js:1294"
|
|
@@ -1087,7 +1087,7 @@
|
|
|
1087
1087
|
"text": "Its author gave no scope recommendation.",
|
|
1088
1088
|
"occurrences": 2,
|
|
1089
1089
|
"at": [
|
|
1090
|
-
"modules/hall-ui/public/board-room.js:
|
|
1090
|
+
"modules/hall-ui/public/board-room.js:300",
|
|
1091
1091
|
"modules/hall-ui/public/idea.js:162"
|
|
1092
1092
|
]
|
|
1093
1093
|
},
|
|
@@ -1097,7 +1097,7 @@
|
|
|
1097
1097
|
"text": "Its author recommends this becomes",
|
|
1098
1098
|
"occurrences": 2,
|
|
1099
1099
|
"at": [
|
|
1100
|
-
"modules/hall-ui/public/board-room.js:
|
|
1100
|
+
"modules/hall-ui/public/board-room.js:299",
|
|
1101
1101
|
"modules/hall-ui/public/idea.js:166"
|
|
1102
1102
|
]
|
|
1103
1103
|
},
|
|
@@ -2343,7 +2343,7 @@
|
|
|
2343
2343
|
"surface": "builders-hall",
|
|
2344
2344
|
"text": ", // membership",
|
|
2345
2345
|
"file": "modules/hall-ui/public/board-room.js",
|
|
2346
|
-
"line":
|
|
2346
|
+
"line": 305,
|
|
2347
2347
|
"origin": "js-markup",
|
|
2348
2348
|
"confidence": "certain"
|
|
2349
2349
|
},
|
|
@@ -3603,7 +3603,7 @@
|
|
|
3603
3603
|
"surface": "builders-hall",
|
|
3604
3604
|
"text": "Cast objection",
|
|
3605
3605
|
"file": "modules/hall-ui/public/board-room.js",
|
|
3606
|
-
"line":
|
|
3606
|
+
"line": 368,
|
|
3607
3607
|
"origin": "js-markup",
|
|
3608
3608
|
"confidence": "certain"
|
|
3609
3609
|
},
|
|
@@ -4863,7 +4863,7 @@
|
|
|
4863
4863
|
"surface": "builders-hall",
|
|
4864
4864
|
"text": "Could not vote: {…}",
|
|
4865
4865
|
"file": "modules/hall-ui/public/board-room.js",
|
|
4866
|
-
"line":
|
|
4866
|
+
"line": 396,
|
|
4867
4867
|
"origin": "js-notice",
|
|
4868
4868
|
"confidence": "certain"
|
|
4869
4869
|
},
|
|
@@ -6663,7 +6663,7 @@
|
|
|
6663
6663
|
"surface": "builders-hall",
|
|
6664
6664
|
"text": "It cleared the completeness grade at {…}/100.",
|
|
6665
6665
|
"file": "modules/hall-ui/public/board-room.js",
|
|
6666
|
-
"line":
|
|
6666
|
+
"line": 331,
|
|
6667
6667
|
"origin": "js-markup",
|
|
6668
6668
|
"confidence": "certain"
|
|
6669
6669
|
},
|
|
@@ -6708,7 +6708,7 @@
|
|
|
6708
6708
|
"surface": "builders-hall",
|
|
6709
6709
|
"text": "Its author gave no scope recommendation.",
|
|
6710
6710
|
"file": "modules/hall-ui/public/board-room.js",
|
|
6711
|
-
"line":
|
|
6711
|
+
"line": 300,
|
|
6712
6712
|
"origin": "js-markup",
|
|
6713
6713
|
"confidence": "certain"
|
|
6714
6714
|
},
|
|
@@ -6735,7 +6735,7 @@
|
|
|
6735
6735
|
"surface": "builders-hall",
|
|
6736
6736
|
"text": "Its author recommends this becomes",
|
|
6737
6737
|
"file": "modules/hall-ui/public/board-room.js",
|
|
6738
|
-
"line":
|
|
6738
|
+
"line": 299,
|
|
6739
6739
|
"origin": "js-markup",
|
|
6740
6740
|
"confidence": "certain"
|
|
6741
6741
|
},
|
|
@@ -7095,7 +7095,7 @@
|
|
|
7095
7095
|
"surface": "builders-hall",
|
|
7096
7096
|
"text": "Loading sittings…",
|
|
7097
7097
|
"file": "modules/hall-ui/public/board-room.js",
|
|
7098
|
-
"line":
|
|
7098
|
+
"line": 544,
|
|
7099
7099
|
"origin": "js-text-assign",
|
|
7100
7100
|
"confidence": "certain"
|
|
7101
7101
|
},
|
|
@@ -9687,7 +9687,7 @@
|
|
|
9687
9687
|
"surface": "builders-hall",
|
|
9688
9688
|
"text": "Restricted.",
|
|
9689
9689
|
"file": "modules/hall-ui/public/board-room.js",
|
|
9690
|
-
"line":
|
|
9690
|
+
"line": 526,
|
|
9691
9691
|
"origin": "js-text-assign",
|
|
9692
9692
|
"confidence": "certain"
|
|
9693
9693
|
},
|
|
@@ -11001,7 +11001,7 @@
|
|
|
11001
11001
|
"surface": "builders-hall",
|
|
11002
11002
|
"text": "The Board Room is open to the builders this project’s constitution seats on its board.",
|
|
11003
11003
|
"file": "modules/hall-ui/public/board-room.js",
|
|
11004
|
-
"line":
|
|
11004
|
+
"line": 529,
|
|
11005
11005
|
"origin": "js-text-assign",
|
|
11006
11006
|
"confidence": "certain"
|
|
11007
11007
|
},
|
|
@@ -12063,7 +12063,7 @@
|
|
|
12063
12063
|
"surface": "builders-hall",
|
|
12064
12064
|
"text": "This sends the idea back to its author. Say why, and which section.",
|
|
12065
12065
|
"file": "modules/hall-ui/public/board-room.js",
|
|
12066
|
-
"line":
|
|
12066
|
+
"line": 368,
|
|
12067
12067
|
"origin": "js-markup",
|
|
12068
12068
|
"confidence": "certain"
|
|
12069
12069
|
},
|
|
@@ -12423,7 +12423,7 @@
|
|
|
12423
12423
|
"surface": "builders-hall",
|
|
12424
12424
|
"text": "Vote yes",
|
|
12425
12425
|
"file": "modules/hall-ui/public/board-room.js",
|
|
12426
|
-
"line":
|
|
12426
|
+
"line": 368,
|
|
12427
12427
|
"origin": "js-markup",
|
|
12428
12428
|
"confidence": "certain"
|
|
12429
12429
|
},
|
|
@@ -12927,7 +12927,7 @@
|
|
|
12927
12927
|
"surface": "builders-hall",
|
|
12928
12928
|
"text": "Why — the reason the author will act on",
|
|
12929
12929
|
"file": "modules/hall-ui/public/board-room.js",
|
|
12930
|
-
"line":
|
|
12930
|
+
"line": 368,
|
|
12931
12931
|
"origin": "js-markup",
|
|
12932
12932
|
"confidence": "certain"
|
|
12933
12933
|
},
|
|
@@ -13116,7 +13116,7 @@
|
|
|
13116
13116
|
"surface": "builders-hall",
|
|
13117
13117
|
"text": "You {…}. One vote per member.",
|
|
13118
13118
|
"file": "modules/hall-ui/public/board-room.js",
|
|
13119
|
-
"line":
|
|
13119
|
+
"line": 440,
|
|
13120
13120
|
"origin": "js-markup",
|
|
13121
13121
|
"confidence": "certain"
|
|
13122
13122
|
},
|
|
@@ -13278,7 +13278,7 @@
|
|
|
13278
13278
|
"surface": "builders-hall",
|
|
13279
13279
|
"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.",
|
|
13280
13280
|
"file": "modules/hall-ui/public/board-room.js",
|
|
13281
|
-
"line":
|
|
13281
|
+
"line": 366,
|
|
13282
13282
|
"origin": "js-markup",
|
|
13283
13283
|
"confidence": "certain"
|
|
13284
13284
|
},
|
|
@@ -14592,7 +14592,7 @@
|
|
|
14592
14592
|
"surface": "builders-hall",
|
|
14593
14593
|
"text": "objected to",
|
|
14594
14594
|
"file": "modules/hall-ui/public/board-room.js",
|
|
14595
|
-
"line":
|
|
14595
|
+
"line": 173,
|
|
14596
14596
|
"origin": "js-markup",
|
|
14597
14597
|
"confidence": "certain"
|
|
14598
14598
|
},
|
|
@@ -15744,7 +15744,7 @@
|
|
|
15744
15744
|
"surface": "builders-hall",
|
|
15745
15745
|
"text": "your vote",
|
|
15746
15746
|
"file": "modules/hall-ui/public/board-room.js",
|
|
15747
|
-
"line":
|
|
15747
|
+
"line": 168,
|
|
15748
15748
|
"origin": "js-markup",
|
|
15749
15749
|
"confidence": "certain"
|
|
15750
15750
|
},
|
|
@@ -16212,7 +16212,7 @@
|
|
|
16212
16212
|
"surface": "builders-hall",
|
|
16213
16213
|
"text": "{…} · who votes, and how it {…}",
|
|
16214
16214
|
"file": "modules/hall-ui/public/board-room.js",
|
|
16215
|
-
"line":
|
|
16215
|
+
"line": 449,
|
|
16216
16216
|
"origin": "js-markup",
|
|
16217
16217
|
"confidence": "certain"
|
|
16218
16218
|
},
|
|
@@ -2707,5 +2707,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2707
2707
|
landed since 1.20.23 with no explicit bump. run 36759918201. (task 1002620)
|
|
2708
2708
|
1.20.25 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2709
2709
|
landed since 1.20.24 with no explicit bump. run 36762454104. (task 1002620)
|
|
2710
|
+
1.20.26 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2711
|
+
landed since 1.20.25 with no explicit bump. run 36764011836. (task 1002620)
|
|
2710
2712
|
---------------------------------------------------------------------------
|
|
2711
2713
|
```
|
package/docs/page-readings.json
CHANGED
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"surface": "builders",
|
|
68
68
|
"title": "Board Room",
|
|
69
69
|
"states_read": ["room","out","sitting-meta","objection-form"],
|
|
70
|
-
"files_hash": "
|
|
70
|
+
"files_hash": "c1978638f2ef9558",
|
|
71
71
|
"reading_hash": "202a0cbb6bc54aac",
|
|
72
72
|
"counts": {"lines":56,"placed":26,"shared":5,"unplaced":25},
|
|
73
73
|
"omitted": {"data":9},
|
|
@@ -83,32 +83,32 @@
|
|
|
83
83
|
{"key":"L0008","section":"{…} Full Idea","text":"Full Idea","placement":"unplaced","occurrences":3},
|
|
84
84
|
{"key":"L0009","section":"{…} Full Idea","text":"open","placement":"placed","file":"modules/hall-ui/public/approval-queue.js","line":163,"string_id":"9a803563c948"},
|
|
85
85
|
{"key":"L0010","section":"{…} Full Idea","text":"closes in 73m","placement":"unplaced"},
|
|
86
|
-
{"key":"L0011","section":"{…} Full Idea","text":"Vote yes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
86
|
+
{"key":"L0011","section":"{…} Full Idea","text":"Vote yes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":368,"string_id":"b2937e14ee9b"},
|
|
87
87
|
{"key":"L0012","section":"{…} Full Idea","text":"Object…","placement":"unplaced"},
|
|
88
|
-
{"key":"L0013","section":"{…} Full Idea","text":"This sends the idea back to its author. Say why, and which section.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
89
|
-
{"key":"L0014","section":"{…} Full Idea","text":"Why — the reason the author will act on","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
90
|
-
{"key":"L0015","section":"{…} Full Idea","text":"Its author gave no scope recommendation.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
88
|
+
{"key":"L0013","section":"{…} Full Idea","text":"This sends the idea back to its author. Say why, and which section.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":368,"string_id":"69e8beced2ff","state":"objection-form"},
|
|
89
|
+
{"key":"L0014","section":"{…} Full Idea","text":"Why — the reason the author will act on","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":368,"string_id":"fbd8a815b808","attr":"placeholder","state":"objection-form"},
|
|
90
|
+
{"key":"L0015","section":"{…} Full Idea","text":"Its author gave no scope recommendation.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":300,"string_id":"a21788f84e29"},
|
|
91
91
|
{"key":"L0016","section":"{…} Full Idea","text":"{…} of {…}","placement":"placed","file":"modules/hall-ui/public/sky-panel.js","line":120,"string_id":"73c1372b43d9","occurrences":2},
|
|
92
92
|
{"key":"L0017","section":"{…} Full Idea","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":4},
|
|
93
93
|
{"key":"L0018","section":"{…} Full Idea","text":"What does the Big Picture look like","placement":"unplaced","occurrences":2},
|
|
94
94
|
{"key":"L0019","section":"{…} Full Idea","text":"Why did you have the Idea","placement":"unplaced","occurrences":2},
|
|
95
95
|
{"key":"L0020","section":"{…} Full Idea","text":"Why does the Idea Matter","placement":"unplaced","occurrences":2},
|
|
96
96
|
{"key":"L0021","section":"{…} Full Idea","text":"{…} project{…}","placement":"placed","file":"modules/hall-ui/public/fleet.js","line":39,"string_id":"771bef3d31b5","occurrences":3},
|
|
97
|
-
{"key":"L0022","section":"{…} Full Idea","text":"Cast objection","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
98
|
-
{"key":"L0023","section":"{…} Full Idea","text":"{…} · who votes, and how it {…}","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
97
|
+
{"key":"L0022","section":"{…} Full Idea","text":"Cast objection","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":368,"string_id":"bdca1f219e6f","state":"objection-form"},
|
|
98
|
+
{"key":"L0023","section":"{…} Full Idea","text":"{…} · who votes, and how it {…}","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":449,"string_id":"d73256fbc395","occurrences":3},
|
|
99
99
|
{"key":"L0024","section":"{…} Full Idea","text":"Every {…}","placement":"unplaced","state":"sitting-meta"},
|
|
100
100
|
{"key":"L0025","section":"{…} Full Idea","text":"{…} or {…}","placement":"unplaced","state":"sitting-meta"},
|
|
101
101
|
{"key":"L0026","section":"{…} Full Idea","text":"may vote on this.","placement":"unplaced","state":"sitting-meta"},
|
|
102
102
|
{"key":"L0027","section":"{…} Full Idea","text":"The sitting runs 1 day, closing early once every member has voted.","placement":"unplaced","state":"sitting-meta"},
|
|
103
|
-
{"key":"L0028","section":"{…} Full Idea","text":"It cleared the completeness grade at {…}/100.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
103
|
+
{"key":"L0028","section":"{…} Full Idea","text":"It cleared the completeness grade at {…}/100.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":331,"string_id":"bd8d04eb265c","state":"sitting-meta"},
|
|
104
104
|
{"key":"L0029","section":"{…} and {…}","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":4},
|
|
105
105
|
{"key":"L0030","section":"{…} and {…}","text":"Full Idea","placement":"unplaced"},
|
|
106
106
|
{"key":"L0031","section":"{…} and {…}","text":"open","placement":"placed","file":"modules/hall-ui/public/approval-queue.js","line":163,"string_id":"9a803563c948"},
|
|
107
107
|
{"key":"L0032","section":"{…} and {…}","text":"no clock","placement":"placed","file":"modules/hall-ui/public/government.js","line":607,"string_id":"b9f2e0d43c94"},
|
|
108
|
-
{"key":"L0033","section":"{…} and {…}","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.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
109
|
-
{"key":"L0034","section":"{…} and {…}","text":"Vote yes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
108
|
+
{"key":"L0033","section":"{…} and {…}","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.","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":366,"string_id":"c0a01a95e97e"},
|
|
109
|
+
{"key":"L0034","section":"{…} and {…}","text":"Vote yes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":368,"string_id":"b2937e14ee9b"},
|
|
110
110
|
{"key":"L0035","section":"{…} and {…}","text":"Object…","placement":"unplaced"},
|
|
111
|
-
{"key":"L0036","section":"{…} and {…}","text":"Its author recommends this becomes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
111
|
+
{"key":"L0036","section":"{…} and {…}","text":"Its author recommends this becomes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":299,"string_id":"680491549e47"},
|
|
112
112
|
{"key":"L0037","section":"{…} and {…}","text":"a {…} inside an existing goal","placement":"unplaced"},
|
|
113
113
|
{"key":"L0038","section":"{…} and {…}","text":". The board decides.","placement":"unplaced"},
|
|
114
114
|
{"key":"L0039","section":"{…} and {…}","text":"What made you have the idea","placement":"unplaced"},
|
|
@@ -116,16 +116,16 @@
|
|
|
116
116
|
{"key":"L0041","section":"{…} and {…}","text":"What should an {…} or artist know prior to building","placement":"unplaced"},
|
|
117
117
|
{"key":"L0042","section":"{…} and {…}","text":"{…} project{…}","placement":"placed","file":"modules/hall-ui/public/fleet.js","line":39,"string_id":"771bef3d31b5","occurrences":2},
|
|
118
118
|
{"key":"L0043","section":"{…} and {…}","text":"{…} of {…}","placement":"placed","file":"modules/hall-ui/public/sky-panel.js","line":120,"string_id":"73c1372b43d9","occurrences":2},
|
|
119
|
-
{"key":"L0044","section":"{…} and {…}","text":"{…} · who votes, and how it {…}","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
119
|
+
{"key":"L0044","section":"{…} and {…}","text":"{…} · who votes, and how it {…}","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":449,"string_id":"d73256fbc395"},
|
|
120
120
|
{"key":"L0045","section":"Constitutional amendment amendment","text":"Constitutional amendment","placement":"unplaced"},
|
|
121
121
|
{"key":"L0046","section":"Constitutional amendment amendment","text":"amendment","placement":"unplaced"},
|
|
122
122
|
{"key":"L0047","section":"Constitutional amendment amendment","text":"open","placement":"placed","file":"modules/hall-ui/public/approval-queue.js","line":163,"string_id":"9a803563c948"},
|
|
123
123
|
{"key":"L0048","section":"Constitutional amendment amendment","text":"closes in 15m","placement":"unplaced"},
|
|
124
124
|
{"key":"L0049","section":"Constitutional amendment amendment","text":"Proposes","placement":"unplaced"},
|
|
125
125
|
{"key":"L0050","section":"Constitutional amendment amendment","text":"{\"pass_rule\":\"{…}\",\"window_minutes\":2880}","placement":"unplaced"},
|
|
126
|
-
{"key":"L0051","section":"Constitutional amendment amendment","text":"Vote yes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
126
|
+
{"key":"L0051","section":"Constitutional amendment amendment","text":"Vote yes","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":368,"string_id":"b2937e14ee9b"},
|
|
127
127
|
{"key":"L0052","section":"Constitutional amendment amendment","text":"Object…","placement":"unplaced"},
|
|
128
|
-
{"key":"L0053","section":"Constitutional amendment amendment","text":"{…} · who votes, and how it {…}","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":
|
|
128
|
+
{"key":"L0053","section":"Constitutional amendment amendment","text":"{…} · who votes, and how it {…}","placement":"placed","file":"modules/hall-ui/public/board-room.js","line":449,"string_id":"d73256fbc395"},
|
|
129
129
|
{"key":"L0054","section":"Constitutional amendment amendment","text":"Closed sittings","placement":"unplaced"},
|
|
130
130
|
{"key":"L0055","section":"{…} Full Idea","text":"passed","placement":"placed","file":"modules/hall-ui/public/government.js","line":633,"string_id":"da5368d10f85"},
|
|
131
131
|
{"key":"L0056","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":464,"string_id":"1883ad7f07fc"}
|
|
@@ -148,6 +148,7 @@ async function postBoardAnnouncement(payload, deps = {}) {
|
|
|
148
148
|
const CHANNEL = 'board_room';
|
|
149
149
|
|
|
150
150
|
function subjectLabel(subjectType) {
|
|
151
|
+
if (subjectType === 'genesis_stage') return 'A genesis stage'; // task 1004429
|
|
151
152
|
return subjectType === 'constitutional_amendment' ? 'A constitutional amendment' : 'A Full Idea';
|
|
152
153
|
}
|
|
153
154
|
|
|
@@ -627,7 +627,12 @@ async function closeItem(item, { cause = 'vote' } = {}) {
|
|
|
627
627
|
});
|
|
628
628
|
if (!closed) return { closed: false, reason: 'already_closed' };
|
|
629
629
|
|
|
630
|
-
|
|
630
|
+
// A genesis stage ratifies no authored work — closing it is the project's
|
|
631
|
+
// decision that a stage is done, not a reward for the founder who put it up
|
|
632
|
+
// (task 1004429). Ideas and amendments pay exactly as before.
|
|
633
|
+
const authorKarma = outcome === 'passed' && closed.subject_type !== 'genesis_stage'
|
|
634
|
+
? await awardAuthorRatifyKarma(closed)
|
|
635
|
+
: null;
|
|
631
636
|
|
|
632
637
|
// R14: a PASSED amendment becomes the live constitution — applied AFTER the
|
|
633
638
|
// close commits (the board's decision is the durable record), loudly on
|
|
@@ -652,6 +657,7 @@ async function closeItem(item, { cause = 'vote' } = {}) {
|
|
|
652
657
|
try {
|
|
653
658
|
if (api.emitAsync) await api.emitAsync('board.item.passed', payload);
|
|
654
659
|
} catch (_) { /* emitAsync isolates listener errors; nothing that moves credits subscribes */ }
|
|
660
|
+
if (closed.subject_type === 'genesis_stage') await emitGenesisStagePassed(closed);
|
|
655
661
|
}
|
|
656
662
|
|
|
657
663
|
// R22's mirror event: BOTH outcomes, after commit, emitAsync. Separate from
|
|
@@ -806,6 +812,188 @@ async function proposeAmendment({
|
|
|
806
812
|
return { ok: true, amendment, item };
|
|
807
813
|
}
|
|
808
814
|
|
|
815
|
+
// ── the board's third subject: closing a genesis stage (task 1004429) ───────
|
|
816
|
+
//
|
|
817
|
+
// Spec decision D7 (docs/specs/bongos-v2-project-startup.md): a new project is
|
|
818
|
+
// founded through five stages, and when a stage's tasks are all finished its
|
|
819
|
+
// close is put to the constitution IN FORCE. It is an ordinary board item, and
|
|
820
|
+
// that is the whole design — the stage borrows every rule the board already
|
|
821
|
+
// has rather than bringing one of its own:
|
|
822
|
+
//
|
|
823
|
+
// • THE CONSTITUTION IS SNAPSHOTTED, like every item, so a charter adopted
|
|
824
|
+
// mid-sitting does not change how an already-open stage closes.
|
|
825
|
+
// • NO NEW PASS RULE, NO GENESIS BYPASS. Under the monarchy default
|
|
826
|
+
// (first_ratifier) the founder's single confirm closes the stage because
|
|
827
|
+
// the founder is the first ratifier — not because anything special-cases
|
|
828
|
+
// genesis. Under consent, majority or unanimous it is a real sitting, with
|
|
829
|
+
// the clock and turnout rules unchanged.
|
|
830
|
+
// • THE AUTHOR RULE IS UNCHANGED — which means it does not reach a stage. It
|
|
831
|
+
// is full_idea-scoped (authorRuleApplies, ADR 0191): it guards an author
|
|
832
|
+
// who GAINS from ratification, and closing a stage pays nobody. A stage is
|
|
833
|
+
// in the amendment's position, where §7 already says the one member of a
|
|
834
|
+
// monarchy must be able to decide their own item.
|
|
835
|
+
// • IT PAYS NOTHING. No credit (the reward port is only reached for a
|
|
836
|
+
// full_idea) and no ratify karma (closeItem skips it for a stage).
|
|
837
|
+
//
|
|
838
|
+
// The subject_id is the stage NUMBER. An instance is one project, so the
|
|
839
|
+
// number names a stage uniquely, and government_003's one-open-per-subject
|
|
840
|
+
// index then keeps at most one sitting per stage STRUCTURALLY — the same
|
|
841
|
+
// idempotency a Full Idea gets. A passed stage may be sat again later (the
|
|
842
|
+
// design's "second pass once your builders join"); the index is partial on
|
|
843
|
+
// open items only. government_020 widens the subject CHECK and bounds the range.
|
|
844
|
+
const GENESIS_STAGES = Object.freeze([
|
|
845
|
+
Object.freeze({ key: 'scope', number: 1, label: 'Scope' }),
|
|
846
|
+
Object.freeze({ key: 'strategize', number: 2, label: 'Strategize' }),
|
|
847
|
+
Object.freeze({ key: 'charter', number: 3, label: 'Charter' }),
|
|
848
|
+
Object.freeze({ key: 'legalize', number: 4, label: 'Legalize' }),
|
|
849
|
+
Object.freeze({ key: 'legislation', number: 5, label: 'Legislation' }),
|
|
850
|
+
]);
|
|
851
|
+
|
|
852
|
+
// "Finished" is the task's terminal states: shipped, or dropped — which the
|
|
853
|
+
// tasks table spells `abandoned` (migration 012; a merged-away task lands
|
|
854
|
+
// there too). `confirmed` is NOT finished: the work is graded but not landed.
|
|
855
|
+
const STAGE_TASK_FINISHED_STATUSES = Object.freeze(['shipped', 'abandoned']);
|
|
856
|
+
|
|
857
|
+
const stageByKey = (key) => GENESIS_STAGES.find((s) => s.key === key) || null;
|
|
858
|
+
const stageByNumber = (n) => GENESIS_STAGES.find((s) => s.number === Number(n)) || null;
|
|
859
|
+
|
|
860
|
+
// The pass rules, said in words for the founding band. Keyed by exactly
|
|
861
|
+
// config.js PASS_RULES (held equal by tests/government_board_genesis_stage.mjs,
|
|
862
|
+
// so a fifth rule cannot land without its words); an unknown rule in an old
|
|
863
|
+
// snapshot says so rather than guessing.
|
|
864
|
+
const PASS_RULE_WORDS = Object.freeze({
|
|
865
|
+
first_ratifier: 'One yes from any member decides it.',
|
|
866
|
+
consent: 'It passes unless a member objects, once everyone has voted or the clock runs out.',
|
|
867
|
+
majority: 'More than half of all members must say yes.',
|
|
868
|
+
unanimous: 'Everyone who votes must agree, and at least one member must say yes.',
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
// Put a finished stage to the board. Returns, never throws for a refusal:
|
|
872
|
+
// { opened: true, item, stage }
|
|
873
|
+
// { opened: false, reason: 'unknown_stage' | 'no_stage_tasks' }
|
|
874
|
+
// { opened: false, reason: 'stage_tasks_unfinished', unfinished: [{ task_id, status }] }
|
|
875
|
+
// { opened: false, reason: 'already_open', item, stage }
|
|
876
|
+
//
|
|
877
|
+
// THE FINISHED-TASKS GATE LIVES HERE, not in the caller — the same posture as
|
|
878
|
+
// the Full Idea bar: the genesis home says which tasks make up the stage, and
|
|
879
|
+
// this module checks them against the tasks table before anything is written.
|
|
880
|
+
// An empty list is refused rather than read as "nothing left to do", because a
|
|
881
|
+
// stage close with no tasks behind it would be exactly the bypass D7 rules out.
|
|
882
|
+
// `openedBy` is the founder (or whoever put it up); it may be null for an
|
|
883
|
+
// automatic open, the same as a grade-opened idea.
|
|
884
|
+
//
|
|
885
|
+
// WHO MAY CALL THIS is the caller's gate, by design, exactly as for
|
|
886
|
+
// openWindowForFullIdea: this is a port method with no HTTP route, and opening
|
|
887
|
+
// a sitting decides nothing — it only asks the board. Whoever exposes it to a
|
|
888
|
+
// person (the genesis home) must gate that route itself. The deciding wall is
|
|
889
|
+
// unchanged either way: only a member under the snapshot can vote it closed.
|
|
890
|
+
async function openGenesisStageClose({ stageKey, taskIds, openedBy = null } = {}) {
|
|
891
|
+
const stage = stageByKey(stageKey);
|
|
892
|
+
if (!stage) return { opened: false, reason: 'unknown_stage' };
|
|
893
|
+
const ids = Array.isArray(taskIds) ? taskIds.map(Number) : [];
|
|
894
|
+
if (!ids.length || !ids.every((n) => Number.isInteger(n) && n > 0)) {
|
|
895
|
+
return { opened: false, reason: 'no_stage_tasks' };
|
|
896
|
+
}
|
|
897
|
+
const unique = [...new Set(ids)];
|
|
898
|
+
const rows = await db.getTaskStatuses(unique);
|
|
899
|
+
const statusById = new Map(rows.map((r) => [String(r.id), r.status]));
|
|
900
|
+
const unfinished = unique
|
|
901
|
+
.filter((id) => !STAGE_TASK_FINISHED_STATUSES.includes(statusById.get(String(id))))
|
|
902
|
+
.map((id) => ({ task_id: String(id), status: statusById.get(String(id)) ?? null }));
|
|
903
|
+
if (unfinished.length) return { opened: false, reason: 'stage_tasks_unfinished', unfinished };
|
|
904
|
+
|
|
905
|
+
const { board } = loadGovernmentConfig();
|
|
906
|
+
const item = await db.openBoardItem({
|
|
907
|
+
subjectType: 'genesis_stage',
|
|
908
|
+
subjectId: stage.number,
|
|
909
|
+
openedBy: openedBy == null || openedBy === '' ? null : openedBy,
|
|
910
|
+
constitution: board,
|
|
911
|
+
windowMinutes: board.window_minutes,
|
|
912
|
+
});
|
|
913
|
+
if (!item) {
|
|
914
|
+
return { opened: false, reason: 'already_open', item: await db.getOpenBoardItem('genesis_stage', stage.number), stage };
|
|
915
|
+
}
|
|
916
|
+
await emitWindowOpened(item);
|
|
917
|
+
return { opened: true, item, stage };
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// The genesis home's cue (spec D7 / task 1004423): a stage's sitting PASSED,
|
|
921
|
+
// so mark it done and wake its room. Emitted after the close commits, beside
|
|
922
|
+
// the general `board.item.passed`, carrying what that contract deliberately
|
|
923
|
+
// does not — which stage. Best-effort by construction, like every board event:
|
|
924
|
+
// the durable fact is the `outcome = 'passed'` row, which genesisStageStatus
|
|
925
|
+
// reads, so a listener that missed the nudge can always recover the state.
|
|
926
|
+
async function emitGenesisStagePassed(closed) {
|
|
927
|
+
const stage = stageByNumber(closed.subject_id);
|
|
928
|
+
if (!stage) return;
|
|
929
|
+
try {
|
|
930
|
+
if (api.emitAsync) {
|
|
931
|
+
await api.emitAsync('board.genesis_stage.passed', {
|
|
932
|
+
item_id: String(closed.id),
|
|
933
|
+
stage_key: stage.key,
|
|
934
|
+
stage_number: stage.number,
|
|
935
|
+
passed_at: closed.closed_at || null,
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
} catch (_) { /* a nudge that fails is recoverable from the passed row */ }
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// "Who is this stage waiting on" — for the founding band. One entry per stage,
|
|
942
|
+
// from the LATEST sitting on it:
|
|
943
|
+
// state: 'not_put' (never put to the board) | 'sitting' | 'passed' | 'returned'
|
|
944
|
+
// and for a sitting, the members under the item's OWN snapshot (not the live
|
|
945
|
+
// config), split into who has voted and who is still to — per request,
|
|
946
|
+
// uncached, the ADR 0016 posture of every membership read here. Under monarchy
|
|
947
|
+
// `one_vote_decides` is true and `waiting_on` names everyone who could give
|
|
948
|
+
// that vote. The countdown is the database's (`remaining_seconds`); the client
|
|
949
|
+
// never does clock math. Membership is resolved once per distinct predicate.
|
|
950
|
+
async function genesisStageStatus() {
|
|
951
|
+
const items = await db.listLatestGenesisStageItems();
|
|
952
|
+
const open = items.filter((i) => !i.closed_at);
|
|
953
|
+
const votes = open.length ? await db.getVotesForItems(open.map((i) => i.id)) : [];
|
|
954
|
+
const membersByPredicate = new Map();
|
|
955
|
+
const stages = [];
|
|
956
|
+
for (const stage of GENESIS_STAGES) {
|
|
957
|
+
const item = items.find((i) => Number(i.subject_id) === stage.number);
|
|
958
|
+
const base = { key: stage.key, number: stage.number, label: stage.label };
|
|
959
|
+
if (!item) {
|
|
960
|
+
stages.push({ ...base, state: 'not_put', item_id: null, voted: [], waiting_on: [] });
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
const constitution = item.constitution || {};
|
|
964
|
+
const common = {
|
|
965
|
+
...base,
|
|
966
|
+
item_id: String(item.id),
|
|
967
|
+
opened_at: item.opened_at || null,
|
|
968
|
+
pass_rule: constitution.pass_rule || null,
|
|
969
|
+
needs: PASS_RULE_WORDS[constitution.pass_rule] || 'The rule this sitting runs under is not one this version knows; only its clock can close it.',
|
|
970
|
+
one_vote_decides: constitution.pass_rule === 'first_ratifier',
|
|
971
|
+
};
|
|
972
|
+
if (item.closed_at) {
|
|
973
|
+
stages.push({ ...common, state: item.outcome, closed_at: item.closed_at, voted: [], waiting_on: [] });
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
const key = String(constitution.membership || '');
|
|
977
|
+
if (!membersByPredicate.has(key)) membersByPredicate.set(key, await resolveBoardMembers(constitution));
|
|
978
|
+
const members = membersByPredicate.get(key);
|
|
979
|
+
const cast = votes.filter((v) => String(v.item_id) === String(item.id));
|
|
980
|
+
const castBy = new Map(cast.map((v) => [String(v.voter_id), v]));
|
|
981
|
+
stages.push({
|
|
982
|
+
...common,
|
|
983
|
+
state: 'sitting',
|
|
984
|
+
closes_at: item.closes_at || null,
|
|
985
|
+
remaining_seconds: item.remaining_seconds ?? null,
|
|
986
|
+
voted: cast.map((v) => ({
|
|
987
|
+
builder_id: String(v.voter_id), github_login: v.github_login, display_name: v.display_name, direction: v.direction,
|
|
988
|
+
})),
|
|
989
|
+
waiting_on: members.filter((m) => !castBy.has(String(m.builder_id))).map((m) => ({
|
|
990
|
+
builder_id: m.builder_id, github_login: m.github_login, display_name: m.display_name,
|
|
991
|
+
})),
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
return { stages };
|
|
995
|
+
}
|
|
996
|
+
|
|
809
997
|
// ── the Board Room view (BV1.R16 of goal 1000069, ADR 0175 §9) ──────────────
|
|
810
998
|
//
|
|
811
999
|
// One assembled read for the room: open items with their clock, the OPEN
|
|
@@ -851,7 +1039,17 @@ async function boardRoomView({ closedLimit = 20 } = {}) {
|
|
|
851
1039
|
});
|
|
852
1040
|
}
|
|
853
1041
|
const project = (item) => {
|
|
854
|
-
|
|
1042
|
+
// A genesis stage's subject is the stage itself — named from the one list,
|
|
1043
|
+
// so the room never mistakes it for an amendment (task 1004429).
|
|
1044
|
+
const subject = item.subject_type === 'genesis_stage'
|
|
1045
|
+
? (() => {
|
|
1046
|
+
const s = stageByNumber(item.subject_id);
|
|
1047
|
+
return s ? {
|
|
1048
|
+
stage_key: s.key, stage_number: s.number, label: s.label,
|
|
1049
|
+
author_id: item.opened_by == null ? null : String(item.opened_by),
|
|
1050
|
+
} : null;
|
|
1051
|
+
})()
|
|
1052
|
+
: item.subject_type === 'full_idea'
|
|
855
1053
|
? (() => {
|
|
856
1054
|
const r = ideaById.get(String(item.subject_id));
|
|
857
1055
|
return r ? {
|
|
@@ -1138,6 +1336,10 @@ async function pendingVotesFor(builderId) {
|
|
|
1138
1336
|
}
|
|
1139
1337
|
|
|
1140
1338
|
module.exports = {
|
|
1339
|
+
GENESIS_STAGES,
|
|
1340
|
+
PASS_RULE_WORDS,
|
|
1341
|
+
openGenesisStageClose,
|
|
1342
|
+
genesisStageStatus,
|
|
1141
1343
|
pendingVotesFor,
|
|
1142
1344
|
hasMajority,
|
|
1143
1345
|
majorityDecided,
|
package/modules/government/db.js
CHANGED
|
@@ -688,7 +688,12 @@ async function getExpiredOpenBoardItems(limit = 100) {
|
|
|
688
688
|
async function openBoardItemsAwaitingVoter(voterId) {
|
|
689
689
|
const { rows } = await pool.query(
|
|
690
690
|
`SELECT i.id, i.subject_type, i.subject_id, i.constitution, i.opened_at, i.closes_at,
|
|
691
|
-
|
|
691
|
+
-- A genesis stage has no author to hold back (task 1004429): the
|
|
692
|
+
-- founder who put it up IS the confirm monarchy waits on, so they
|
|
693
|
+
-- must be summoned, not skipped.
|
|
694
|
+
CASE WHEN i.subject_type = 'full_idea' THEN idea.captured_by
|
|
695
|
+
WHEN i.subject_type = 'genesis_stage' THEN NULL
|
|
696
|
+
ELSE i.opened_by END AS author_id,
|
|
692
697
|
(v.id IS NOT NULL) AS already_voted
|
|
693
698
|
FROM government_board_items i
|
|
694
699
|
LEFT JOIN idea_inbox idea
|
|
@@ -703,6 +708,40 @@ async function openBoardItemsAwaitingVoter(voterId) {
|
|
|
703
708
|
return rows;
|
|
704
709
|
}
|
|
705
710
|
|
|
711
|
+
// --- genesis stages (task 1004429 / BV2.PS04) --------------------------------
|
|
712
|
+
|
|
713
|
+
// The status of each task a stage names, for the "are this stage's tasks all
|
|
714
|
+
// finished" gate. `tasks` is a core table; READING it is the sanctioned pattern
|
|
715
|
+
// (the boundary is code imports, not table access). An id with no row comes
|
|
716
|
+
// back absent, and the caller counts that as unfinished — a stage cannot close
|
|
717
|
+
// on a task nobody can find.
|
|
718
|
+
async function getTaskStatuses(taskIds) {
|
|
719
|
+
if (!taskIds || !taskIds.length) return [];
|
|
720
|
+
const { rows } = await pool.query(
|
|
721
|
+
`SELECT id, status FROM tasks WHERE id = ANY($1::bigint[])`,
|
|
722
|
+
[taskIds],
|
|
723
|
+
);
|
|
724
|
+
return rows;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// The latest sitting for each genesis stage (one row per stage number, newest
|
|
728
|
+
// first within it), with the same database-clock fields the room reads. Served
|
|
729
|
+
// by government_020's partial index, keyed to exactly this ORDER BY — the table
|
|
730
|
+
// holds every idea and amendment sitting forever, and without it this read
|
|
731
|
+
// would scan all of that history to find a handful of stage rows.
|
|
732
|
+
async function listLatestGenesisStageItems() {
|
|
733
|
+
const { rows } = await pool.query(
|
|
734
|
+
`SELECT DISTINCT ON (subject_id) *,
|
|
735
|
+
(closes_at IS NOT NULL AND closes_at < now()) AS expired,
|
|
736
|
+
CASE WHEN closes_at IS NULL THEN NULL
|
|
737
|
+
ELSE GREATEST(0, EXTRACT(EPOCH FROM (closes_at - now()))::int) END AS remaining_seconds
|
|
738
|
+
FROM government_board_items
|
|
739
|
+
WHERE subject_type = 'genesis_stage'
|
|
740
|
+
ORDER BY subject_id, opened_at DESC, id DESC`,
|
|
741
|
+
);
|
|
742
|
+
return rows;
|
|
743
|
+
}
|
|
744
|
+
|
|
706
745
|
// Author-ratification karma (+3, board.js AUTHOR_RATIFY_KARMA_DELTA) — fired by
|
|
707
746
|
// the R10 close on a passed item. IDEMPOTENT: the reason key is unique per item
|
|
708
747
|
// and the INSERT is one atomic WHERE-NOT-EXISTS statement (the economy
|
|
@@ -731,6 +770,8 @@ async function insertVoteKarma({ voterId, itemId, delta }) {
|
|
|
731
770
|
|
|
732
771
|
module.exports = {
|
|
733
772
|
openBoardItemsAwaitingVoter,
|
|
773
|
+
getTaskStatuses,
|
|
774
|
+
listLatestGenesisStageItems,
|
|
734
775
|
getPermissionKeysForBuilder,
|
|
735
776
|
getRankKeysForBuilder,
|
|
736
777
|
getSeededRanks,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
-- government_020_genesis_stage_subject.sql — the board's third subject: closing
|
|
2
|
+
-- a genesis stage (task 1004429 / BV2.PS04, spec docs/specs/bongos-v2-project-
|
|
3
|
+
-- startup.md decision D7).
|
|
4
|
+
--
|
|
5
|
+
-- government_003 built `subject_type` so that a third subject would be "a row,
|
|
6
|
+
-- not a migration". That held for the ROWS — no new table, no new column — but
|
|
7
|
+
-- the column carries a CHECK naming the two subjects it knew about, so the
|
|
8
|
+
-- vocabulary itself still has to widen here. That is the whole of this file.
|
|
9
|
+
--
|
|
10
|
+
-- A genesis stage's subject_id is its STAGE NUMBER (1 scope · 2 strategize ·
|
|
11
|
+
-- 3 charter · 4 legalize · 5 legislation — board.js GENESIS_STAGES is the one
|
|
12
|
+
-- list). An instance is one project, so the number names the stage uniquely,
|
|
13
|
+
-- and that is what lets government_003's partial unique index keep AT MOST ONE
|
|
14
|
+
-- OPEN SITTING PER STAGE structurally, the same way it keeps one per idea: a
|
|
15
|
+
-- double-fired "the stage's tasks are done" opens exactly one item. A stage
|
|
16
|
+
-- that passed can be sat again later (the "second pass once your builders
|
|
17
|
+
-- join" in the design of record) — the index is partial on open items only.
|
|
18
|
+
--
|
|
19
|
+
-- Nothing about deciding changes. The item snapshots the constitution in force
|
|
20
|
+
-- like every other item, the pass rules are the same four, and the author and
|
|
21
|
+
-- clock rules are the ones every item runs under.
|
|
22
|
+
--
|
|
23
|
+
-- Additive, idempotent: dropping and re-adding a CHECK that only WIDENS admits
|
|
24
|
+
-- every row already in the table.
|
|
25
|
+
|
|
26
|
+
BEGIN;
|
|
27
|
+
|
|
28
|
+
ALTER TABLE government_board_items
|
|
29
|
+
DROP CONSTRAINT IF EXISTS government_board_items_subject_type_check;
|
|
30
|
+
|
|
31
|
+
ALTER TABLE government_board_items
|
|
32
|
+
ADD CONSTRAINT government_board_items_subject_type_check
|
|
33
|
+
CHECK (subject_type IN ('full_idea', 'constitutional_amendment', 'genesis_stage'));
|
|
34
|
+
|
|
35
|
+
-- The stage number is a small closed range; a stray id would be a sitting on a
|
|
36
|
+
-- stage that does not exist. Scoped to genesis_stage so the other subjects'
|
|
37
|
+
-- ids (idea and amendment row ids) are untouched.
|
|
38
|
+
ALTER TABLE government_board_items
|
|
39
|
+
DROP CONSTRAINT IF EXISTS government_board_items_genesis_stage_number;
|
|
40
|
+
|
|
41
|
+
ALTER TABLE government_board_items
|
|
42
|
+
ADD CONSTRAINT government_board_items_genesis_stage_number
|
|
43
|
+
CHECK (subject_type <> 'genesis_stage' OR subject_id BETWEEN 1 AND 5);
|
|
44
|
+
|
|
45
|
+
-- Serves the founding band's read (db.js listLatestGenesisStageItems: the latest
|
|
46
|
+
-- sitting per stage, DISTINCT ON subject_id ORDER BY subject_id, opened_at DESC,
|
|
47
|
+
-- id DESC). The table only grows — every Full Idea and amendment sitting stays
|
|
48
|
+
-- forever — so a read that filters by subject_type alone would scan that whole
|
|
49
|
+
-- history to find at most a handful of stage rows. Partial on the one subject it
|
|
50
|
+
-- asks for and keyed to its ORDER BY exactly, the government_006/018 shape.
|
|
51
|
+
CREATE INDEX IF NOT EXISTS government_board_items_genesis_stage_latest
|
|
52
|
+
ON government_board_items (subject_id, opened_at DESC, id DESC)
|
|
53
|
+
WHERE subject_type = 'genesis_stage';
|
|
54
|
+
|
|
55
|
+
COMMIT;
|