@bongos/core 1.24.1 → 1.24.2
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 -39
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +6 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/adr/0385-money-lives-with-a-processor-credits-stay-a-project-meter.md +12 -1
- package/docs/adr/README.md +1 -1
- package/docs/api/openapi.json +140 -1
- package/docs/api-reference.md +5 -2
- package/docs/copy-inventory.md +11 -6
- package/docs/copy-registry.json +63 -8
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +2 -1
- package/docs/page-readings.json +29 -20
- package/modules/government/access-routes.js +70 -0
- package/modules/government/access.js +88 -0
- package/modules/government/asks.js +5 -2
- package/modules/government/db.js +38 -0
- package/modules/government/docket.js +12 -2
- package/modules/government/migrations/government_036_access_holders.sql +33 -0
- package/modules/government/routes/government.js +9 -0
- package/modules/government/routing.js +11 -3
- package/modules/government/waiting.js +4 -0
- package/modules/hall-ui/public/blockers-by-person.js +2 -0
- package/modules/hall-ui/public/government-access.js +108 -0
- package/modules/hall-ui/public/government.html +21 -0
- package/modules/hall-ui/records/blockers-people.md +14 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +10 -0
- package/scripts/bongos/government-walk.js +3 -0
- package/src/bongos/route-rank-check.js +4 -0
- package/src/module-api.js +1 -1
- package/tests/access_holders.mjs +214 -0
- package/tests/government_board_membership.mjs +4 -1
- package/tests/government_routes.mjs +1 -1
- package/tests/government_seed.mjs +1 -0
package/docs/copy-inventory.md
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
> Deterministic on a given tree — regenerate and commit rather than hand-editing.
|
|
5
5
|
> Machine-readable twin: [`copy-registry.json`](copy-registry.json).
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**3111 strings** across 3 surfaces · 2450 distinct pieces of copy · 3111 certain, 0 heuristic · 261 shown in more than one place.
|
|
8
8
|
|
|
9
9
|
## Surfaces
|
|
10
10
|
|
|
11
11
|
| surface | lives at | files | strings |
|
|
12
12
|
|---|---|---|---|
|
|
13
|
-
| `builders-hall` | `modules/hall-ui/public` |
|
|
13
|
+
| `builders-hall` | `modules/hall-ui/public` | 160 | 2013 |
|
|
14
14
|
| `status-dashboard` | `modules/status-ui/public` | 4 | 91 |
|
|
15
15
|
| `landing` | `modules/public-landing/public` | 10 | 1007 |
|
|
16
16
|
|
|
@@ -76,9 +76,9 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
76
76
|
| `eff70e59d12e` | 4x | Task {…} | modules/hall-ui/public/task.js:491, modules/hall-ui/public/task.js:527, modules/hall-ui/public/task.js:1022 |
|
|
77
77
|
| `72a29648399b` | 4x | Tweak a page | modules/hall-ui/public/tweak-editor.html:32, modules/hall-ui/public/tweak-editor.html:32, modules/hall-ui/public/tweak-editor.html:55 |
|
|
78
78
|
| `ed394c66d49c` | 4x | What the choice changes | modules/public-landing/public/projects.html:1342, modules/public-landing/public/projects.html:1430, modules/public-landing/public/projects.html:1442 |
|
|
79
|
-
| … | |
|
|
79
|
+
| … | | _221 more in the JSON registry_ | |
|
|
80
80
|
|
|
81
|
-
## `builders-hall` —
|
|
81
|
+
## `builders-hall` — 2013 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -251,6 +251,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
251
251
|
| `da910fee01d7` | certain | Browse goals → | `modules/hall-ui/public/goals.js:105` |
|
|
252
252
|
| `25b4ab94728a` | certain | Build from your own machine — get a copy of the project, sign in once, then work in Claude. | `modules/hall-ui/public/hall-render.js:817` |
|
|
253
253
|
| `08bfcc9198ba` | certain | Builder id | `modules/hall-ui/public/government.html:107` |
|
|
254
|
+
| `08bfcc9198ba` | certain | Builder id | `modules/hall-ui/public/government.html:127` |
|
|
254
255
|
| `fd1903586e8e` | certain | Builders hall · this session | `modules/hall-ui/public/craft-mode.js:670` |
|
|
255
256
|
| `306bc316184d` | certain | Builders you may know | `modules/hall-ui/public/profile-connections.js:158` |
|
|
256
257
|
| `60b248791dc3` | certain | Builders — {{worldName}} | `modules/hall-ui/public/roster.html:6` |
|
|
@@ -270,7 +271,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
270
271
|
| `181cfa50b127` | certain | By the numbers | `modules/hall-ui/public/profile.html:64` |
|
|
271
272
|
| `a015a43bfc7f` | certain | CLI access | `modules/hall-ui/public/settings.html:141` |
|
|
272
273
|
| `09e863eb90af` | certain | Can: {…} | `modules/hall-ui/public/government.js:911` |
|
|
273
|
-
| `084eb64bce72` | certain | Capabilities held by a machine identity, never by a person or a rank. Shown for completeness; nothing here can | `modules/hall-ui/public/government.html:
|
|
274
|
+
| `084eb64bce72` | certain | Capabilities held by a machine identity, never by a person or a rank. Shown for completeness; nothing here can | `modules/hall-ui/public/government.html:141` |
|
|
274
275
|
| `c31f00bce184` | certain | Card added. | `modules/hall-ui/public/settings-passkeys.js:92` |
|
|
275
276
|
| `3cd965c9e3b1` | certain | Card name | `modules/hall-ui/public/settings.html:186` |
|
|
276
277
|
| `6fad39976eaf` | certain | Card removed. | `modules/hall-ui/public/settings-passkeys.js:59` |
|
|
@@ -757,7 +758,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
757
758
|
| `9da6a9b67eea` | certain | Look around first | `modules/hall-ui/public/welcome-boot.js:197` |
|
|
758
759
|
| `4017ee030bc2` | certain | Lookback window | `modules/hall-ui/public/profile-charts.js:540` |
|
|
759
760
|
| `73677b6839d7` | certain | Looks good | `modules/hall-ui/public/studio.js:196` |
|
|
760
|
-
| `06b4ede3fb58` | certain | Machine principals | `modules/hall-ui/public/government.html:
|
|
761
|
+
| `06b4ede3fb58` | certain | Machine principals | `modules/hall-ui/public/government.html:140` |
|
|
761
762
|
| `b8a2b32fdbb1` | certain | Main role | `modules/hall-ui/public/settings.html:125` |
|
|
762
763
|
| `4088dac7b364` | certain | Make it a law | `modules/hall-ui/public/my-agents.js:418` |
|
|
763
764
|
| `d535454fac6d` | certain | Make it again | `modules/hall-ui/public/ai-studio.js:416` |
|
|
@@ -874,6 +875,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
874
875
|
| `1967d2176f2e` | certain | Nobody has an open ask right now. | `modules/hall-ui/public/collab.html:95` |
|
|
875
876
|
| `c65c9f5c02e3` | certain | Nobody is waiting on you. Requests reach you by role — | `modules/hall-ui/public/collab.html:54` |
|
|
876
877
|
| `d5cf262e671d` | certain | Nobody is writing this page. Take it, and every line here is yours to type over. | `modules/hall-ui/public/tweak-editor.js:323` |
|
|
878
|
+
| `380b924cd4a0` | certain | Nobody recorded — it falls to the owner | `modules/hall-ui/public/government-access.js:35` |
|
|
877
879
|
| `91c409a4aea5` | certain | Nobody vouched for this name — anyone can file a request naming anyone. Confirm who is asking before you admit | `modules/hall-ui/public/watch.js:397` |
|
|
878
880
|
| `4016b9cf8d57` | certain | None of your asks have been settled yet. | `modules/hall-ui/public/collab.html:145` |
|
|
879
881
|
| `36c97d6094bf` | certain | None yet. Yours will be the first. | `modules/hall-ui/public/lightbulb.js:90` |
|
|
@@ -983,6 +985,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
983
985
|
| `a2bd76ae030d` | certain | Opens this project's upgrade{…}: you see what would move, confirm it, and it installs, checks itself, and roll | `modules/hall-ui/public/settings-software-update.js:130` |
|
|
984
986
|
| `569980a263ae` | certain | Optional. This is the ledger link — a copy change ships as a task like every other change. | `modules/hall-ui/public/copy-desk.html:130` |
|
|
985
987
|
| `f2e9e8f21e8e` | certain | Or add someone without asking… | `modules/hall-ui/public/goals-render.js:1258` |
|
|
988
|
+
| `8b1e7aae1e5f` | certain | Outside access | `modules/hall-ui/public/government.html:123` |
|
|
989
|
+
| `20cf1adf4154` | certain | Outside access could not be read just now. | `modules/hall-ui/public/government-access.js:72` |
|
|
986
990
|
| `d89d1923ecf9` | certain | Over the last {…} days | `modules/hall-ui/public/watch.js:1287` |
|
|
987
991
|
| `695ffff13730` | certain | Over time | `modules/hall-ui/public/profile.html:76` |
|
|
988
992
|
| `5ded44da0cc5` | certain | Override requests | `modules/hall-ui/public/watch.html:137` |
|
|
@@ -1592,6 +1596,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1592
1596
|
| `f9ab9eb870a6` | certain | While you wait: install the Claude desktop app at | `modules/hall-ui/public/hall-render.js:471` |
|
|
1593
1597
|
| `74d8c58ef235` | certain | Who builds on this project, ranked by work shipped. This project's own ledger — everyone who builds here is li | `modules/hall-ui/public/roster.html:28` |
|
|
1594
1598
|
| `b79ea4077bc5` | certain | Who can use it | `modules/hall-ui/public/settings.html:306` |
|
|
1599
|
+
| `c6d830ca9e08` | certain | Who holds the keys outside the project, so a blocker that needs one reaches them. This is a record of who to a | `modules/hall-ui/public/government.html:124` |
|
|
1595
1600
|
| `045c629729b7` | certain | Who’s on this goal, and what they’ve contributed to it. | `modules/hall-ui/public/goals.html:219` |
|
|
1596
1601
|
| `5cc53380b1e8` | certain | Why (optional) | `modules/hall-ui/public/goals-render.js:1029` |
|
|
1597
1602
|
| `62ab2e60f629` | certain | Why is this better? | `modules/hall-ui/public/copy-desk.html:164` |
|
package/docs/copy-registry.json
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
"generator": "scripts/bongos/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 3111,
|
|
7
|
+
"unique_ids": 2450,
|
|
8
|
+
"certain": 3111,
|
|
9
9
|
"heuristic": 0,
|
|
10
|
-
"duplicate_ids":
|
|
10
|
+
"duplicate_ids": 261
|
|
11
11
|
},
|
|
12
12
|
"surfaces": {
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
|
-
"files":
|
|
16
|
-
"strings":
|
|
15
|
+
"files": 160,
|
|
16
|
+
"strings": 2013
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -1258,6 +1258,16 @@
|
|
|
1258
1258
|
"modules/hall-ui/public/goals.js:105"
|
|
1259
1259
|
]
|
|
1260
1260
|
},
|
|
1261
|
+
{
|
|
1262
|
+
"id": "08bfcc9198ba",
|
|
1263
|
+
"surface": "builders-hall",
|
|
1264
|
+
"text": "Builder id",
|
|
1265
|
+
"occurrences": 2,
|
|
1266
|
+
"at": [
|
|
1267
|
+
"modules/hall-ui/public/government.html:107",
|
|
1268
|
+
"modules/hall-ui/public/government.html:127"
|
|
1269
|
+
]
|
|
1270
|
+
},
|
|
1261
1271
|
{
|
|
1262
1272
|
"id": "60b248791dc3",
|
|
1263
1273
|
"surface": "builders-hall",
|
|
@@ -4561,6 +4571,15 @@
|
|
|
4561
4571
|
"origin": "html-text",
|
|
4562
4572
|
"confidence": "certain"
|
|
4563
4573
|
},
|
|
4574
|
+
{
|
|
4575
|
+
"id": "08bfcc9198ba",
|
|
4576
|
+
"surface": "builders-hall",
|
|
4577
|
+
"text": "Builder id",
|
|
4578
|
+
"file": "modules/hall-ui/public/government.html",
|
|
4579
|
+
"line": 127,
|
|
4580
|
+
"origin": "html-text",
|
|
4581
|
+
"confidence": "certain"
|
|
4582
|
+
},
|
|
4564
4583
|
{
|
|
4565
4584
|
"id": "fd1903586e8e",
|
|
4566
4585
|
"surface": "builders-hall",
|
|
@@ -4737,7 +4756,7 @@
|
|
|
4737
4756
|
"surface": "builders-hall",
|
|
4738
4757
|
"text": "Capabilities held by a machine identity, never by a person or a rank. Shown for completeness; nothing here can be granted.",
|
|
4739
4758
|
"file": "modules/hall-ui/public/government.html",
|
|
4740
|
-
"line":
|
|
4759
|
+
"line": 141,
|
|
4741
4760
|
"origin": "html-text",
|
|
4742
4761
|
"confidence": "certain"
|
|
4743
4762
|
},
|
|
@@ -9120,7 +9139,7 @@
|
|
|
9120
9139
|
"surface": "builders-hall",
|
|
9121
9140
|
"text": "Machine principals",
|
|
9122
9141
|
"file": "modules/hall-ui/public/government.html",
|
|
9123
|
-
"line":
|
|
9142
|
+
"line": 140,
|
|
9124
9143
|
"origin": "html-text",
|
|
9125
9144
|
"confidence": "certain"
|
|
9126
9145
|
},
|
|
@@ -10168,6 +10187,15 @@
|
|
|
10168
10187
|
"origin": "js-markup",
|
|
10169
10188
|
"confidence": "certain"
|
|
10170
10189
|
},
|
|
10190
|
+
{
|
|
10191
|
+
"id": "380b924cd4a0",
|
|
10192
|
+
"surface": "builders-hall",
|
|
10193
|
+
"text": "Nobody recorded — it falls to the owner",
|
|
10194
|
+
"file": "modules/hall-ui/public/government-access.js",
|
|
10195
|
+
"line": 35,
|
|
10196
|
+
"origin": "js-markup",
|
|
10197
|
+
"confidence": "certain"
|
|
10198
|
+
},
|
|
10171
10199
|
{
|
|
10172
10200
|
"id": "91c409a4aea5",
|
|
10173
10201
|
"surface": "builders-hall",
|
|
@@ -11149,6 +11177,24 @@
|
|
|
11149
11177
|
"origin": "js-markup",
|
|
11150
11178
|
"confidence": "certain"
|
|
11151
11179
|
},
|
|
11180
|
+
{
|
|
11181
|
+
"id": "8b1e7aae1e5f",
|
|
11182
|
+
"surface": "builders-hall",
|
|
11183
|
+
"text": "Outside access",
|
|
11184
|
+
"file": "modules/hall-ui/public/government.html",
|
|
11185
|
+
"line": 123,
|
|
11186
|
+
"origin": "html-text",
|
|
11187
|
+
"confidence": "certain"
|
|
11188
|
+
},
|
|
11189
|
+
{
|
|
11190
|
+
"id": "20cf1adf4154",
|
|
11191
|
+
"surface": "builders-hall",
|
|
11192
|
+
"text": "Outside access could not be read just now.",
|
|
11193
|
+
"file": "modules/hall-ui/public/government-access.js",
|
|
11194
|
+
"line": 72,
|
|
11195
|
+
"origin": "js-markup",
|
|
11196
|
+
"confidence": "certain"
|
|
11197
|
+
},
|
|
11152
11198
|
{
|
|
11153
11199
|
"id": "d89d1923ecf9",
|
|
11154
11200
|
"surface": "builders-hall",
|
|
@@ -16630,6 +16676,15 @@
|
|
|
16630
16676
|
"origin": "html-text",
|
|
16631
16677
|
"confidence": "certain"
|
|
16632
16678
|
},
|
|
16679
|
+
{
|
|
16680
|
+
"id": "c6d830ca9e08",
|
|
16681
|
+
"surface": "builders-hall",
|
|
16682
|
+
"text": "Who holds the keys outside the project, so a blocker that needs one reaches them. This is a record of who to ask: it grants nothing here and stores no password or token.",
|
|
16683
|
+
"file": "modules/hall-ui/public/government.html",
|
|
16684
|
+
"line": 124,
|
|
16685
|
+
"origin": "html-text",
|
|
16686
|
+
"confidence": "certain"
|
|
16687
|
+
},
|
|
16633
16688
|
{
|
|
16634
16689
|
"id": "045c629729b7",
|
|
16635
16690
|
"surface": "builders-hall",
|
|
@@ -3264,5 +3264,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3264
3264
|
The lifecycle feed and claim gate apply it. Additive, so MINOR.
|
|
3265
3265
|
1.24.1 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3266
3266
|
landed since 1.24.0 with no explicit bump. run 40985289858. (task 1002620)
|
|
3267
|
+
1.24.2 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3268
|
+
landed since 1.24.1 with no explicit bump. run 41000649913. (task 1002620)
|
|
3267
3269
|
---------------------------------------------------------------------------
|
|
3268
3270
|
```
|
package/docs/page-inventory.json
CHANGED
|
@@ -611,7 +611,8 @@
|
|
|
611
611
|
"modules/hall-ui/public/palette.js",
|
|
612
612
|
"modules/hall-ui/public/hall-kit.js",
|
|
613
613
|
"modules/hall-ui/public/board-lib.js",
|
|
614
|
-
"modules/hall-ui/public/government.js"
|
|
614
|
+
"modules/hall-ui/public/government.js",
|
|
615
|
+
"modules/hall-ui/public/government-access.js"
|
|
615
616
|
],
|
|
616
617
|
"states_files": [
|
|
617
618
|
"modules/hall-ui/public/government.states.json"
|
package/docs/page-readings.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"adr": "0341 D3",
|
|
6
6
|
"reread": "node scripts/bongos/page-reader.js --stale",
|
|
7
7
|
"how": ["Generated by node scripts/bongos/page-reader.js (BV2.TW04, task 1004315; ADR 0341 D3). Do not hand-edit.","One entry per page of docs/page-inventory.json. Lines are in page order: the default state first, and a line only another state shows sits where that state shows it, with its state named.","placement: placed = a docs/copy-registry.json row renders it (file, line, string_id); shared = shell text every page of the surface carries (top bar, sidebar, footer), with its row when the registry has one; unplaced = the registry cannot place it (a rewrite becomes an engineer task, ADR 0341 D4).","text keeps {…} where the registry row has a hole, or where a value the page read from its API was filled in. A string that is only such data is counted under omitted.data, not listed.","reading_hash is over the non-shared lines (key, section, text); files_hash is over the page's inventory files. --check compares files_hash only and never opens a browser.","Rendered through the hall-preview harness (hall) and the ui-design kit stub (landing, status), at 1280x800 with the clock pinned, so a reading is the fixture world's page."],
|
|
8
|
-
"counts": {"pages":48,"lines":
|
|
8
|
+
"counts": {"pages":48,"lines":5456,"placed":1683,"shared":427,"unplaced":3346,"omitted_data":3730},
|
|
9
9
|
"pages": [
|
|
10
10
|
{
|
|
11
11
|
"id": "builders:ai-studio",
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"surface": "builders",
|
|
161
161
|
"title": "Blockers",
|
|
162
162
|
"states_read": ["default"],
|
|
163
|
-
"files_hash": "
|
|
163
|
+
"files_hash": "439934ddd9d1a4d2",
|
|
164
164
|
"reading_hash": "c393e54845d545ee",
|
|
165
165
|
"counts": {"lines":34,"placed":14,"shared":8,"unplaced":12},
|
|
166
166
|
"omitted": {"data":49},
|
|
@@ -327,7 +327,7 @@
|
|
|
327
327
|
"surface": "builders",
|
|
328
328
|
"title": "Collab",
|
|
329
329
|
"states_read": ["board","out"],
|
|
330
|
-
"files_hash": "
|
|
330
|
+
"files_hash": "2e20ca6774af82fc",
|
|
331
331
|
"reading_hash": "71803b40b959a9b9",
|
|
332
332
|
"counts": {"lines":80,"placed":31,"shared":7,"unplaced":42},
|
|
333
333
|
"omitted": {"data":21},
|
|
@@ -1456,10 +1456,10 @@
|
|
|
1456
1456
|
"surface": "builders",
|
|
1457
1457
|
"title": "Government",
|
|
1458
1458
|
"states_read": ["constitution","out","permissions","board","constitution-tab"],
|
|
1459
|
-
"files_hash": "
|
|
1460
|
-
"reading_hash": "
|
|
1461
|
-
"counts": {"lines":
|
|
1462
|
-
"omitted": {"data":
|
|
1459
|
+
"files_hash": "25c1dc0003420a7c",
|
|
1460
|
+
"reading_hash": "18d8e460088c2550",
|
|
1461
|
+
"counts": {"lines":63,"placed":24,"shared":8,"unplaced":31},
|
|
1462
|
+
"omitted": {"data":274},
|
|
1463
1463
|
"notes": ["permissions: an action failed (page.click: Timeout 6000ms exceeded.); read as it stood","board: an action failed (page.click: Timeout 6000ms exceeded.); read as it stood","constitution-tab: an action failed (page.click: Timeout 6000ms exceeded.); read as it stood"],
|
|
1464
1464
|
"lines": [
|
|
1465
1465
|
{"key":"L0001","section":"skip link","text":"Skip to content","placement":"shared","file":"modules/hall-ui/public/shell.js","line":868,"string_id":"87cee78ca134"},
|
|
@@ -1503,19 +1503,28 @@
|
|
|
1503
1503
|
{"key":"L0039","section":"Assignments","text":"{…} · via rank","placement":"unplaced","occurrences":2},
|
|
1504
1504
|
{"key":"L0040","section":"Assignments","text":"rank","placement":"unplaced","occurrences":3},
|
|
1505
1505
|
{"key":"L0041","section":"Assignments","text":"Remove","placement":"unplaced"},
|
|
1506
|
-
{"key":"L0042","section":"
|
|
1507
|
-
{"key":"L0043","section":"
|
|
1508
|
-
{"key":"L0044","section":"
|
|
1509
|
-
{"key":"L0045","section":"
|
|
1510
|
-
{"key":"L0046","section":"
|
|
1511
|
-
{"key":"L0047","section":"
|
|
1512
|
-
{"key":"L0048","section":"
|
|
1513
|
-
{"key":"L0049","section":"
|
|
1514
|
-
{"key":"L0050","section":"
|
|
1515
|
-
{"key":"L0051","section":"
|
|
1516
|
-
{"key":"L0052","section":"
|
|
1517
|
-
{"key":"L0053","section":"
|
|
1518
|
-
{"key":"L0054","section":"
|
|
1506
|
+
{"key":"L0042","section":"Outside access","text":"Outside access","placement":"placed","file":"modules/hall-ui/public/government.html","line":123,"string_id":"8b1e7aae1e5f"},
|
|
1507
|
+
{"key":"L0043","section":"Outside access","text":"Who holds the keys outside the project, so a blocker that needs one reaches them. This is a record of who to ask: it grants nothing here and stores no password or token.","placement":"placed","file":"modules/hall-ui/public/government.html","line":124,"string_id":"c6d830ca9e08"},
|
|
1508
|
+
{"key":"L0044","section":"Outside access","text":"Builder id","placement":"placed","file":"modules/hall-ui/public/government.html","line":107,"string_id":"08bfcc9198ba"},
|
|
1509
|
+
{"key":"L0045","section":"Outside access","text":"e.g. 95","placement":"unplaced","attr":"placeholder"},
|
|
1510
|
+
{"key":"L0046","section":"Outside access","text":"Access","placement":"unplaced"},
|
|
1511
|
+
{"key":"L0047","section":"Outside access","text":"Record","placement":"unplaced"},
|
|
1512
|
+
{"key":"L0048","section":"Outside access","text":"Remove","placement":"unplaced","occurrences":2},
|
|
1513
|
+
{"key":"L0049","section":"Outside access","text":"Nobody recorded — it falls to the owner","placement":"placed","file":"modules/hall-ui/public/government-access.js","line":35,"string_id":"380b924cd4a0","occurrences":4},
|
|
1514
|
+
{"key":"L0050","section":"Outside access","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232"},
|
|
1515
|
+
{"key":"L0051","section":"Machine principals","text":"Machine principals","placement":"placed","file":"modules/hall-ui/public/government.html","line":140,"string_id":"06b4ede3fb58"},
|
|
1516
|
+
{"key":"L0052","section":"Machine principals","text":"Capabilities held by a machine identity, never by a person or a rank. Shown for completeness; nothing here can be granted.","placement":"placed","file":"modules/hall-ui/public/government.html","line":141,"string_id":"084eb64bce72"},
|
|
1517
|
+
{"key":"L0053","section":"Machine principals","text":"holds","placement":"unplaced","occurrences":2},
|
|
1518
|
+
{"key":"L0054","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":516,"string_id":"1883ad7f07fc"},
|
|
1519
|
+
{"key":"L0055","section":"Machine principals","text":"Builders hall · this session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":670,"string_id":"fd1903586e8e"},
|
|
1520
|
+
{"key":"L0056","section":"What are you doing now?","text":"What are you doing now?","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":671,"string_id":"60853319f290"},
|
|
1521
|
+
{"key":"L0057","section":"What are you doing now?","text":"Pick one. You can switch any time from the top bar.","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":672,"string_id":"4f87d5b17b54"},
|
|
1522
|
+
{"key":"L0058","section":"What are you doing now?","text":"Building","placement":"unplaced"},
|
|
1523
|
+
{"key":"L0059","section":"What are you doing now?","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":3},
|
|
1524
|
+
{"key":"L0060","section":"What are you doing now?","text":"Artistry","placement":"unplaced"},
|
|
1525
|
+
{"key":"L0061","section":"What are you doing now?","text":"Ideating","placement":"unplaced"},
|
|
1526
|
+
{"key":"L0062","section":"What are you doing now?","text":"Turn thoughts into ideas.","placement":"unplaced"},
|
|
1527
|
+
{"key":"L0063","section":"What are you doing now?","text":"Ask me again next session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":675,"string_id":"fa86bf403775"}
|
|
1519
1528
|
]
|
|
1520
1529
|
},
|
|
1521
1530
|
{
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/government/access-routes.js — the handlers behind the three ACCESS HOLDERS
|
|
4
|
+
// routes (task 1005000). The routes themselves — path, permission gate and pin — are
|
|
5
|
+
// declared in routes/government.js, where route-rank-check reads them; only the bodies
|
|
6
|
+
// live here, so the router file stays under the size ratchet.
|
|
7
|
+
//
|
|
8
|
+
// GET /government/access (board.vote.cast) — read
|
|
9
|
+
// POST /government/access/:accessKey/holders (government.manage) — record
|
|
10
|
+
// DELETE /government/access/:accessKey/holders/:builderId (government.manage) — remove
|
|
11
|
+
//
|
|
12
|
+
// ⚠ A record of who to ask, never authority (access.js). Not act-alone routes: they
|
|
13
|
+
// record a fact about the world outside the project; they decide nothing a sitting
|
|
14
|
+
// votes on.
|
|
15
|
+
|
|
16
|
+
const access = require('./access');
|
|
17
|
+
|
|
18
|
+
// deps: { db, log, parseId, validateOrRespond } — the router's own, passed in so a
|
|
19
|
+
// test that stubs the db by property reaches these handlers too.
|
|
20
|
+
function accessHandlers({ db, log, parseId, validateOrRespond }) {
|
|
21
|
+
const keyOf = (req, res) => {
|
|
22
|
+
const key = String(req.params.accessKey || '');
|
|
23
|
+
if (access.isAccessKey(key)) return key;
|
|
24
|
+
res.fail('unknown_access', { status: 400, message: `Name one of: ${access.KEYS.join(', ')}.` });
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async function read(req, res) {
|
|
29
|
+
try {
|
|
30
|
+
res.json({ access: access.accessView(await db.listAccessHolders()) });
|
|
31
|
+
} catch (err) {
|
|
32
|
+
log.error('[gds] GET /government/access', err);
|
|
33
|
+
res.fail('access_read_failed', { status: 500, message: 'internal error' });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function grant(req, res) {
|
|
38
|
+
if (validateOrRespond(req, res, { builder_id: { required: true, type: 'integer', min: 1 } })) return;
|
|
39
|
+
const key = keyOf(req, res);
|
|
40
|
+
if (key === null) return;
|
|
41
|
+
const builderId = req.body.builder_id;
|
|
42
|
+
try {
|
|
43
|
+
const who = await db.getActiveBuilder(builderId);
|
|
44
|
+
if (!who) return res.fail('builder_not_found', { status: 404, message: 'No active builder with that id.' });
|
|
45
|
+
const added = await db.grantAccess(key, builderId, req.builder.id);
|
|
46
|
+
res.status(added ? 201 : 200).json({ ok: true, access_key: key, builder_id: String(builderId), added });
|
|
47
|
+
} catch (err) {
|
|
48
|
+
log.error('[gds] POST /government/access/:accessKey/holders', err);
|
|
49
|
+
res.fail('access_grant_failed', { status: 500, message: 'internal error' });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function revoke(req, res) {
|
|
54
|
+
const key = keyOf(req, res);
|
|
55
|
+
if (key === null) return;
|
|
56
|
+
const builderId = parseId(req, res, { param: 'builderId', code: 'bad_builder_id', positiveInt: true });
|
|
57
|
+
if (builderId === null) return;
|
|
58
|
+
try {
|
|
59
|
+
const removed = await db.revokeAccess(key, builderId);
|
|
60
|
+
res.json({ ok: true, access_key: key, builder_id: String(builderId), removed });
|
|
61
|
+
} catch (err) {
|
|
62
|
+
log.error('[gds] DELETE /government/access/:accessKey/holders/:builderId', err);
|
|
63
|
+
res.fail('access_revoke_failed', { status: 500, message: 'internal error' });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { read, grant, revoke };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = { accessHandlers };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/government/access.js — ACCESS HOLDERS: who holds the keys that live OUTSIDE
|
|
4
|
+
// Bongos, so a blocker that needs one reaches the person who has it (task 1005000,
|
|
5
|
+
// goal 1000111).
|
|
6
|
+
//
|
|
7
|
+
// WHY. Some blockers can only be cleared by whoever holds access no rank or Lead
|
|
8
|
+
// models: the GitHub repository's admin (token scopes, branch protection, the App's
|
|
9
|
+
// permissions), the server, the domain's DNS, the hosting account, package publishing,
|
|
10
|
+
// payments. An Archon in Bongos may not hold the GitHub admin, and a Lead never does.
|
|
11
|
+
// It lived only in prose (docs/credentials.md "account logins the owner holds") and in
|
|
12
|
+
// blocker text — blocker 1000199, the publish token missing GitHub's `workflow` scope,
|
|
13
|
+
// could only be filed to "the owner". The builder's words: repo owner is a category of
|
|
14
|
+
// blockers that go beyond normality.
|
|
15
|
+
//
|
|
16
|
+
// WHAT IT IS. A fixed vocabulary of outside-access keys (ACCESS, extendable here and in
|
|
17
|
+
// the migration's CHECK together), a record of which builders hold each
|
|
18
|
+
// (government_access_holders, written by the owner only), and a seat, `access:<key>`,
|
|
19
|
+
// that an ask can wait on. routing.js resolves that seat to the recorded holders, and
|
|
20
|
+
// to the owner when nobody is recorded — the same fall every other seat takes.
|
|
21
|
+
//
|
|
22
|
+
// ⚠ A RECORD OF WHO TO ASK, NEVER AN AUTHORITY GRANT. Holding github-admin here unlocks
|
|
23
|
+
// no Bongos route and no rank: this module is not read by requireRank, the permission
|
|
24
|
+
// resolver, or any gate, and an access key is not a permission atom. No secret is
|
|
25
|
+
// stored either — the row says WHO holds an access, never a credential or a token.
|
|
26
|
+
//
|
|
27
|
+
// PURE: no db.
|
|
28
|
+
|
|
29
|
+
const ACCESS = Object.freeze([
|
|
30
|
+
// name: how the seat reads after "waits on the"; needs: what a blocker on it needs.
|
|
31
|
+
Object.freeze({ key: 'github-admin', name: 'repo owner', needs: 'repo-owner access', what: 'Admin on the GitHub repository: token scopes, branch protection, the GitHub App.' }),
|
|
32
|
+
Object.freeze({ key: 'server-ssh', name: 'server access', needs: 'server access', what: 'A login on the server the project runs on.' }),
|
|
33
|
+
Object.freeze({ key: 'dns', name: 'domain settings', needs: 'access to the domain settings', what: 'The DNS account for the project\'s domain.' }),
|
|
34
|
+
Object.freeze({ key: 'hosting', name: 'hosting account', needs: 'access to the hosting account', what: 'The account that pays for and runs the project\'s servers.' }),
|
|
35
|
+
Object.freeze({ key: 'npm-publish', name: 'package publishing', needs: 'package publishing rights', what: 'Publishing rights for the project\'s packages.' }),
|
|
36
|
+
Object.freeze({ key: 'payments', name: 'payments', needs: 'access to payments', what: 'The account money moves through.' }),
|
|
37
|
+
]);
|
|
38
|
+
const KEYS = Object.freeze(ACCESS.map((a) => a.key));
|
|
39
|
+
const isAccessKey = (k) => KEYS.includes(String(k || ''));
|
|
40
|
+
const byKey = (k) => ACCESS.find((a) => a.key === k) || null;
|
|
41
|
+
|
|
42
|
+
// The seat grammar's access half, for asks.js SEAT and the migration's CHECK.
|
|
43
|
+
const SEAT_PART = `access:(${KEYS.join('|')})`;
|
|
44
|
+
|
|
45
|
+
// "the repo owner" — how the seat reads after "waits on".
|
|
46
|
+
function accessLabel(key) {
|
|
47
|
+
const a = byKey(key);
|
|
48
|
+
return a ? `the ${a.name}` : 'the owner';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// "needs repo-owner access" — how a blocker on this seat reads under its holder.
|
|
52
|
+
function needsWords(key) {
|
|
53
|
+
const a = byKey(key);
|
|
54
|
+
return a ? `needs ${a.needs}` : 'needs outside access';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The holders read as the seat resolver wants them: { key: [{ builder_id, github_login, display_name }] }.
|
|
58
|
+
function holdersByKey(rows) {
|
|
59
|
+
const out = {};
|
|
60
|
+
for (const k of KEYS) out[k] = [];
|
|
61
|
+
for (const r of rows || []) {
|
|
62
|
+
if (r && isAccessKey(r.access_key) && r.builder_id != null) out[r.access_key].push(r);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The read GET /government/access answers: every access, its words, who holds it.
|
|
68
|
+
function accessView(rows) {
|
|
69
|
+
const held = holdersByKey(rows);
|
|
70
|
+
return ACCESS.map((a) => ({
|
|
71
|
+
key: a.key,
|
|
72
|
+
name: a.name,
|
|
73
|
+
// the one display form, for the list and the picker alike
|
|
74
|
+
title: a.name.charAt(0).toUpperCase() + a.name.slice(1),
|
|
75
|
+
what: a.what,
|
|
76
|
+
seat: `access:${a.key}`,
|
|
77
|
+
holders: held[a.key].map((r) => ({
|
|
78
|
+
id: String(r.builder_id),
|
|
79
|
+
login: r.github_login || null,
|
|
80
|
+
name: r.display_name || r.github_login || null,
|
|
81
|
+
since: r.granted_at || null,
|
|
82
|
+
})),
|
|
83
|
+
// nobody recorded: the seat falls to the owner, as every unheld seat does
|
|
84
|
+
falls_to_owner: held[a.key].length === 0,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { ACCESS, KEYS, SEAT_PART, isAccessKey, accessLabel, needsWords, holdersByKey, accessView };
|
|
@@ -44,10 +44,12 @@ const MARKUP = /\*\*|`|^#|\[[^\]]*\]\(|https?:\/\/|<\/?[a-z]/im;
|
|
|
44
44
|
// Control characters other than tab and line breaks (line breaks have their own rule).
|
|
45
45
|
// eslint-disable-next-line no-control-regex
|
|
46
46
|
const CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/;
|
|
47
|
+
const access = require('./access');
|
|
47
48
|
const BUILDINGS = Object.freeze(['charter', 'rules', 'disputes', 'people', 'planning', 'automation', 'review', 'release', 'treasury']);
|
|
48
49
|
// The seats an ask can wait on (gov-living.js seatResolver's grammar). An
|
|
49
50
|
// unseated building falls to the owner when it is routed (owner ruling).
|
|
50
|
-
|
|
51
|
+
// `access:<key>` (task 1005000): whoever holds that outside access (access.js).
|
|
52
|
+
const SEAT = new RegExp(`^(owner|(lead|board):(${BUILDINGS.join('|')})|committee:(${BUILDINGS.join('|')})\\.[a-z][a-z0-9_]{0,31}|${access.SEAT_PART})$`);
|
|
51
53
|
// An answer's key: what a tap sends. Its act, optional: the matter's own act it
|
|
52
54
|
// takes ('go ahead', 'record', 'force:tested_by_hand'); the deck maps it onto
|
|
53
55
|
// the desk row's real route and refuses one the row does not offer.
|
|
@@ -150,7 +152,7 @@ function checkAsk(raw) {
|
|
|
150
152
|
// ---- who it waits on: a seat the direction desk can route ----
|
|
151
153
|
const seat = str(c.waits_on).trim();
|
|
152
154
|
if (!seat) err('waits_on', 'missing', 'Say who this waits on.');
|
|
153
|
-
else if (!SEAT.test(seat)) err('waits_on', 'unknown_seat', `"${seat.slice(0, 40)}" is not a seat: say owner, lead:<building>, committee:<building>.<name> or
|
|
155
|
+
else if (!SEAT.test(seat)) err('waits_on', 'unknown_seat', `"${seat.slice(0, 40)}" is not a seat: say owner, lead:<building>, committee:<building>.<name>, board:<building> or access:<key>.`);
|
|
154
156
|
|
|
155
157
|
// ---- what happens if nobody answers ----
|
|
156
158
|
lineRule('if_nobody_answers', c.if_nobody_answers, 'Say what happens if nobody answers.');
|
|
@@ -206,6 +208,7 @@ const ASK_REQUIRED_MESSAGE = 'An AI session files a blocker with an ask: a quest
|
|
|
206
208
|
function seatWords(seat) {
|
|
207
209
|
const s = str(seat);
|
|
208
210
|
if (s === 'owner') return 'the owner';
|
|
211
|
+
if (/^access:/.test(s)) return access.accessLabel(s.slice(7));
|
|
209
212
|
const m = /^(lead|board|committee):([a-z]+)(?:\.([a-z0-9_]+))?$/.exec(s);
|
|
210
213
|
if (!m) return s;
|
|
211
214
|
const building = m[2].charAt(0).toUpperCase() + m[2].slice(1);
|
package/modules/government/db.js
CHANGED
|
@@ -888,6 +888,41 @@ async function listActiveArchons() {
|
|
|
888
888
|
return rows;
|
|
889
889
|
}
|
|
890
890
|
|
|
891
|
+
// ACCESS HOLDERS (task 1005000, government_036): who holds each outside access.
|
|
892
|
+
// Active builders only — someone who left the project is nobody to ask. A record
|
|
893
|
+
// of who to ask, never authority: no gate reads these rows (access.js).
|
|
894
|
+
async function listAccessHolders() {
|
|
895
|
+
const { rows } = await pool.query(
|
|
896
|
+
`SELECT h.access_key, h.builder_id, h.granted_at, b.github_login, b.display_name
|
|
897
|
+
FROM government_access_holders h
|
|
898
|
+
JOIN builders b ON b.id = h.builder_id
|
|
899
|
+
WHERE b.status = 'active' AND b.system_role IS NULL
|
|
900
|
+
ORDER BY h.access_key, h.granted_at, h.builder_id`,
|
|
901
|
+
);
|
|
902
|
+
return rows;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Record that a builder holds an access. Idempotent: an existing row is kept as it
|
|
906
|
+
// was (its original granted_at/by). → true when a row was added.
|
|
907
|
+
async function grantAccess(accessKey, builderId, grantedBy) {
|
|
908
|
+
const { rowCount } = await pool.query(
|
|
909
|
+
`INSERT INTO government_access_holders (access_key, builder_id, granted_by)
|
|
910
|
+
VALUES ($1, $2, $3)
|
|
911
|
+
ON CONFLICT (access_key, builder_id) DO NOTHING`,
|
|
912
|
+
[accessKey, builderId, grantedBy],
|
|
913
|
+
);
|
|
914
|
+
return rowCount > 0;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// → true when a row was removed.
|
|
918
|
+
async function revokeAccess(accessKey, builderId) {
|
|
919
|
+
const { rowCount } = await pool.query(
|
|
920
|
+
'DELETE FROM government_access_holders WHERE access_key = $1 AND builder_id = $2',
|
|
921
|
+
[accessKey, builderId],
|
|
922
|
+
);
|
|
923
|
+
return rowCount > 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
891
926
|
// One builder of this project, or null: who a Lead appointment names. Only an
|
|
892
927
|
// active person (no machine identity) can be appointed.
|
|
893
928
|
async function getActiveBuilder(builderId) {
|
|
@@ -1306,6 +1341,9 @@ module.exports = {
|
|
|
1306
1341
|
applyLaws,
|
|
1307
1342
|
listLeads,
|
|
1308
1343
|
listActiveArchons,
|
|
1344
|
+
listAccessHolders,
|
|
1345
|
+
grantAccess,
|
|
1346
|
+
revokeAccess,
|
|
1309
1347
|
getActiveBuilder,
|
|
1310
1348
|
applyLeadAct,
|
|
1311
1349
|
listBuildouts,
|
|
@@ -86,6 +86,7 @@ const governmentConfig = require('./config');
|
|
|
86
86
|
const asksDb = require('./asks-db');
|
|
87
87
|
const routing = require('./routing');
|
|
88
88
|
const waiting = require('./waiting');
|
|
89
|
+
const access = require('./access');
|
|
89
90
|
const { ownerOf } = require('./leads');
|
|
90
91
|
|
|
91
92
|
const log = api.logger('government');
|
|
@@ -840,8 +841,17 @@ async function docketFor(builder) {
|
|
|
840
841
|
// nothing — the docket then says `routed: false` rather than guess whose it is.
|
|
841
842
|
async function loadSeats(government) {
|
|
842
843
|
try {
|
|
843
|
-
|
|
844
|
-
|
|
844
|
+
// task 1005000: who holds each outside access. Its own read fails OPEN to nobody
|
|
845
|
+
// recorded (every access seat then falls to the owner), so a missing table on an
|
|
846
|
+
// instance that has not migrated yet never stops the City routing. Started inside
|
|
847
|
+
// a .then so a db layer without the function (an older stub, the walk's store)
|
|
848
|
+
// fails open too rather than throwing before the catch is attached.
|
|
849
|
+
const accessRows = Promise.resolve().then(() => db.listAccessHolders()).catch((err) => {
|
|
850
|
+
log.warn({ err: err && err.message }, 'governor docket: access holders unreadable, access seats fall to the owner');
|
|
851
|
+
return [];
|
|
852
|
+
});
|
|
853
|
+
const [leads, archons, held] = await Promise.all([db.listLeads(), db.listActiveArchons(), accessRows]);
|
|
854
|
+
return { leads, owner: ownerOf(archons), holders: (government && government.holders) || {}, access: access.holdersByKey(held) };
|
|
845
855
|
} catch (err) {
|
|
846
856
|
log.warn({ err: err && err.message }, 'governor docket: seats unreadable, nothing routed');
|
|
847
857
|
return null;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-- government_036_access_holders.sql — who holds the keys OUTSIDE Bongos, and an ask
|
|
2
|
+
-- that waits on them (task 1005000, goal 1000111).
|
|
3
|
+
--
|
|
4
|
+
-- WHAT THIS ADDS
|
|
5
|
+
--
|
|
6
|
+
-- government_access_holders — one row per (outside access, builder): the GitHub
|
|
7
|
+
-- repository's admin, the server, the domain's DNS, the hosting account, package
|
|
8
|
+
-- publishing, payments. The vocabulary is modules/government/access.js `ACCESS`,
|
|
9
|
+
-- held equal to the CHECK below by tests/access_holders.mjs. Written by the owner
|
|
10
|
+
-- only (the routes gate government.manage); who granted it and when ride the row.
|
|
11
|
+
--
|
|
12
|
+
-- It is a RECORD OF WHO TO ASK, never an authority grant: no permission atom, rank
|
|
13
|
+
-- check or gate reads this table, and it stores no credential — only who holds one.
|
|
14
|
+
--
|
|
15
|
+
-- government_asks.waits_on gains the seat `access:<key>`, so an ask can wait on
|
|
16
|
+
-- whoever holds an outside access (routing.js; nobody recorded → the owner).
|
|
17
|
+
|
|
18
|
+
BEGIN;
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS government_access_holders (
|
|
21
|
+
access_key text NOT NULL
|
|
22
|
+
CHECK (access_key IN ('github-admin', 'server-ssh', 'dns', 'hosting', 'npm-publish', 'payments')),
|
|
23
|
+
builder_id bigint NOT NULL REFERENCES builders(id) ON DELETE CASCADE,
|
|
24
|
+
granted_by bigint REFERENCES builders(id) ON DELETE SET NULL,
|
|
25
|
+
granted_at timestamptz NOT NULL DEFAULT now(),
|
|
26
|
+
PRIMARY KEY (access_key, builder_id)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
ALTER TABLE government_asks DROP CONSTRAINT IF EXISTS government_asks_waits_on_check;
|
|
30
|
+
ALTER TABLE government_asks ADD CONSTRAINT government_asks_waits_on_check
|
|
31
|
+
CHECK (waits_on ~ '^(owner|(lead|board):(charter|rules|disputes|people|planning|automation|review|release|treasury)|committee:(charter|rules|disputes|people|planning|automation|review|release|treasury)\.[a-z][a-z0-9_]{0,31}|access:(github-admin|server-ssh|dns|hosting|npm-publish|payments))$');
|
|
32
|
+
|
|
33
|
+
COMMIT;
|