@bongos/core 1.19.514 → 1.19.515
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 +47 -22
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +12 -0
- package/clients/bongos-client/index.cjs +12 -0
- package/clients/bongos-client/index.d.ts +20 -0
- package/clients/bongos-client/index.mjs +12 -0
- package/docs/api/openapi.json +350 -3
- package/docs/api-reference.md +11 -1
- package/docs/module-api-changelog.md +2 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +12 -0
- package/modules/government/catalog.js +6 -0
- package/modules/government/migrations/government_009_mingle_manage.sql +55 -0
- package/modules/lifecycle/migrations/lifecycle_007_mingle.sql +134 -0
- package/modules/lifecycle/mingle.js +337 -0
- package/modules/lifecycle/module.json +1 -0
- package/modules/lifecycle/routes/mingle.js +116 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/government_seed.mjs +35 -0
- package/tests/mingle.mjs +370 -0
package/docs/api/openapi.json
CHANGED
|
@@ -161,6 +161,10 @@
|
|
|
161
161
|
"name": "memory",
|
|
162
162
|
"description": "Endpoints under `/api/bongos/memory`."
|
|
163
163
|
},
|
|
164
|
+
{
|
|
165
|
+
"name": "mingle",
|
|
166
|
+
"description": "Endpoints under `/api/bongos/mingle`."
|
|
167
|
+
},
|
|
164
168
|
{
|
|
165
169
|
"name": "modules",
|
|
166
170
|
"description": "Endpoints under `/api/bongos/modules`."
|
|
@@ -10054,6 +10058,253 @@
|
|
|
10054
10058
|
]
|
|
10055
10059
|
}
|
|
10056
10060
|
},
|
|
10061
|
+
"/mingle": {
|
|
10062
|
+
"get": {
|
|
10063
|
+
"operationId": "get_mingle",
|
|
10064
|
+
"tags": [
|
|
10065
|
+
"mingle"
|
|
10066
|
+
],
|
|
10067
|
+
"summary": "GET /mingle",
|
|
10068
|
+
"description": ", and OWN-SCOPED — your own state, never someone else's. Who else has opted in to being introduced is a fact about them.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
10069
|
+
"x-rank": "any-builder",
|
|
10070
|
+
"x-source": "modules/lifecycle/routes/mingle.js",
|
|
10071
|
+
"responses": {
|
|
10072
|
+
"200": {
|
|
10073
|
+
"description": "Success.",
|
|
10074
|
+
"content": {
|
|
10075
|
+
"application/json": {
|
|
10076
|
+
"schema": {
|
|
10077
|
+
"$ref": "#/components/schemas/GetMingleResponse"
|
|
10078
|
+
}
|
|
10079
|
+
}
|
|
10080
|
+
}
|
|
10081
|
+
},
|
|
10082
|
+
"400": {
|
|
10083
|
+
"$ref": "#/components/responses/BadRequest"
|
|
10084
|
+
},
|
|
10085
|
+
"401": {
|
|
10086
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
10087
|
+
},
|
|
10088
|
+
"403": {
|
|
10089
|
+
"$ref": "#/components/responses/Forbidden"
|
|
10090
|
+
}
|
|
10091
|
+
},
|
|
10092
|
+
"security": [
|
|
10093
|
+
{
|
|
10094
|
+
"builderSession": []
|
|
10095
|
+
}
|
|
10096
|
+
]
|
|
10097
|
+
}
|
|
10098
|
+
},
|
|
10099
|
+
"/mingle/opt-in": {
|
|
10100
|
+
"put": {
|
|
10101
|
+
"operationId": "put_mingle_opt_in",
|
|
10102
|
+
"tags": [
|
|
10103
|
+
"mingle"
|
|
10104
|
+
],
|
|
10105
|
+
"summary": "PUT /mingle/opt-in",
|
|
10106
|
+
"description": ", OWN-SCOPED. Keyed by the SESSION, never a body field — consent you can give on someone else's behalf is not consent.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
10107
|
+
"x-rank": "any-builder",
|
|
10108
|
+
"x-source": "modules/lifecycle/routes/mingle.js",
|
|
10109
|
+
"requestBody": {
|
|
10110
|
+
"required": true,
|
|
10111
|
+
"content": {
|
|
10112
|
+
"application/json": {
|
|
10113
|
+
"schema": {
|
|
10114
|
+
"$ref": "#/components/schemas/PutMingleOptInRequest"
|
|
10115
|
+
}
|
|
10116
|
+
}
|
|
10117
|
+
},
|
|
10118
|
+
"x-validated": true
|
|
10119
|
+
},
|
|
10120
|
+
"responses": {
|
|
10121
|
+
"200": {
|
|
10122
|
+
"description": "Success.",
|
|
10123
|
+
"content": {
|
|
10124
|
+
"application/json": {
|
|
10125
|
+
"schema": {
|
|
10126
|
+
"$ref": "#/components/schemas/PutMingleOptInResponse"
|
|
10127
|
+
}
|
|
10128
|
+
}
|
|
10129
|
+
}
|
|
10130
|
+
},
|
|
10131
|
+
"400": {
|
|
10132
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
10133
|
+
},
|
|
10134
|
+
"401": {
|
|
10135
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
10136
|
+
},
|
|
10137
|
+
"403": {
|
|
10138
|
+
"$ref": "#/components/responses/Forbidden"
|
|
10139
|
+
}
|
|
10140
|
+
},
|
|
10141
|
+
"security": [
|
|
10142
|
+
{
|
|
10143
|
+
"builderSession": []
|
|
10144
|
+
}
|
|
10145
|
+
]
|
|
10146
|
+
}
|
|
10147
|
+
},
|
|
10148
|
+
"/mingle/pairs/{id}/decide": {
|
|
10149
|
+
"post": {
|
|
10150
|
+
"operationId": "post_mingle_pairs_id_decide",
|
|
10151
|
+
"tags": [
|
|
10152
|
+
"mingle"
|
|
10153
|
+
],
|
|
10154
|
+
"summary": "POST /mingle/pairs/:id/decide",
|
|
10155
|
+
"description": ". The UPDATE itself requires the caller to be one of the two people in the pair, so a builder cannot answer an introduction they are not part of — enforced in SQL, not by a check here that a later caller could skip.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
10156
|
+
"x-rank": "any-builder",
|
|
10157
|
+
"x-source": "modules/lifecycle/routes/mingle.js",
|
|
10158
|
+
"parameters": [
|
|
10159
|
+
{
|
|
10160
|
+
"name": "id",
|
|
10161
|
+
"in": "path",
|
|
10162
|
+
"required": true,
|
|
10163
|
+
"schema": {
|
|
10164
|
+
"type": "string"
|
|
10165
|
+
},
|
|
10166
|
+
"description": "Path parameter `id`."
|
|
10167
|
+
}
|
|
10168
|
+
],
|
|
10169
|
+
"requestBody": {
|
|
10170
|
+
"required": true,
|
|
10171
|
+
"content": {
|
|
10172
|
+
"application/json": {
|
|
10173
|
+
"schema": {
|
|
10174
|
+
"$ref": "#/components/schemas/PostMinglePairsIdDecideRequest"
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
},
|
|
10178
|
+
"x-validated": true
|
|
10179
|
+
},
|
|
10180
|
+
"responses": {
|
|
10181
|
+
"200": {
|
|
10182
|
+
"description": "Success.",
|
|
10183
|
+
"content": {
|
|
10184
|
+
"application/json": {
|
|
10185
|
+
"schema": {
|
|
10186
|
+
"$ref": "#/components/schemas/PostMinglePairsIdDecideResponse"
|
|
10187
|
+
}
|
|
10188
|
+
}
|
|
10189
|
+
}
|
|
10190
|
+
},
|
|
10191
|
+
"400": {
|
|
10192
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
10193
|
+
},
|
|
10194
|
+
"401": {
|
|
10195
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
10196
|
+
},
|
|
10197
|
+
"403": {
|
|
10198
|
+
"$ref": "#/components/responses/Forbidden"
|
|
10199
|
+
},
|
|
10200
|
+
"404": {
|
|
10201
|
+
"$ref": "#/components/responses/NotFound"
|
|
10202
|
+
}
|
|
10203
|
+
},
|
|
10204
|
+
"security": [
|
|
10205
|
+
{
|
|
10206
|
+
"builderSession": []
|
|
10207
|
+
}
|
|
10208
|
+
]
|
|
10209
|
+
}
|
|
10210
|
+
},
|
|
10211
|
+
"/mingle/project": {
|
|
10212
|
+
"put": {
|
|
10213
|
+
"operationId": "put_mingle_project",
|
|
10214
|
+
"tags": [
|
|
10215
|
+
"mingle"
|
|
10216
|
+
],
|
|
10217
|
+
"summary": "PUT /mingle/project",
|
|
10218
|
+
"description": "perm: mingle.manage (floor metic) — see the header. Offering the feature to the project is an operator act; it still cannot pair anyone who has not opted themselves in.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `mingle.manage` (all required).",
|
|
10219
|
+
"x-rank": "metic+archon",
|
|
10220
|
+
"x-source": "modules/lifecycle/routes/mingle.js",
|
|
10221
|
+
"x-permissions": [
|
|
10222
|
+
"mingle.manage"
|
|
10223
|
+
],
|
|
10224
|
+
"requestBody": {
|
|
10225
|
+
"required": true,
|
|
10226
|
+
"content": {
|
|
10227
|
+
"application/json": {
|
|
10228
|
+
"schema": {
|
|
10229
|
+
"$ref": "#/components/schemas/PutMingleProjectRequest"
|
|
10230
|
+
}
|
|
10231
|
+
}
|
|
10232
|
+
},
|
|
10233
|
+
"x-validated": true
|
|
10234
|
+
},
|
|
10235
|
+
"responses": {
|
|
10236
|
+
"200": {
|
|
10237
|
+
"description": "Success."
|
|
10238
|
+
},
|
|
10239
|
+
"400": {
|
|
10240
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
10241
|
+
},
|
|
10242
|
+
"401": {
|
|
10243
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
10244
|
+
},
|
|
10245
|
+
"403": {
|
|
10246
|
+
"$ref": "#/components/responses/Forbidden"
|
|
10247
|
+
}
|
|
10248
|
+
},
|
|
10249
|
+
"security": [
|
|
10250
|
+
{
|
|
10251
|
+
"builderSession": []
|
|
10252
|
+
}
|
|
10253
|
+
]
|
|
10254
|
+
}
|
|
10255
|
+
},
|
|
10256
|
+
"/mingle/round": {
|
|
10257
|
+
"post": {
|
|
10258
|
+
"operationId": "post_mingle_round",
|
|
10259
|
+
"tags": [
|
|
10260
|
+
"mingle"
|
|
10261
|
+
],
|
|
10262
|
+
"summary": "POST /mingle/round",
|
|
10263
|
+
"description": "perm: mingle.manage — running a round writes introductions for other people, so it is an operator action rather than something a builder does to themselves. Refuses on its own when the project switch is off (runMingleRound checks).\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `mingle.manage` (all required).",
|
|
10264
|
+
"x-rank": "metic+archon",
|
|
10265
|
+
"x-source": "modules/lifecycle/routes/mingle.js",
|
|
10266
|
+
"x-permissions": [
|
|
10267
|
+
"mingle.manage"
|
|
10268
|
+
],
|
|
10269
|
+
"requestBody": {
|
|
10270
|
+
"required": false,
|
|
10271
|
+
"content": {
|
|
10272
|
+
"application/json": {
|
|
10273
|
+
"schema": {
|
|
10274
|
+
"$ref": "#/components/schemas/PostMingleRoundRequest"
|
|
10275
|
+
}
|
|
10276
|
+
}
|
|
10277
|
+
},
|
|
10278
|
+
"x-validated": true
|
|
10279
|
+
},
|
|
10280
|
+
"responses": {
|
|
10281
|
+
"200": {
|
|
10282
|
+
"description": "Success.",
|
|
10283
|
+
"content": {
|
|
10284
|
+
"application/json": {
|
|
10285
|
+
"schema": {
|
|
10286
|
+
"$ref": "#/components/schemas/PostMingleRoundResponse"
|
|
10287
|
+
}
|
|
10288
|
+
}
|
|
10289
|
+
}
|
|
10290
|
+
},
|
|
10291
|
+
"400": {
|
|
10292
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
10293
|
+
},
|
|
10294
|
+
"401": {
|
|
10295
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
10296
|
+
},
|
|
10297
|
+
"403": {
|
|
10298
|
+
"$ref": "#/components/responses/Forbidden"
|
|
10299
|
+
}
|
|
10300
|
+
},
|
|
10301
|
+
"security": [
|
|
10302
|
+
{
|
|
10303
|
+
"builderSession": []
|
|
10304
|
+
}
|
|
10305
|
+
]
|
|
10306
|
+
}
|
|
10307
|
+
},
|
|
10057
10308
|
"/modules": {
|
|
10058
10309
|
"get": {
|
|
10059
10310
|
"operationId": "get_modules",
|
|
@@ -16840,6 +17091,19 @@
|
|
|
16840
17091
|
"files"
|
|
16841
17092
|
]
|
|
16842
17093
|
},
|
|
17094
|
+
"GetMingleResponse": {
|
|
17095
|
+
"type": "object",
|
|
17096
|
+
"properties": {
|
|
17097
|
+
"mingle": {},
|
|
17098
|
+
"open_pairs": {},
|
|
17099
|
+
"matching": {}
|
|
17100
|
+
},
|
|
17101
|
+
"required": [
|
|
17102
|
+
"mingle",
|
|
17103
|
+
"open_pairs",
|
|
17104
|
+
"matching"
|
|
17105
|
+
]
|
|
17106
|
+
},
|
|
16843
17107
|
"GetModulesResponse": {
|
|
16844
17108
|
"type": "object",
|
|
16845
17109
|
"properties": {
|
|
@@ -20219,6 +20483,54 @@
|
|
|
20219
20483
|
},
|
|
20220
20484
|
"additionalProperties": true
|
|
20221
20485
|
},
|
|
20486
|
+
"PostMinglePairsIdDecideRequest": {
|
|
20487
|
+
"type": "object",
|
|
20488
|
+
"properties": {
|
|
20489
|
+
"decision": {
|
|
20490
|
+
"type": "string",
|
|
20491
|
+
"enum": [
|
|
20492
|
+
"accepted",
|
|
20493
|
+
"declined"
|
|
20494
|
+
]
|
|
20495
|
+
}
|
|
20496
|
+
},
|
|
20497
|
+
"required": [
|
|
20498
|
+
"decision"
|
|
20499
|
+
],
|
|
20500
|
+
"additionalProperties": false
|
|
20501
|
+
},
|
|
20502
|
+
"PostMinglePairsIdDecideResponse": {
|
|
20503
|
+
"type": "object",
|
|
20504
|
+
"properties": {
|
|
20505
|
+
"pair": {}
|
|
20506
|
+
},
|
|
20507
|
+
"required": [
|
|
20508
|
+
"pair"
|
|
20509
|
+
]
|
|
20510
|
+
},
|
|
20511
|
+
"PostMingleRoundRequest": {
|
|
20512
|
+
"type": "object",
|
|
20513
|
+
"properties": {
|
|
20514
|
+
"limit": {
|
|
20515
|
+
"type": "integer",
|
|
20516
|
+
"minimum": 1
|
|
20517
|
+
}
|
|
20518
|
+
},
|
|
20519
|
+
"additionalProperties": false
|
|
20520
|
+
},
|
|
20521
|
+
"PostMingleRoundResponse": {
|
|
20522
|
+
"type": "object",
|
|
20523
|
+
"properties": {
|
|
20524
|
+
"paired": {},
|
|
20525
|
+
"count": {},
|
|
20526
|
+
"skipped": {}
|
|
20527
|
+
},
|
|
20528
|
+
"required": [
|
|
20529
|
+
"paired",
|
|
20530
|
+
"count",
|
|
20531
|
+
"skipped"
|
|
20532
|
+
]
|
|
20533
|
+
},
|
|
20222
20534
|
"PostModulesKeyEnableRequest": {
|
|
20223
20535
|
"type": "object",
|
|
20224
20536
|
"properties": {
|
|
@@ -21683,6 +21995,41 @@
|
|
|
21683
21995
|
"granted"
|
|
21684
21996
|
]
|
|
21685
21997
|
},
|
|
21998
|
+
"PutMingleOptInRequest": {
|
|
21999
|
+
"type": "object",
|
|
22000
|
+
"properties": {
|
|
22001
|
+
"opted_in": {
|
|
22002
|
+
"type": "boolean"
|
|
22003
|
+
}
|
|
22004
|
+
},
|
|
22005
|
+
"required": [
|
|
22006
|
+
"opted_in"
|
|
22007
|
+
],
|
|
22008
|
+
"additionalProperties": false
|
|
22009
|
+
},
|
|
22010
|
+
"PutMingleOptInResponse": {
|
|
22011
|
+
"type": "object",
|
|
22012
|
+
"properties": {
|
|
22013
|
+
"opted_in": {},
|
|
22014
|
+
"updated_at": {}
|
|
22015
|
+
},
|
|
22016
|
+
"required": [
|
|
22017
|
+
"opted_in",
|
|
22018
|
+
"updated_at"
|
|
22019
|
+
]
|
|
22020
|
+
},
|
|
22021
|
+
"PutMingleProjectRequest": {
|
|
22022
|
+
"type": "object",
|
|
22023
|
+
"properties": {
|
|
22024
|
+
"enabled": {
|
|
22025
|
+
"type": "boolean"
|
|
22026
|
+
}
|
|
22027
|
+
},
|
|
22028
|
+
"required": [
|
|
22029
|
+
"enabled"
|
|
22030
|
+
],
|
|
22031
|
+
"additionalProperties": false
|
|
22032
|
+
},
|
|
21686
22033
|
"PutTasksIdDependenciesRequest": {
|
|
21687
22034
|
"type": "object",
|
|
21688
22035
|
"properties": {
|
|
@@ -21834,9 +22181,9 @@
|
|
|
21834
22181
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
21835
22182
|
}
|
|
21836
22183
|
},
|
|
21837
|
-
"x-endpoint-count":
|
|
21838
|
-
"x-schema-count":
|
|
22184
|
+
"x-endpoint-count": 352,
|
|
22185
|
+
"x-schema-count": 387,
|
|
21839
22186
|
"x-undocumented-bodies": 9,
|
|
21840
|
-
"x-response-schemas":
|
|
22187
|
+
"x-response-schemas": 263,
|
|
21841
22188
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
21842
22189
|
}
|
package/docs/api-reference.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Bongos API reference
|
|
4
4
|
|
|
5
|
-
> **Generated from the live route files** — the route file is authoritative.
|
|
5
|
+
> **Generated from the live route files** — the route file is authoritative. 352 endpoints across 57 route files.
|
|
6
6
|
> Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
|
|
7
7
|
|
|
8
8
|
Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -399,6 +399,16 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
399
399
|
| GET | `/api/bongos/memory/files` | `any-builder` | — | GET /memory/files — list the AUTHENTICATED builder's memory files (metadata only, no content). |
|
|
400
400
|
| POST | `/api/bongos/memory/sync` | `any-builder` | `files` | POST /memory/sync — push a builder's local memory files to the server. |
|
|
401
401
|
|
|
402
|
+
## `mingle` (5)
|
|
403
|
+
|
|
404
|
+
| Method | Path | Rank | Body | Description |
|
|
405
|
+
|---|---|---|---|---|
|
|
406
|
+
| GET | `/api/bongos/mingle` | `any-builder` | — | , and OWN-SCOPED — your own state, never someone else's. |
|
|
407
|
+
| PUT | `/api/bongos/mingle/opt-in` | `any-builder` | `opted_in` | , OWN-SCOPED. |
|
|
408
|
+
| POST | `/api/bongos/mingle/pairs/:id/decide` | `any-builder` | `decision` | . |
|
|
409
|
+
| PUT | `/api/bongos/mingle/project` | `metic+archon` | `enabled` | perm: mingle.manage (floor metic) — see the header. |
|
|
410
|
+
| POST | `/api/bongos/mingle/round` | `metic+archon` | `limit` | perm: mingle.manage — running a round writes introductions for other people, so it is an operator action rather than something a builder … |
|
|
411
|
+
|
|
402
412
|
## `modules` (4)
|
|
403
413
|
|
|
404
414
|
| Method | Path | Rank | Body | Description |
|
|
@@ -1465,5 +1465,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1465
1465
|
landed since 1.19.512 with no explicit bump. run 33795977371. (task 1002620)
|
|
1466
1466
|
1.19.514 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1467
1467
|
landed since 1.19.513 with no explicit bump. run 33796959752. (task 1002620)
|
|
1468
|
+
1.19.515 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1469
|
+
landed since 1.19.514 with no explicit bump. run 33797825811. (task 1002620)
|
|
1468
1470
|
---------------------------------------------------------------------------
|
|
1469
1471
|
```
|
|
@@ -587,6 +587,18 @@ function createClient(opts = {}) {
|
|
|
587
587
|
// POST /memory/sync — rank: any-builder — POST /memory/sync
|
|
588
588
|
postMemorySync: (args) => request("POST", "/memory/sync", { hasBody: true }, args),
|
|
589
589
|
},
|
|
590
|
+
"mingle": {
|
|
591
|
+
// GET /mingle — rank: any-builder — GET /mingle
|
|
592
|
+
getMingle: (args) => request("GET", "/mingle", { hasBody: false }, args),
|
|
593
|
+
// PUT /mingle/opt-in — rank: any-builder — PUT /mingle/opt-in
|
|
594
|
+
putMingleOptIn: (args) => request("PUT", "/mingle/opt-in", { hasBody: true }, args),
|
|
595
|
+
// POST /mingle/pairs/{id}/decide — rank: any-builder — POST /mingle/pairs/:id/decide
|
|
596
|
+
postMinglePairsIdDecide: (args) => request("POST", "/mingle/pairs/{id}/decide", { hasBody: true }, args),
|
|
597
|
+
// PUT /mingle/project — rank: metic+archon — PUT /mingle/project
|
|
598
|
+
putMingleProject: (args) => request("PUT", "/mingle/project", { hasBody: true }, args),
|
|
599
|
+
// POST /mingle/round — rank: metic+archon — POST /mingle/round
|
|
600
|
+
postMingleRound: (args) => request("POST", "/mingle/round", { hasBody: true }, args),
|
|
601
|
+
},
|
|
590
602
|
"modules": {
|
|
591
603
|
// GET /modules — rank: any-builder — GET /modules
|
|
592
604
|
getModules: (args) => request("GET", "/modules", { hasBody: false }, args),
|
|
@@ -194,6 +194,12 @@ const PERMISSIONS = [
|
|
|
194
194
|
{ key: 'box.fleet.manage', system: false, floor: 'metic', scope: 'any', resource: 'box', guards: 'GET /boxes[/cost-ledger], POST /boxes/:b/close, PATCH .../block (ADR 0157, was archon)' },
|
|
195
195
|
{ key: 'autonomy.precheck', system: false, floor: 'metic', guards: 'GET /autonomy/precheck — unsupervised-runner gate (ADR 0157, was archon)' },
|
|
196
196
|
{ key: 'discord.channels.manage', system: false, floor: 'metic', guards: 'GET /discord/channels/plan, POST .../reconcile (ADR 0157, was archon)' },
|
|
197
|
+
// Metic by the discord.channels.manage analogue — configuring a project-wide
|
|
198
|
+
// social surface — rather than archon. The blast radius is bounded by the OTHER
|
|
199
|
+
// switch: enabling mingle offers it, and can never pair a builder who has not
|
|
200
|
+
// personally opted in (task 1003177, ADR 0187 §5), so this atom cannot put
|
|
201
|
+
// anyone in a room with a stranger on its own.
|
|
202
|
+
{ key: 'mingle.manage', system: false, floor: 'metic', guards: 'PUT /mingle/project (the project half of mingle consent), POST /mingle/round (run a pairing round) — task 1003177, goal 1000075' },
|
|
197
203
|
{ key: 'project.curate', system: false, floor: 'metic', guards: 'platform-identity: GET/POST /projects, /projects/invite, PATCH /projects/:id/featured, GET /scouting (ADR 0157, was archon)' },
|
|
198
204
|
{ key: 'override_request.decide', system: false, floor: 'metic', guards: 'GET /override-requests, POST .../:id/decide (ADR 0157, was archon)' },
|
|
199
205
|
{ key: 'claim.release.any', system: false, floor: 'metic', scope: 'any', resource: 'claim', guards: 'POST /claims/:id/release-on-behalf (ADR 0157, was archon)' },
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
-- government_009_mingle_manage.sql — grant the new `mingle.manage` atom to the
|
|
2
|
+
-- Metic and Archon rank-roles (task 1003177 / C07, goal 1000075; ADR 0187 §5).
|
|
3
|
+
--
|
|
4
|
+
-- Mingle's two operator routes — `PUT /mingle/project` (the PROJECT half of
|
|
5
|
+
-- mingle consent) and `POST /mingle/round` (run a pairing round) — need a gate,
|
|
6
|
+
-- and BV1.R105 says a modules/*/routes gate is a permission atom and never a
|
|
7
|
+
-- rank: tests/government_require_permission.mjs fails the build on a new
|
|
8
|
+
-- `requireRank`, and the two files still spelling it that way are documented
|
|
9
|
+
-- holdouts rather than a pattern to copy.
|
|
10
|
+
--
|
|
11
|
+
-- FLOOR `metic`, by the `discord.channels.manage` analogue — configuring a
|
|
12
|
+
-- project-wide social surface — rather than archon. What makes that safe is
|
|
13
|
+
-- mingle's OTHER switch: enabling mingle only OFFERS it, and the matcher can never
|
|
14
|
+
-- pair a builder who has not personally opted in (lifecycle_mingle_optin), so this
|
|
15
|
+
-- atom cannot put anyone in a room with a stranger on its own.
|
|
16
|
+
--
|
|
17
|
+
-- A BRAND-NEW key: no rank holds it yet, so BOTH rungs the floor implies are
|
|
18
|
+
-- granted here. RANK_SEED is a cumulative superset — floor `metic` means
|
|
19
|
+
-- {metic, archon} — and the drift guard (tests/government_seed.mjs) compares the
|
|
20
|
+
-- UNION of the run-once seed plus every later grant migration against that
|
|
21
|
+
-- floor-derived seed, per rank. Minting the catalog row WITHOUT this migration is
|
|
22
|
+
-- exactly what that guard caught on this task.
|
|
23
|
+
--
|
|
24
|
+
-- COPY government_004_board_permissions.sql, NOT governance_004_criterion_create.
|
|
25
|
+
-- The `governance_*` grant migrations predate government_001_rename_from_governance,
|
|
26
|
+
-- which renamed `governance_role_permissions` → `government_rank_permissions` and
|
|
27
|
+
-- its `role_key` column → `rank_key`, with NO compatibility shim. Copying one of
|
|
28
|
+
-- those files verbatim — which the first draft of THIS migration did — writes to a
|
|
29
|
+
-- table that no longer exists, so it errors at apply time on any database that
|
|
30
|
+
-- runs migrations in filename order, a fresh install included. The drift guard
|
|
31
|
+
-- above does not catch it: it is a regex over `SELECT '<rank>', unnest(ARRAY[…])`
|
|
32
|
+
-- and never looks at the target table (tests/mingle.mjs now checks that part).
|
|
33
|
+
--
|
|
34
|
+
-- Additive only: one new permission key granted to two ranks; nothing is revoked.
|
|
35
|
+
-- Idempotent (ON CONFLICT DO NOTHING); forward-safe (INSERT only).
|
|
36
|
+
--
|
|
37
|
+
-- One block PER RANK, in the government_004 shape: the drift guard scans for
|
|
38
|
+
-- `SELECT '<rank>', unnest(ARRAY[…])` literally, so a combined
|
|
39
|
+
-- `unnest(ARRAY['metic','archon']), 'key'` form parses as zero grants and reds it.
|
|
40
|
+
|
|
41
|
+
BEGIN;
|
|
42
|
+
|
|
43
|
+
INSERT INTO government_rank_permissions (rank_key, permission_key)
|
|
44
|
+
SELECT 'metic', unnest(ARRAY[
|
|
45
|
+
'mingle.manage'
|
|
46
|
+
])
|
|
47
|
+
ON CONFLICT (rank_key, permission_key) DO NOTHING;
|
|
48
|
+
|
|
49
|
+
INSERT INTO government_rank_permissions (rank_key, permission_key)
|
|
50
|
+
SELECT 'archon', unnest(ARRAY[
|
|
51
|
+
'mingle.manage'
|
|
52
|
+
])
|
|
53
|
+
ON CONFLICT (rank_key, permission_key) DO NOTHING;
|
|
54
|
+
|
|
55
|
+
COMMIT;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
-- lifecycle_007_mingle.sql — mingle: pairing two builders who do NOT share a craft,
|
|
2
|
+
-- and only when both the project and the person said yes (task 1003177 / C07,
|
|
3
|
+
-- goal 1000075; criterion collab-mingle-two-switches, ADR 0187 §5).
|
|
4
|
+
--
|
|
5
|
+
-- TWO SWITCHES, AND THIS TABLE IS ONLY THE SECOND ONE. Consent is the project
|
|
6
|
+
-- enabling mingle AND the individual opting in. The PROJECT switch is not here —
|
|
7
|
+
-- it is `project_settings['mingle.enabled']` (migration core_231), because a
|
|
8
|
+
-- project-wide knob belongs in the project's own store and forking a second one is
|
|
9
|
+
-- what src/bongos/project-settings.js exists to prevent. This table is the
|
|
10
|
+
-- per-builder half. Both are read on every pairing; either one off means no pair.
|
|
11
|
+
--
|
|
12
|
+
-- WHY AN EXPLICIT ROW RATHER THAN A behavior_prefs KEY. Opting in to be
|
|
13
|
+
-- auto-introduced to a stranger a few times a week is a real-world social
|
|
14
|
+
-- commitment, so the record of it should say WHEN it was given and be legible on
|
|
15
|
+
-- its own — not be one key inside a jsonb blob of unrelated UI knobs. It also
|
|
16
|
+
-- keeps the write inside this module: lifecycle may READ core `builders`, but the
|
|
17
|
+
-- behaviour prefs belong to builder-settings and writing them from here would
|
|
18
|
+
-- reach across a module boundary (ADR 0083).
|
|
19
|
+
--
|
|
20
|
+
-- FAIL CLOSED, both halves. No row means NOT participating — never "hasn't said
|
|
21
|
+
-- no". `opted_in = false` is a deliberate withdrawal and is kept rather than
|
|
22
|
+
-- deleted, so the surface can tell "asked and declined the whole feature" from
|
|
23
|
+
-- "never offered it", the same distinction ADR 0171 draws between a hidden account
|
|
24
|
+
-- and one that never federated.
|
|
25
|
+
--
|
|
26
|
+
-- MODULE-OWNED and `lifecycle_`-prefixed, so fitness check 11 permits it (ADR 0083
|
|
27
|
+
-- §Decision #5). Mirrors lifecycle_004_help_requests and lifecycle_005_task_-
|
|
28
|
+
-- recommendations, its siblings under this goal.
|
|
29
|
+
|
|
30
|
+
CREATE TABLE IF NOT EXISTS lifecycle_mingle_optin (
|
|
31
|
+
builder_id bigint PRIMARY KEY REFERENCES builders(id),
|
|
32
|
+
|
|
33
|
+
-- The switch itself. See the header: absent ≠ false ≠ true.
|
|
34
|
+
opted_in boolean NOT NULL,
|
|
35
|
+
|
|
36
|
+
-- When consent was last changed. The criterion calls mingle a real-world social
|
|
37
|
+
-- commitment; a consent record with no timestamp cannot answer "since when".
|
|
38
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
-- The matcher's read: everyone currently participating.
|
|
42
|
+
CREATE INDEX IF NOT EXISTS lifecycle_mingle_optin_in_idx
|
|
43
|
+
ON lifecycle_mingle_optin (builder_id) WHERE opted_in = true;
|
|
44
|
+
|
|
45
|
+
COMMENT ON TABLE lifecycle_mingle_optin IS
|
|
46
|
+
'The per-builder half of mingle consent (task 1003177). The project half is project_settings[''mingle.enabled'']; both must be on. No row means not participating.';
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
-- ---------------------------------------------------------------------------
|
|
50
|
+
-- The pairs
|
|
51
|
+
-- ---------------------------------------------------------------------------
|
|
52
|
+
--
|
|
53
|
+
-- A PROPOSAL, never an obligation. A row here says "you two were introduced"; it
|
|
54
|
+
-- is declinable, and declining is terminal and free.
|
|
55
|
+
--
|
|
56
|
+
-- DECLINING COSTS NOTHING, AND THE SCHEMA IS WHERE THAT IS TRUE. There is no
|
|
57
|
+
-- decline counter, no cooldown column, no cost or streak field on either party,
|
|
58
|
+
-- and no karma or credit reference anywhere in this file — a table that recorded
|
|
59
|
+
-- a decline ANYWHERE BUT HERE would be the penalty the criterion forbids. This is
|
|
60
|
+
-- the same posture lifecycle_005 takes for a declined recommendation, deliberately
|
|
61
|
+
-- restated rather than cross-referenced, because the next person to add a column
|
|
62
|
+
-- reads this file and not that one.
|
|
63
|
+
--
|
|
64
|
+
-- Nothing reads `status` back into matching, either: the matcher's eligibility read
|
|
65
|
+
-- never joins this table, so a decline cannot make the decliner less likely to be
|
|
66
|
+
-- paired again. That is the "no future matching weight" half of the criterion, and
|
|
67
|
+
-- it is a property of the READ, not a promise made here.
|
|
68
|
+
|
|
69
|
+
CREATE TABLE IF NOT EXISTS lifecycle_mingle_pairs (
|
|
70
|
+
id bigserial PRIMARY KEY,
|
|
71
|
+
|
|
72
|
+
-- Ordered low/high at write time so a pair has ONE row whichever way round it
|
|
73
|
+
-- was generated, and the open-pair unique index below actually bites.
|
|
74
|
+
builder_lo bigint NOT NULL REFERENCES builders(id),
|
|
75
|
+
builder_hi bigint NOT NULL REFERENCES builders(id),
|
|
76
|
+
|
|
77
|
+
-- The crafts that made this pair legal, recorded AS MATCHED. The criterion asks
|
|
78
|
+
-- for cross-craft "chosen deliberately and recorded"; storing the two crafts
|
|
79
|
+
-- means a pair can be audited later even if a builder changes their disciplines
|
|
80
|
+
-- afterwards, which a re-derivation from builders.preferred_disciplines could
|
|
81
|
+
-- not do.
|
|
82
|
+
craft_lo text NOT NULL,
|
|
83
|
+
craft_hi text NOT NULL,
|
|
84
|
+
|
|
85
|
+
-- 'proposed' — introduced, nobody has answered
|
|
86
|
+
-- 'accepted' — someone said yes
|
|
87
|
+
-- 'declined' — someone said no. Terminal, and carries NO consequence.
|
|
88
|
+
status text NOT NULL DEFAULT 'proposed',
|
|
89
|
+
|
|
90
|
+
-- Who answered. A record of what happened, never an input to matching.
|
|
91
|
+
decided_by bigint REFERENCES builders(id),
|
|
92
|
+
decided_at timestamptz,
|
|
93
|
+
|
|
94
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
95
|
+
|
|
96
|
+
CONSTRAINT lifecycle_mingle_pairs_status_chk
|
|
97
|
+
CHECK (status IN ('proposed', 'accepted', 'declined')),
|
|
98
|
+
|
|
99
|
+
-- Pairing someone with themselves is not a pairing.
|
|
100
|
+
CONSTRAINT lifecycle_mingle_pairs_not_self_chk
|
|
101
|
+
CHECK (builder_lo <> builder_hi),
|
|
102
|
+
|
|
103
|
+
-- The ordering invariant the open-pair index depends on.
|
|
104
|
+
CONSTRAINT lifecycle_mingle_pairs_ordered_chk
|
|
105
|
+
CHECK (builder_lo < builder_hi),
|
|
106
|
+
|
|
107
|
+
-- CROSS-CRAFT IS A DATABASE CONSTRAINT, not only a matcher rule. The criterion's
|
|
108
|
+
-- first testable claim is "a pair is only ever cross-craft"; enforcing it solely
|
|
109
|
+
-- in the matcher would leave any future writer — a backfill, a second caller, a
|
|
110
|
+
-- fixture — free to violate the one property the feature is named for.
|
|
111
|
+
CONSTRAINT lifecycle_mingle_pairs_cross_craft_chk
|
|
112
|
+
CHECK (craft_lo <> craft_hi),
|
|
113
|
+
|
|
114
|
+
CONSTRAINT lifecycle_mingle_pairs_decided_chk
|
|
115
|
+
CHECK ((status = 'proposed') = (decided_at IS NULL))
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
-- One OPEN proposal per pair. Partial, so a pair CAN be introduced again in a later
|
|
119
|
+
-- round after it was settled — a permanent unique key would make a single decline
|
|
120
|
+
-- final between two people forever, which is a consequence, and declining is free.
|
|
121
|
+
CREATE UNIQUE INDEX IF NOT EXISTS lifecycle_mingle_pairs_open_uniq
|
|
122
|
+
ON lifecycle_mingle_pairs (builder_lo, builder_hi) WHERE status = 'proposed';
|
|
123
|
+
|
|
124
|
+
-- "What am I being introduced to right now" — both sides of an open pair.
|
|
125
|
+
CREATE INDEX IF NOT EXISTS lifecycle_mingle_pairs_open_lo_idx
|
|
126
|
+
ON lifecycle_mingle_pairs (builder_lo) WHERE status = 'proposed';
|
|
127
|
+
CREATE INDEX IF NOT EXISTS lifecycle_mingle_pairs_open_hi_idx
|
|
128
|
+
ON lifecycle_mingle_pairs (builder_hi) WHERE status = 'proposed';
|
|
129
|
+
|
|
130
|
+
COMMENT ON TABLE lifecycle_mingle_pairs IS
|
|
131
|
+
'A cross-craft mingle introduction (task 1003177). Declinable, and a decline is terminal, free, and never read back into matching.';
|
|
132
|
+
|
|
133
|
+
INSERT INTO schema_migrations (version) VALUES ('lifecycle_007_mingle')
|
|
134
|
+
ON CONFLICT (version) DO NOTHING;
|