@bongos/core 1.19.1025 → 1.19.1027

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.bongos-core.json +88 -33
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +40 -0
  4. package/clients/bongos-client/index.cjs +40 -0
  5. package/clients/bongos-client/index.d.ts +63 -0
  6. package/clients/bongos-client/index.mjs +40 -0
  7. package/docs/adr/0213-platform-terms-acceptance-is-the-hubs-own-witness.md +4 -2
  8. package/docs/api/openapi.json +1361 -151
  9. package/docs/api-reference.md +32 -4
  10. package/docs/copy-inventory.md +6 -6
  11. package/docs/copy-registry.json +10 -20
  12. package/docs/module-api-changelog.md +4 -0
  13. package/modules/hall-ui/public/goal-map.html +1 -1
  14. package/modules/platform-identity/community-leaderboard.js +235 -0
  15. package/modules/platform-identity/connections.js +1 -0
  16. package/modules/platform-identity/guilds.js +705 -0
  17. package/modules/platform-identity/migrations/platform_identity_025_guilds.sql +100 -0
  18. package/modules/platform-identity/module.json +1 -1
  19. package/modules/platform-identity/read-rate-limit.js +62 -0
  20. package/modules/platform-identity/routes/community-leaderboard.js +69 -0
  21. package/modules/platform-identity/routes/community-search.js +4 -31
  22. package/modules/platform-identity/routes/guilds-public.js +57 -0
  23. package/modules/platform-identity/routes/guilds.js +393 -0
  24. package/modules/platform-identity/routes/projects.js +1 -1
  25. package/modules/platform-identity/terms-acceptance.js +14 -5
  26. package/modules/public-landing/public/terms.html +3 -3
  27. package/package-lock.json +2 -2
  28. package/package.json +1 -1
  29. package/scripts/gds/run-unit-tests.js +10 -0
  30. package/src/module-api.js +1 -1
  31. package/tests/community_leaderboard_db.mjs +202 -0
  32. package/tests/community_leaderboard_route.mjs +414 -0
  33. package/tests/goal_map_page.mjs +8 -0
  34. package/tests/guilds_api.mjs +521 -0
  35. package/tests/guilds_db.mjs +343 -0
  36. package/tests/own_scope_guard.mjs +5 -0
  37. package/tests/terms_acceptance.mjs +74 -28
  38. package/tests/terms_grandfather_db.mjs +2 -2
@@ -141,6 +141,14 @@
141
141
  "name": "grades",
142
142
  "description": "Endpoints under `/api/bongos/grades`."
143
143
  },
144
+ {
145
+ "name": "guild-requests",
146
+ "description": "Endpoints under `/api/bongos/guild-requests`."
147
+ },
148
+ {
149
+ "name": "guilds",
150
+ "description": "Endpoints under `/api/bongos/guilds`."
151
+ },
144
152
  {
145
153
  "name": "healthz",
146
154
  "description": "Endpoints under `/api/bongos/healthz`."
@@ -4436,6 +4444,34 @@
4436
4444
  ]
4437
4445
  }
4438
4446
  },
4447
+ "/community/leaderboard": {
4448
+ "get": {
4449
+ "operationId": "get_community_leaderboard",
4450
+ "tags": [
4451
+ "community"
4452
+ ],
4453
+ "summary": "GET /community/leaderboard",
4454
+ "description": "the cross-project leaderboard (?window=7d|30d|365d|all, default all; ?discipline=; ?limit=&offset=). Eligibility is the privacy matrix's numbers predicate (active + public + stats shown); the recruiting opt-out does not apply. no-store: hide must beat the cache (ADR 0171 D5). Rollout note: until a builder's first changed report after snapshot history began, their whole lifetime total counts inside every window (spec D2's missing-baseline-is-zero rule) — expected on day one, and self-correcting.\n\n**Rank:** `public` — No authentication — any caller.",
4455
+ "x-rank": "public",
4456
+ "x-source": "modules/platform-identity/routes/community-leaderboard.js",
4457
+ "responses": {
4458
+ "200": {
4459
+ "description": "Success.",
4460
+ "content": {
4461
+ "application/json": {
4462
+ "schema": {
4463
+ "$ref": "#/components/schemas/GetCommunityLeaderboardResponse"
4464
+ }
4465
+ }
4466
+ }
4467
+ },
4468
+ "400": {
4469
+ "$ref": "#/components/responses/BadRequest"
4470
+ }
4471
+ },
4472
+ "security": []
4473
+ }
4474
+ },
4439
4475
  "/community/search": {
4440
4476
  "get": {
4441
4477
  "operationId": "get_community_search",
@@ -8248,51 +8284,34 @@
8248
8284
  ]
8249
8285
  }
8250
8286
  },
8251
- "/healthz": {
8252
- "get": {
8253
- "operationId": "get_healthz",
8287
+ "/guild-requests/{id}": {
8288
+ "delete": {
8289
+ "operationId": "delete_guild_requests_id",
8254
8290
  "tags": [
8255
- "healthz"
8291
+ "guild-requests"
8256
8292
  ],
8257
- "summary": "GET /healthz",
8258
- "description": "liveness probe; the status dashboard (status.<apex>) hits this anonymously.\n\n**Rank:** `public` — No authentication — any caller.",
8259
- "x-rank": "public",
8260
- "x-source": "src/bongos/routes/healthz.js",
8261
- "responses": {
8262
- "200": {
8263
- "description": "Success.",
8264
- "content": {
8265
- "application/json": {
8266
- "schema": {
8267
- "$ref": "#/components/schemas/GetHealthzResponse"
8268
- }
8269
- }
8270
- }
8271
- },
8272
- "400": {
8273
- "$ref": "#/components/responses/BadRequest"
8293
+ "summary": "DELETE /guild-requests/:id",
8294
+ "description": "cancel a row I opened: my request, or (as owner) an invite my guild sent.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8295
+ "x-rank": "any-builder",
8296
+ "x-source": "modules/platform-identity/routes/guilds.js",
8297
+ "parameters": [
8298
+ {
8299
+ "name": "id",
8300
+ "in": "path",
8301
+ "required": true,
8302
+ "schema": {
8303
+ "type": "string"
8304
+ },
8305
+ "description": "Path parameter `id`."
8274
8306
  }
8275
- },
8276
- "security": []
8277
- }
8278
- },
8279
- "/help-requests": {
8280
- "get": {
8281
- "operationId": "get_help_requests",
8282
- "tags": [
8283
- "help-requests"
8284
8307
  ],
8285
- "summary": "GET /help-requests",
8286
- "description": "The BOARD read (task 1003942): every open ask in the project, newest first, for the Collab page's \"all open asks\" block. rank: any-builder — an ask is addressed inside this project and every reader here is a member; the route takes no craft and no builder from the caller, so there is nothing to widen.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8287
- "x-rank": "any-builder",
8288
- "x-source": "modules/lifecycle/routes/help-requests.js",
8289
8308
  "responses": {
8290
8309
  "200": {
8291
8310
  "description": "Success.",
8292
8311
  "content": {
8293
8312
  "application/json": {
8294
8313
  "schema": {
8295
- "$ref": "#/components/schemas/GetHelpRequestsResponse"
8314
+ "$ref": "#/components/schemas/DeleteGuildRequestsIdResponse"
8296
8315
  }
8297
8316
  }
8298
8317
  }
@@ -8305,6 +8324,9 @@
8305
8324
  },
8306
8325
  "403": {
8307
8326
  "$ref": "#/components/responses/Forbidden"
8327
+ },
8328
+ "404": {
8329
+ "$ref": "#/components/responses/NotFound"
8308
8330
  }
8309
8331
  },
8310
8332
  "security": [
@@ -8312,22 +8334,35 @@
8312
8334
  "builderSession": []
8313
8335
  }
8314
8336
  ]
8315
- },
8337
+ }
8338
+ },
8339
+ "/guild-requests/{id}/accept": {
8316
8340
  "post": {
8317
- "operationId": "post_help_requests",
8341
+ "operationId": "post_guild_requests_id_accept",
8318
8342
  "tags": [
8319
- "help-requests"
8343
+ "guild-requests"
8320
8344
  ],
8321
- "summary": "POST /help-requests",
8322
- "description": "asking for help is not a privileged act, and gating it would exclude exactly the newcomers most likely to be stuck.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8345
+ "summary": "POST /guild-requests/:id/accept",
8346
+ "description": "accept an invite addressed to ME, or (as the guild's owner) a request to join it. An invite accept must echo the visibility its card showed; if the guild changed since, 409 and the card re-renders (ADR 0336 D4 §3). Any row the caller may not accept is 404.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8323
8347
  "x-rank": "any-builder",
8324
- "x-source": "modules/lifecycle/routes/help-requests.js",
8348
+ "x-source": "modules/platform-identity/routes/guilds.js",
8349
+ "parameters": [
8350
+ {
8351
+ "name": "id",
8352
+ "in": "path",
8353
+ "required": true,
8354
+ "schema": {
8355
+ "type": "string"
8356
+ },
8357
+ "description": "Path parameter `id`."
8358
+ }
8359
+ ],
8325
8360
  "requestBody": {
8326
- "required": true,
8361
+ "required": false,
8327
8362
  "content": {
8328
8363
  "application/json": {
8329
8364
  "schema": {
8330
- "$ref": "#/components/schemas/PostHelpRequestsRequest"
8365
+ "$ref": "#/components/schemas/PostGuildRequestsIdAcceptRequest"
8331
8366
  }
8332
8367
  }
8333
8368
  },
@@ -8339,7 +8374,7 @@
8339
8374
  "content": {
8340
8375
  "application/json": {
8341
8376
  "schema": {
8342
- "$ref": "#/components/schemas/PostHelpRequestsResponse"
8377
+ "$ref": "#/components/schemas/PostGuildRequestsIdAcceptResponse"
8343
8378
  }
8344
8379
  }
8345
8380
  }
@@ -8352,6 +8387,9 @@
8352
8387
  },
8353
8388
  "403": {
8354
8389
  "$ref": "#/components/responses/Forbidden"
8390
+ },
8391
+ "404": {
8392
+ "$ref": "#/components/responses/NotFound"
8355
8393
  }
8356
8394
  },
8357
8395
  "security": [
@@ -8361,26 +8399,30 @@
8361
8399
  ]
8362
8400
  }
8363
8401
  },
8364
- "/help-requests/archive": {
8365
- "get": {
8366
- "operationId": "get_help_requests_archive",
8402
+ "/guild-requests/{id}/decline": {
8403
+ "post": {
8404
+ "operationId": "post_guild_requests_id_decline",
8367
8405
  "tags": [
8368
- "help-requests"
8406
+ "guild-requests"
8369
8407
  ],
8370
- "summary": "GET /help-requests/archive",
8371
- "description": "THE ARCHIVE (task 1004224, owner direction): what a settled ask becomes. Until now a settled ask simply vanished from Collab. That was tolerable while settling meant a person had decided the ask was done — they knew. It stops being tolerable the moment the LEDGER settles asks on its own: a row can now leave the page without anybody having watched it go, and a surface that silently disappears its own history is asking to be distrusted. TWO LISTS, NOT ONE, and the split is the owner's: asks made OF YOU and asks you made OF OTHERS are different questions with different follow-ups, and a merged list would need a per-row label to say which it was — which is the same information, said worse. ONE ROUTE FOR BOTH. They share a settle, a bound and a shape, and the page draws them side by side in one section; two routes would be two round trips for one collapsed block and two places for the projection to drift. rank: any-builder, and the scoping matches the open reads exactly — you see what was addressed to you and what you asked, and nothing else. `crafts` comes from `builders.preferred_disciplines` through craftsForBuilder, NEVER from the query string. That is not caution: a ?craft= param validated only against the enum is precisely the hole C01's grader found on the open read, and an archive is the larger prize because it is the whole history rather than today's board. This route accepts no craft, no builder and no status from the caller, so there is nothing here to narrow or widen.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8408
+ "summary": "POST /guild-requests/:id/decline",
8409
+ "description": "decline an invite to ME, or (as owner) a request to my guild. SILENT: 204, and the other side is never told.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8372
8410
  "x-rank": "any-builder",
8373
- "x-source": "modules/lifecycle/routes/help-requests.js",
8411
+ "x-source": "modules/platform-identity/routes/guilds.js",
8412
+ "parameters": [
8413
+ {
8414
+ "name": "id",
8415
+ "in": "path",
8416
+ "required": true,
8417
+ "schema": {
8418
+ "type": "string"
8419
+ },
8420
+ "description": "Path parameter `id`."
8421
+ }
8422
+ ],
8374
8423
  "responses": {
8375
8424
  "200": {
8376
- "description": "Success.",
8377
- "content": {
8378
- "application/json": {
8379
- "schema": {
8380
- "$ref": "#/components/schemas/GetHelpRequestsArchiveResponse"
8381
- }
8382
- }
8383
- }
8425
+ "description": "Success."
8384
8426
  },
8385
8427
  "400": {
8386
8428
  "$ref": "#/components/responses/BadRequest"
@@ -8390,6 +8432,9 @@
8390
8432
  },
8391
8433
  "403": {
8392
8434
  "$ref": "#/components/responses/Forbidden"
8435
+ },
8436
+ "404": {
8437
+ "$ref": "#/components/responses/NotFound"
8393
8438
  }
8394
8439
  },
8395
8440
  "security": [
@@ -8399,29 +8444,40 @@
8399
8444
  ]
8400
8445
  }
8401
8446
  },
8402
- "/help-requests/for-me": {
8403
- "get": {
8404
- "operationId": "get_help_requests_for_me",
8447
+ "/guilds": {
8448
+ "post": {
8449
+ "operationId": "post_guilds",
8405
8450
  "tags": [
8406
- "help-requests"
8451
+ "guilds"
8407
8452
  ],
8408
- "summary": "GET /help-requests/for-me",
8409
- "description": "The addressed read — C01's answer to \"and fires a notification\". A request is immediately visible to the person or craft it names. A true PUSH lives in the builder-needs feed (`builder-settings`), which is outside this goal's scope wall, so it is recorded as a gap rather than reached for. rank: any-builder — you only ever see what is addressed to you.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8453
+ "summary": "POST /guilds",
8454
+ "description": "create a guild. The creator becomes its one owner, and chooses its visibility: there is no default (ADR 0336 D1).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8410
8455
  "x-rank": "any-builder",
8411
- "x-source": "modules/lifecycle/routes/help-requests.js",
8456
+ "x-source": "modules/platform-identity/routes/guilds.js",
8457
+ "requestBody": {
8458
+ "required": true,
8459
+ "content": {
8460
+ "application/json": {
8461
+ "schema": {
8462
+ "$ref": "#/components/schemas/PostGuildsRequest"
8463
+ }
8464
+ }
8465
+ },
8466
+ "x-validated": true
8467
+ },
8412
8468
  "responses": {
8413
8469
  "200": {
8414
8470
  "description": "Success.",
8415
8471
  "content": {
8416
8472
  "application/json": {
8417
8473
  "schema": {
8418
- "$ref": "#/components/schemas/GetHelpRequestsForMeResponse"
8474
+ "$ref": "#/components/schemas/PostGuildsResponse"
8419
8475
  }
8420
8476
  }
8421
8477
  }
8422
8478
  },
8423
8479
  "400": {
8424
- "$ref": "#/components/responses/BadRequest"
8480
+ "$ref": "#/components/responses/ValidationFailed"
8425
8481
  },
8426
8482
  "401": {
8427
8483
  "$ref": "#/components/responses/Unauthorized"
@@ -8437,23 +8493,34 @@
8437
8493
  ]
8438
8494
  }
8439
8495
  },
8440
- "/help-requests/mine": {
8496
+ "/guilds/{slug}": {
8441
8497
  "get": {
8442
- "operationId": "get_help_requests_mine",
8498
+ "operationId": "get_guilds_slug",
8443
8499
  "tags": [
8444
- "help-requests"
8500
+ "guilds"
8501
+ ],
8502
+ "summary": "GET /guilds/:slug",
8503
+ "description": "a public guild's page data. The roster is the members who agreed to be shown AND whose own accounts are publicly visible (ADR 0336 D4); size is that roster's length. no-store: a hide or a visibility flip must beat every cache (ADR 0171 D5).\n\n**Rank:** `public` — No authentication — any caller.",
8504
+ "x-rank": "public",
8505
+ "x-source": "modules/platform-identity/routes/guilds-public.js",
8506
+ "parameters": [
8507
+ {
8508
+ "name": "slug",
8509
+ "in": "path",
8510
+ "required": true,
8511
+ "schema": {
8512
+ "type": "string"
8513
+ },
8514
+ "description": "Path parameter `slug`."
8515
+ }
8445
8516
  ],
8446
- "summary": "GET /help-requests/mine",
8447
- "description": "your own asks, so you can settle them.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8448
- "x-rank": "any-builder",
8449
- "x-source": "modules/lifecycle/routes/help-requests.js",
8450
8517
  "responses": {
8451
8518
  "200": {
8452
8519
  "description": "Success.",
8453
8520
  "content": {
8454
8521
  "application/json": {
8455
8522
  "schema": {
8456
- "$ref": "#/components/schemas/GetHelpRequestsMineResponse"
8523
+ "$ref": "#/components/schemas/GetGuildsSlugResponse"
8457
8524
  }
8458
8525
  }
8459
8526
  }
@@ -8461,47 +8528,38 @@
8461
8528
  "400": {
8462
8529
  "$ref": "#/components/responses/BadRequest"
8463
8530
  },
8464
- "401": {
8465
- "$ref": "#/components/responses/Unauthorized"
8466
- },
8467
- "403": {
8468
- "$ref": "#/components/responses/Forbidden"
8531
+ "404": {
8532
+ "$ref": "#/components/responses/NotFound"
8469
8533
  }
8470
8534
  },
8471
- "security": [
8472
- {
8473
- "builderSession": []
8474
- }
8475
- ]
8476
- }
8477
- },
8478
- "/help-requests/{id}": {
8535
+ "security": []
8536
+ },
8479
8537
  "patch": {
8480
- "operationId": "patch_help_requests_id",
8538
+ "operationId": "patch_guilds_slug",
8481
8539
  "tags": [
8482
- "help-requests"
8540
+ "guilds"
8483
8541
  ],
8484
- "summary": "PATCH /help-requests/:id",
8485
- "description": "THE ASKER OR THE ADDRESSEE settles the request: 'answered' when it has been dealt with, 'withdrawn' when the asker no longer needs it. ADR 0330 widened 'answered' to the person the request NAMES — once a reply puts the answer on the row itself, an addressee who has answered in the open and still cannot close it is a surface arguing with itself. 'withdrawn' stays the asker's alone: only the person who needed something can say they no longer need it. Nobody is assigned by being asked — closing an ask ends a question, it does not accept work (ADR 0187 §3). rank: any-builder — who may settle is enforced in the UPDATE's WHERE, not by rank. The caller's crafts come from THEIR OWN profile, never from the body, for the same reason the addressed read refuses a ?craft= it was handed.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8542
+ "summary": "PATCH /guilds/:slug",
8543
+ "description": "owner: edit name, description or visibility. The slug is fixed at creation (ADR 0336 D6), so it is not a field here.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8486
8544
  "x-rank": "any-builder",
8487
- "x-source": "modules/lifecycle/routes/help-requests.js",
8545
+ "x-source": "modules/platform-identity/routes/guilds.js",
8488
8546
  "parameters": [
8489
8547
  {
8490
- "name": "id",
8548
+ "name": "slug",
8491
8549
  "in": "path",
8492
8550
  "required": true,
8493
8551
  "schema": {
8494
8552
  "type": "string"
8495
8553
  },
8496
- "description": "Path parameter `id`."
8554
+ "description": "Path parameter `slug`."
8497
8555
  }
8498
8556
  ],
8499
8557
  "requestBody": {
8500
- "required": true,
8558
+ "required": false,
8501
8559
  "content": {
8502
8560
  "application/json": {
8503
8561
  "schema": {
8504
- "$ref": "#/components/schemas/PatchHelpRequestsIdRequest"
8562
+ "$ref": "#/components/schemas/PatchGuildsSlugRequest"
8505
8563
  }
8506
8564
  }
8507
8565
  },
@@ -8513,7 +8571,7 @@
8513
8571
  "content": {
8514
8572
  "application/json": {
8515
8573
  "schema": {
8516
- "$ref": "#/components/schemas/PatchHelpRequestsIdResponse"
8574
+ "$ref": "#/components/schemas/PatchGuildsSlugResponse"
8517
8575
  }
8518
8576
  }
8519
8577
  }
@@ -8536,27 +8594,25 @@
8536
8594
  "builderSession": []
8537
8595
  }
8538
8596
  ]
8539
- }
8540
- },
8541
- "/help-requests/{id}/replies": {
8542
- "get": {
8543
- "operationId": "get_help_requests_id_replies",
8597
+ },
8598
+ "delete": {
8599
+ "operationId": "delete_guilds_slug",
8544
8600
  "tags": [
8545
- "help-requests"
8601
+ "guilds"
8546
8602
  ],
8547
- "summary": "GET /help-requests/:id/replies",
8548
- "description": ", matching the board read this hangs under. AN UNKNOWN ID ANSWERS `[]`, NOT 404, and that asymmetry with the POST above is deliberate rather than an oversight (raised by the grader). This surface has already had one lesson about existence lookups: PATCH used to look a row up to say WHICH of three things was wrong, which let a caller enumerate ids by watching the refusal change. A 404 here would rebuild exactly that oracle on the cheapest possible request — a GET, repeatable, with nothing written. The POST can afford the 404 because it is a WRITE: it costs an attempt, it is rate-limited by being a write, and a reply to a phantom ask has to fail with a reason or fail as an FK 500 that tells the caller nothing useful. A read has no such excuse, and \"no replies\" is a true answer about an ask that does not exist.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8603
+ "summary": "DELETE /guilds/:slug",
8604
+ "description": "owner: dissolve. Hard delete, cascading to every membership and open request (ADR 0336 D2).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8549
8605
  "x-rank": "any-builder",
8550
- "x-source": "modules/lifecycle/routes/help-requests.js",
8606
+ "x-source": "modules/platform-identity/routes/guilds.js",
8551
8607
  "parameters": [
8552
8608
  {
8553
- "name": "id",
8609
+ "name": "slug",
8554
8610
  "in": "path",
8555
8611
  "required": true,
8556
8612
  "schema": {
8557
8613
  "type": "string"
8558
8614
  },
8559
- "description": "Path parameter `id`."
8615
+ "description": "Path parameter `slug`."
8560
8616
  }
8561
8617
  ],
8562
8618
  "responses": {
@@ -8565,7 +8621,7 @@
8565
8621
  "content": {
8566
8622
  "application/json": {
8567
8623
  "schema": {
8568
- "$ref": "#/components/schemas/GetHelpRequestsIdRepliesResponse"
8624
+ "$ref": "#/components/schemas/DeleteGuildsSlugResponse"
8569
8625
  }
8570
8626
  }
8571
8627
  }
@@ -8588,25 +8644,27 @@
8588
8644
  "builderSession": []
8589
8645
  }
8590
8646
  ]
8591
- },
8647
+ }
8648
+ },
8649
+ "/guilds/{slug}/invites": {
8592
8650
  "post": {
8593
- "operationId": "post_help_requests_id_replies",
8651
+ "operationId": "post_guilds_slug_invites",
8594
8652
  "tags": [
8595
- "help-requests"
8653
+ "guilds"
8596
8654
  ],
8597
- "summary": "POST /help-requests/:id/replies",
8598
- "description": "---- replies (task 1004216, ADR 0330) ------------------------------------- THE HALF THIS SURFACE SHIPPED WITHOUT. A builder could ask, and the asker could mark their own row answered; the person being ASKED had no write at all, so the answer happened somewhere Bongos could not see it. ANY BUILDER MAY REPLY, deliberately. The board read (GET /help-requests) is already project-wide, so every member can see every open ask — and a reply says nothing more private than the ask it hangs under. Restricting replies to the addressee would also break the common good case: somebody who is not who you asked turns out to know the answer. APPEND-ONLY, with no edit and no delete route. That is not an omission to fill in later: the table's own trigger refuses an UPDATE or a DELETE, and the restraint is what keeps this an answer surface rather than the chat surface ADR 0187 §4a declined to build here.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8655
+ "summary": "POST /guilds/:slug/invites",
8656
+ "description": "owner: invite up to INVITE_BATCH_MAX builders by handle, each an ordinary individual invite (ADR 0336 D3). Unlisted guilds take invites. count is the array's own length.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8599
8657
  "x-rank": "any-builder",
8600
- "x-source": "modules/lifecycle/routes/help-requests.js",
8658
+ "x-source": "modules/platform-identity/routes/guilds.js",
8601
8659
  "parameters": [
8602
8660
  {
8603
- "name": "id",
8661
+ "name": "slug",
8604
8662
  "in": "path",
8605
8663
  "required": true,
8606
8664
  "schema": {
8607
8665
  "type": "string"
8608
8666
  },
8609
- "description": "Path parameter `id`."
8667
+ "description": "Path parameter `slug`."
8610
8668
  }
8611
8669
  ],
8612
8670
  "requestBody": {
@@ -8614,7 +8672,7 @@
8614
8672
  "content": {
8615
8673
  "application/json": {
8616
8674
  "schema": {
8617
- "$ref": "#/components/schemas/PostHelpRequestsIdRepliesRequest"
8675
+ "$ref": "#/components/schemas/PostGuildsSlugInvitesRequest"
8618
8676
  }
8619
8677
  }
8620
8678
  },
@@ -8626,7 +8684,7 @@
8626
8684
  "content": {
8627
8685
  "application/json": {
8628
8686
  "schema": {
8629
- "$ref": "#/components/schemas/PostHelpRequestsIdRepliesResponse"
8687
+ "$ref": "#/components/schemas/PostGuildsSlugInvitesResponse"
8630
8688
  }
8631
8689
  }
8632
8690
  }
@@ -8651,23 +8709,34 @@
8651
8709
  ]
8652
8710
  }
8653
8711
  },
8654
- "/inbox": {
8712
+ "/guilds/{slug}/members": {
8655
8713
  "get": {
8656
- "operationId": "get_inbox",
8714
+ "operationId": "get_guilds_slug_members",
8657
8715
  "tags": [
8658
- "inbox"
8716
+ "guilds"
8659
8717
  ],
8660
- "summary": "GET /inbox",
8661
- "description": "read open ideas; skill-friction rows filtered by viewer rank.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8718
+ "summary": "GET /guilds/:slug/members",
8719
+ "description": "the members' roster, for members only (the own-scope door to an unlisted guild, ADR 0336 D4). A private member is listed here, to fellow members, and on no public page.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8662
8720
  "x-rank": "any-builder",
8663
- "x-source": "modules/ideas/routes/inbox.js",
8721
+ "x-source": "modules/platform-identity/routes/guilds.js",
8722
+ "parameters": [
8723
+ {
8724
+ "name": "slug",
8725
+ "in": "path",
8726
+ "required": true,
8727
+ "schema": {
8728
+ "type": "string"
8729
+ },
8730
+ "description": "Path parameter `slug`."
8731
+ }
8732
+ ],
8664
8733
  "responses": {
8665
8734
  "200": {
8666
8735
  "description": "Success.",
8667
8736
  "content": {
8668
8737
  "application/json": {
8669
8738
  "schema": {
8670
- "$ref": "#/components/schemas/GetInboxResponse"
8739
+ "$ref": "#/components/schemas/GetGuildsSlugMembersResponse"
8671
8740
  }
8672
8741
  }
8673
8742
  }
@@ -8680,6 +8749,9 @@
8680
8749
  },
8681
8750
  "403": {
8682
8751
  "$ref": "#/components/responses/Forbidden"
8752
+ },
8753
+ "404": {
8754
+ "$ref": "#/components/responses/NotFound"
8683
8755
  }
8684
8756
  },
8685
8757
  "security": [
@@ -8687,8 +8759,792 @@
8687
8759
  "builderSession": []
8688
8760
  }
8689
8761
  ]
8690
- },
8691
- "post": {
8762
+ }
8763
+ },
8764
+ "/guilds/{slug}/members/{handle}": {
8765
+ "delete": {
8766
+ "operationId": "delete_guilds_slug_members_handle",
8767
+ "tags": [
8768
+ "guilds"
8769
+ ],
8770
+ "summary": "DELETE /guilds/:slug/members/:handle",
8771
+ "description": "owner: remove a member (never the owner). Hard delete.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8772
+ "x-rank": "any-builder",
8773
+ "x-source": "modules/platform-identity/routes/guilds.js",
8774
+ "parameters": [
8775
+ {
8776
+ "name": "slug",
8777
+ "in": "path",
8778
+ "required": true,
8779
+ "schema": {
8780
+ "type": "string"
8781
+ },
8782
+ "description": "Path parameter `slug`."
8783
+ },
8784
+ {
8785
+ "name": "handle",
8786
+ "in": "path",
8787
+ "required": true,
8788
+ "schema": {
8789
+ "type": "string"
8790
+ },
8791
+ "description": "Path parameter `handle`."
8792
+ }
8793
+ ],
8794
+ "responses": {
8795
+ "200": {
8796
+ "description": "Success.",
8797
+ "content": {
8798
+ "application/json": {
8799
+ "schema": {
8800
+ "$ref": "#/components/schemas/DeleteGuildsSlugMembersHandleResponse"
8801
+ }
8802
+ }
8803
+ }
8804
+ },
8805
+ "400": {
8806
+ "$ref": "#/components/responses/BadRequest"
8807
+ },
8808
+ "401": {
8809
+ "$ref": "#/components/responses/Unauthorized"
8810
+ },
8811
+ "403": {
8812
+ "$ref": "#/components/responses/Forbidden"
8813
+ },
8814
+ "404": {
8815
+ "$ref": "#/components/responses/NotFound"
8816
+ }
8817
+ },
8818
+ "security": [
8819
+ {
8820
+ "builderSession": []
8821
+ }
8822
+ ]
8823
+ }
8824
+ },
8825
+ "/guilds/{slug}/membership": {
8826
+ "patch": {
8827
+ "operationId": "patch_guilds_slug_membership",
8828
+ "tags": [
8829
+ "guilds"
8830
+ ],
8831
+ "summary": "PATCH /guilds/:slug/membership",
8832
+ "description": "MY switches in one guild: shown on its public page (ADR 0336 D4) and counted in its totals (ADR 0337 D4.4). Each optional, at least one required, both keyed on the session.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8833
+ "x-rank": "any-builder",
8834
+ "x-source": "modules/platform-identity/routes/guilds.js",
8835
+ "parameters": [
8836
+ {
8837
+ "name": "slug",
8838
+ "in": "path",
8839
+ "required": true,
8840
+ "schema": {
8841
+ "type": "string"
8842
+ },
8843
+ "description": "Path parameter `slug`."
8844
+ }
8845
+ ],
8846
+ "requestBody": {
8847
+ "required": false,
8848
+ "content": {
8849
+ "application/json": {
8850
+ "schema": {
8851
+ "$ref": "#/components/schemas/PatchGuildsSlugMembershipRequest"
8852
+ }
8853
+ }
8854
+ },
8855
+ "x-validated": true
8856
+ },
8857
+ "responses": {
8858
+ "200": {
8859
+ "description": "Success."
8860
+ },
8861
+ "400": {
8862
+ "$ref": "#/components/responses/ValidationFailed"
8863
+ },
8864
+ "401": {
8865
+ "$ref": "#/components/responses/Unauthorized"
8866
+ },
8867
+ "403": {
8868
+ "$ref": "#/components/responses/Forbidden"
8869
+ },
8870
+ "404": {
8871
+ "$ref": "#/components/responses/NotFound"
8872
+ }
8873
+ },
8874
+ "security": [
8875
+ {
8876
+ "builderSession": []
8877
+ }
8878
+ ]
8879
+ },
8880
+ "delete": {
8881
+ "operationId": "delete_guilds_slug_membership",
8882
+ "tags": [
8883
+ "guilds"
8884
+ ],
8885
+ "summary": "DELETE /guilds/:slug/membership",
8886
+ "description": "leave. The owner must transfer first unless they are the last member, when leaving dissolves the guild (ADR 0336 D2).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8887
+ "x-rank": "any-builder",
8888
+ "x-source": "modules/platform-identity/routes/guilds.js",
8889
+ "parameters": [
8890
+ {
8891
+ "name": "slug",
8892
+ "in": "path",
8893
+ "required": true,
8894
+ "schema": {
8895
+ "type": "string"
8896
+ },
8897
+ "description": "Path parameter `slug`."
8898
+ }
8899
+ ],
8900
+ "responses": {
8901
+ "200": {
8902
+ "description": "Success.",
8903
+ "content": {
8904
+ "application/json": {
8905
+ "schema": {
8906
+ "$ref": "#/components/schemas/DeleteGuildsSlugMembershipResponse"
8907
+ }
8908
+ }
8909
+ }
8910
+ },
8911
+ "400": {
8912
+ "$ref": "#/components/responses/BadRequest"
8913
+ },
8914
+ "401": {
8915
+ "$ref": "#/components/responses/Unauthorized"
8916
+ },
8917
+ "403": {
8918
+ "$ref": "#/components/responses/Forbidden"
8919
+ },
8920
+ "404": {
8921
+ "$ref": "#/components/responses/NotFound"
8922
+ }
8923
+ },
8924
+ "security": [
8925
+ {
8926
+ "builderSession": []
8927
+ }
8928
+ ]
8929
+ }
8930
+ },
8931
+ "/guilds/{slug}/requests": {
8932
+ "get": {
8933
+ "operationId": "get_guilds_slug_requests",
8934
+ "tags": [
8935
+ "guilds"
8936
+ ],
8937
+ "summary": "GET /guilds/:slug/requests",
8938
+ "description": "owner: the join requests to decide and the invites still out. Stamps seen_at on the requests only.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8939
+ "x-rank": "any-builder",
8940
+ "x-source": "modules/platform-identity/routes/guilds.js",
8941
+ "parameters": [
8942
+ {
8943
+ "name": "slug",
8944
+ "in": "path",
8945
+ "required": true,
8946
+ "schema": {
8947
+ "type": "string"
8948
+ },
8949
+ "description": "Path parameter `slug`."
8950
+ }
8951
+ ],
8952
+ "responses": {
8953
+ "200": {
8954
+ "description": "Success.",
8955
+ "content": {
8956
+ "application/json": {
8957
+ "schema": {
8958
+ "$ref": "#/components/schemas/GetGuildsSlugRequestsResponse"
8959
+ }
8960
+ }
8961
+ }
8962
+ },
8963
+ "400": {
8964
+ "$ref": "#/components/responses/BadRequest"
8965
+ },
8966
+ "401": {
8967
+ "$ref": "#/components/responses/Unauthorized"
8968
+ },
8969
+ "403": {
8970
+ "$ref": "#/components/responses/Forbidden"
8971
+ },
8972
+ "404": {
8973
+ "$ref": "#/components/responses/NotFound"
8974
+ }
8975
+ },
8976
+ "security": [
8977
+ {
8978
+ "builderSession": []
8979
+ }
8980
+ ]
8981
+ },
8982
+ "post": {
8983
+ "operationId": "post_guilds_slug_requests",
8984
+ "tags": [
8985
+ "guilds"
8986
+ ],
8987
+ "summary": "POST /guilds/:slug/requests",
8988
+ "description": "ask to join. Only a PUBLIC guild takes requests, so an unlisted guild and an unknown slug are one 404 (ADR 0336 D3/D4). A request written and one swallowed by a decline cooldown answer identically.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
8989
+ "x-rank": "any-builder",
8990
+ "x-source": "modules/platform-identity/routes/guilds.js",
8991
+ "parameters": [
8992
+ {
8993
+ "name": "slug",
8994
+ "in": "path",
8995
+ "required": true,
8996
+ "schema": {
8997
+ "type": "string"
8998
+ },
8999
+ "description": "Path parameter `slug`."
9000
+ }
9001
+ ],
9002
+ "requestBody": {
9003
+ "required": false,
9004
+ "content": {
9005
+ "application/json": {
9006
+ "schema": {
9007
+ "$ref": "#/components/schemas/PostGuildsSlugRequestsRequest"
9008
+ }
9009
+ }
9010
+ },
9011
+ "x-validated": true
9012
+ },
9013
+ "responses": {
9014
+ "200": {
9015
+ "description": "Success.",
9016
+ "content": {
9017
+ "application/json": {
9018
+ "schema": {
9019
+ "$ref": "#/components/schemas/PostGuildsSlugRequestsResponse"
9020
+ }
9021
+ }
9022
+ }
9023
+ },
9024
+ "400": {
9025
+ "$ref": "#/components/responses/ValidationFailed"
9026
+ },
9027
+ "401": {
9028
+ "$ref": "#/components/responses/Unauthorized"
9029
+ },
9030
+ "403": {
9031
+ "$ref": "#/components/responses/Forbidden"
9032
+ },
9033
+ "404": {
9034
+ "$ref": "#/components/responses/NotFound"
9035
+ }
9036
+ },
9037
+ "security": [
9038
+ {
9039
+ "builderSession": []
9040
+ }
9041
+ ]
9042
+ }
9043
+ },
9044
+ "/guilds/{slug}/transfer": {
9045
+ "post": {
9046
+ "operationId": "post_guilds_slug_transfer",
9047
+ "tags": [
9048
+ "guilds"
9049
+ ],
9050
+ "summary": "POST /guilds/:slug/transfer",
9051
+ "description": "owner: hand ownership to an existing member. Two statements in one transaction (ADR 0336 D2).\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9052
+ "x-rank": "any-builder",
9053
+ "x-source": "modules/platform-identity/routes/guilds.js",
9054
+ "parameters": [
9055
+ {
9056
+ "name": "slug",
9057
+ "in": "path",
9058
+ "required": true,
9059
+ "schema": {
9060
+ "type": "string"
9061
+ },
9062
+ "description": "Path parameter `slug`."
9063
+ }
9064
+ ],
9065
+ "requestBody": {
9066
+ "required": true,
9067
+ "content": {
9068
+ "application/json": {
9069
+ "schema": {
9070
+ "$ref": "#/components/schemas/PostGuildsSlugTransferRequest"
9071
+ }
9072
+ }
9073
+ },
9074
+ "x-validated": true
9075
+ },
9076
+ "responses": {
9077
+ "200": {
9078
+ "description": "Success.",
9079
+ "content": {
9080
+ "application/json": {
9081
+ "schema": {
9082
+ "$ref": "#/components/schemas/PostGuildsSlugTransferResponse"
9083
+ }
9084
+ }
9085
+ }
9086
+ },
9087
+ "400": {
9088
+ "$ref": "#/components/responses/ValidationFailed"
9089
+ },
9090
+ "401": {
9091
+ "$ref": "#/components/responses/Unauthorized"
9092
+ },
9093
+ "403": {
9094
+ "$ref": "#/components/responses/Forbidden"
9095
+ },
9096
+ "404": {
9097
+ "$ref": "#/components/responses/NotFound"
9098
+ }
9099
+ },
9100
+ "security": [
9101
+ {
9102
+ "builderSession": []
9103
+ }
9104
+ ]
9105
+ }
9106
+ },
9107
+ "/healthz": {
9108
+ "get": {
9109
+ "operationId": "get_healthz",
9110
+ "tags": [
9111
+ "healthz"
9112
+ ],
9113
+ "summary": "GET /healthz",
9114
+ "description": "liveness probe; the status dashboard (status.<apex>) hits this anonymously.\n\n**Rank:** `public` — No authentication — any caller.",
9115
+ "x-rank": "public",
9116
+ "x-source": "src/bongos/routes/healthz.js",
9117
+ "responses": {
9118
+ "200": {
9119
+ "description": "Success.",
9120
+ "content": {
9121
+ "application/json": {
9122
+ "schema": {
9123
+ "$ref": "#/components/schemas/GetHealthzResponse"
9124
+ }
9125
+ }
9126
+ }
9127
+ },
9128
+ "400": {
9129
+ "$ref": "#/components/responses/BadRequest"
9130
+ }
9131
+ },
9132
+ "security": []
9133
+ }
9134
+ },
9135
+ "/help-requests": {
9136
+ "get": {
9137
+ "operationId": "get_help_requests",
9138
+ "tags": [
9139
+ "help-requests"
9140
+ ],
9141
+ "summary": "GET /help-requests",
9142
+ "description": "The BOARD read (task 1003942): every open ask in the project, newest first, for the Collab page's \"all open asks\" block. rank: any-builder — an ask is addressed inside this project and every reader here is a member; the route takes no craft and no builder from the caller, so there is nothing to widen.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9143
+ "x-rank": "any-builder",
9144
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9145
+ "responses": {
9146
+ "200": {
9147
+ "description": "Success.",
9148
+ "content": {
9149
+ "application/json": {
9150
+ "schema": {
9151
+ "$ref": "#/components/schemas/GetHelpRequestsResponse"
9152
+ }
9153
+ }
9154
+ }
9155
+ },
9156
+ "400": {
9157
+ "$ref": "#/components/responses/BadRequest"
9158
+ },
9159
+ "401": {
9160
+ "$ref": "#/components/responses/Unauthorized"
9161
+ },
9162
+ "403": {
9163
+ "$ref": "#/components/responses/Forbidden"
9164
+ }
9165
+ },
9166
+ "security": [
9167
+ {
9168
+ "builderSession": []
9169
+ }
9170
+ ]
9171
+ },
9172
+ "post": {
9173
+ "operationId": "post_help_requests",
9174
+ "tags": [
9175
+ "help-requests"
9176
+ ],
9177
+ "summary": "POST /help-requests",
9178
+ "description": "asking for help is not a privileged act, and gating it would exclude exactly the newcomers most likely to be stuck.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9179
+ "x-rank": "any-builder",
9180
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9181
+ "requestBody": {
9182
+ "required": true,
9183
+ "content": {
9184
+ "application/json": {
9185
+ "schema": {
9186
+ "$ref": "#/components/schemas/PostHelpRequestsRequest"
9187
+ }
9188
+ }
9189
+ },
9190
+ "x-validated": true
9191
+ },
9192
+ "responses": {
9193
+ "200": {
9194
+ "description": "Success.",
9195
+ "content": {
9196
+ "application/json": {
9197
+ "schema": {
9198
+ "$ref": "#/components/schemas/PostHelpRequestsResponse"
9199
+ }
9200
+ }
9201
+ }
9202
+ },
9203
+ "400": {
9204
+ "$ref": "#/components/responses/ValidationFailed"
9205
+ },
9206
+ "401": {
9207
+ "$ref": "#/components/responses/Unauthorized"
9208
+ },
9209
+ "403": {
9210
+ "$ref": "#/components/responses/Forbidden"
9211
+ }
9212
+ },
9213
+ "security": [
9214
+ {
9215
+ "builderSession": []
9216
+ }
9217
+ ]
9218
+ }
9219
+ },
9220
+ "/help-requests/archive": {
9221
+ "get": {
9222
+ "operationId": "get_help_requests_archive",
9223
+ "tags": [
9224
+ "help-requests"
9225
+ ],
9226
+ "summary": "GET /help-requests/archive",
9227
+ "description": "THE ARCHIVE (task 1004224, owner direction): what a settled ask becomes. Until now a settled ask simply vanished from Collab. That was tolerable while settling meant a person had decided the ask was done — they knew. It stops being tolerable the moment the LEDGER settles asks on its own: a row can now leave the page without anybody having watched it go, and a surface that silently disappears its own history is asking to be distrusted. TWO LISTS, NOT ONE, and the split is the owner's: asks made OF YOU and asks you made OF OTHERS are different questions with different follow-ups, and a merged list would need a per-row label to say which it was — which is the same information, said worse. ONE ROUTE FOR BOTH. They share a settle, a bound and a shape, and the page draws them side by side in one section; two routes would be two round trips for one collapsed block and two places for the projection to drift. rank: any-builder, and the scoping matches the open reads exactly — you see what was addressed to you and what you asked, and nothing else. `crafts` comes from `builders.preferred_disciplines` through craftsForBuilder, NEVER from the query string. That is not caution: a ?craft= param validated only against the enum is precisely the hole C01's grader found on the open read, and an archive is the larger prize because it is the whole history rather than today's board. This route accepts no craft, no builder and no status from the caller, so there is nothing here to narrow or widen.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9228
+ "x-rank": "any-builder",
9229
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9230
+ "responses": {
9231
+ "200": {
9232
+ "description": "Success.",
9233
+ "content": {
9234
+ "application/json": {
9235
+ "schema": {
9236
+ "$ref": "#/components/schemas/GetHelpRequestsArchiveResponse"
9237
+ }
9238
+ }
9239
+ }
9240
+ },
9241
+ "400": {
9242
+ "$ref": "#/components/responses/BadRequest"
9243
+ },
9244
+ "401": {
9245
+ "$ref": "#/components/responses/Unauthorized"
9246
+ },
9247
+ "403": {
9248
+ "$ref": "#/components/responses/Forbidden"
9249
+ }
9250
+ },
9251
+ "security": [
9252
+ {
9253
+ "builderSession": []
9254
+ }
9255
+ ]
9256
+ }
9257
+ },
9258
+ "/help-requests/for-me": {
9259
+ "get": {
9260
+ "operationId": "get_help_requests_for_me",
9261
+ "tags": [
9262
+ "help-requests"
9263
+ ],
9264
+ "summary": "GET /help-requests/for-me",
9265
+ "description": "The addressed read — C01's answer to \"and fires a notification\". A request is immediately visible to the person or craft it names. A true PUSH lives in the builder-needs feed (`builder-settings`), which is outside this goal's scope wall, so it is recorded as a gap rather than reached for. rank: any-builder — you only ever see what is addressed to you.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9266
+ "x-rank": "any-builder",
9267
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9268
+ "responses": {
9269
+ "200": {
9270
+ "description": "Success.",
9271
+ "content": {
9272
+ "application/json": {
9273
+ "schema": {
9274
+ "$ref": "#/components/schemas/GetHelpRequestsForMeResponse"
9275
+ }
9276
+ }
9277
+ }
9278
+ },
9279
+ "400": {
9280
+ "$ref": "#/components/responses/BadRequest"
9281
+ },
9282
+ "401": {
9283
+ "$ref": "#/components/responses/Unauthorized"
9284
+ },
9285
+ "403": {
9286
+ "$ref": "#/components/responses/Forbidden"
9287
+ }
9288
+ },
9289
+ "security": [
9290
+ {
9291
+ "builderSession": []
9292
+ }
9293
+ ]
9294
+ }
9295
+ },
9296
+ "/help-requests/mine": {
9297
+ "get": {
9298
+ "operationId": "get_help_requests_mine",
9299
+ "tags": [
9300
+ "help-requests"
9301
+ ],
9302
+ "summary": "GET /help-requests/mine",
9303
+ "description": "your own asks, so you can settle them.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9304
+ "x-rank": "any-builder",
9305
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9306
+ "responses": {
9307
+ "200": {
9308
+ "description": "Success.",
9309
+ "content": {
9310
+ "application/json": {
9311
+ "schema": {
9312
+ "$ref": "#/components/schemas/GetHelpRequestsMineResponse"
9313
+ }
9314
+ }
9315
+ }
9316
+ },
9317
+ "400": {
9318
+ "$ref": "#/components/responses/BadRequest"
9319
+ },
9320
+ "401": {
9321
+ "$ref": "#/components/responses/Unauthorized"
9322
+ },
9323
+ "403": {
9324
+ "$ref": "#/components/responses/Forbidden"
9325
+ }
9326
+ },
9327
+ "security": [
9328
+ {
9329
+ "builderSession": []
9330
+ }
9331
+ ]
9332
+ }
9333
+ },
9334
+ "/help-requests/{id}": {
9335
+ "patch": {
9336
+ "operationId": "patch_help_requests_id",
9337
+ "tags": [
9338
+ "help-requests"
9339
+ ],
9340
+ "summary": "PATCH /help-requests/:id",
9341
+ "description": "THE ASKER OR THE ADDRESSEE settles the request: 'answered' when it has been dealt with, 'withdrawn' when the asker no longer needs it. ADR 0330 widened 'answered' to the person the request NAMES — once a reply puts the answer on the row itself, an addressee who has answered in the open and still cannot close it is a surface arguing with itself. 'withdrawn' stays the asker's alone: only the person who needed something can say they no longer need it. Nobody is assigned by being asked — closing an ask ends a question, it does not accept work (ADR 0187 §3). rank: any-builder — who may settle is enforced in the UPDATE's WHERE, not by rank. The caller's crafts come from THEIR OWN profile, never from the body, for the same reason the addressed read refuses a ?craft= it was handed.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9342
+ "x-rank": "any-builder",
9343
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9344
+ "parameters": [
9345
+ {
9346
+ "name": "id",
9347
+ "in": "path",
9348
+ "required": true,
9349
+ "schema": {
9350
+ "type": "string"
9351
+ },
9352
+ "description": "Path parameter `id`."
9353
+ }
9354
+ ],
9355
+ "requestBody": {
9356
+ "required": true,
9357
+ "content": {
9358
+ "application/json": {
9359
+ "schema": {
9360
+ "$ref": "#/components/schemas/PatchHelpRequestsIdRequest"
9361
+ }
9362
+ }
9363
+ },
9364
+ "x-validated": true
9365
+ },
9366
+ "responses": {
9367
+ "200": {
9368
+ "description": "Success.",
9369
+ "content": {
9370
+ "application/json": {
9371
+ "schema": {
9372
+ "$ref": "#/components/schemas/PatchHelpRequestsIdResponse"
9373
+ }
9374
+ }
9375
+ }
9376
+ },
9377
+ "400": {
9378
+ "$ref": "#/components/responses/ValidationFailed"
9379
+ },
9380
+ "401": {
9381
+ "$ref": "#/components/responses/Unauthorized"
9382
+ },
9383
+ "403": {
9384
+ "$ref": "#/components/responses/Forbidden"
9385
+ },
9386
+ "404": {
9387
+ "$ref": "#/components/responses/NotFound"
9388
+ }
9389
+ },
9390
+ "security": [
9391
+ {
9392
+ "builderSession": []
9393
+ }
9394
+ ]
9395
+ }
9396
+ },
9397
+ "/help-requests/{id}/replies": {
9398
+ "get": {
9399
+ "operationId": "get_help_requests_id_replies",
9400
+ "tags": [
9401
+ "help-requests"
9402
+ ],
9403
+ "summary": "GET /help-requests/:id/replies",
9404
+ "description": ", matching the board read this hangs under. AN UNKNOWN ID ANSWERS `[]`, NOT 404, and that asymmetry with the POST above is deliberate rather than an oversight (raised by the grader). This surface has already had one lesson about existence lookups: PATCH used to look a row up to say WHICH of three things was wrong, which let a caller enumerate ids by watching the refusal change. A 404 here would rebuild exactly that oracle on the cheapest possible request — a GET, repeatable, with nothing written. The POST can afford the 404 because it is a WRITE: it costs an attempt, it is rate-limited by being a write, and a reply to a phantom ask has to fail with a reason or fail as an FK 500 that tells the caller nothing useful. A read has no such excuse, and \"no replies\" is a true answer about an ask that does not exist.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9405
+ "x-rank": "any-builder",
9406
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9407
+ "parameters": [
9408
+ {
9409
+ "name": "id",
9410
+ "in": "path",
9411
+ "required": true,
9412
+ "schema": {
9413
+ "type": "string"
9414
+ },
9415
+ "description": "Path parameter `id`."
9416
+ }
9417
+ ],
9418
+ "responses": {
9419
+ "200": {
9420
+ "description": "Success.",
9421
+ "content": {
9422
+ "application/json": {
9423
+ "schema": {
9424
+ "$ref": "#/components/schemas/GetHelpRequestsIdRepliesResponse"
9425
+ }
9426
+ }
9427
+ }
9428
+ },
9429
+ "400": {
9430
+ "$ref": "#/components/responses/BadRequest"
9431
+ },
9432
+ "401": {
9433
+ "$ref": "#/components/responses/Unauthorized"
9434
+ },
9435
+ "403": {
9436
+ "$ref": "#/components/responses/Forbidden"
9437
+ },
9438
+ "404": {
9439
+ "$ref": "#/components/responses/NotFound"
9440
+ }
9441
+ },
9442
+ "security": [
9443
+ {
9444
+ "builderSession": []
9445
+ }
9446
+ ]
9447
+ },
9448
+ "post": {
9449
+ "operationId": "post_help_requests_id_replies",
9450
+ "tags": [
9451
+ "help-requests"
9452
+ ],
9453
+ "summary": "POST /help-requests/:id/replies",
9454
+ "description": "---- replies (task 1004216, ADR 0330) ------------------------------------- THE HALF THIS SURFACE SHIPPED WITHOUT. A builder could ask, and the asker could mark their own row answered; the person being ASKED had no write at all, so the answer happened somewhere Bongos could not see it. ANY BUILDER MAY REPLY, deliberately. The board read (GET /help-requests) is already project-wide, so every member can see every open ask — and a reply says nothing more private than the ask it hangs under. Restricting replies to the addressee would also break the common good case: somebody who is not who you asked turns out to know the answer. APPEND-ONLY, with no edit and no delete route. That is not an omission to fill in later: the table's own trigger refuses an UPDATE or a DELETE, and the restraint is what keeps this an answer surface rather than the chat surface ADR 0187 §4a declined to build here.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9455
+ "x-rank": "any-builder",
9456
+ "x-source": "modules/lifecycle/routes/help-requests.js",
9457
+ "parameters": [
9458
+ {
9459
+ "name": "id",
9460
+ "in": "path",
9461
+ "required": true,
9462
+ "schema": {
9463
+ "type": "string"
9464
+ },
9465
+ "description": "Path parameter `id`."
9466
+ }
9467
+ ],
9468
+ "requestBody": {
9469
+ "required": true,
9470
+ "content": {
9471
+ "application/json": {
9472
+ "schema": {
9473
+ "$ref": "#/components/schemas/PostHelpRequestsIdRepliesRequest"
9474
+ }
9475
+ }
9476
+ },
9477
+ "x-validated": true
9478
+ },
9479
+ "responses": {
9480
+ "200": {
9481
+ "description": "Success.",
9482
+ "content": {
9483
+ "application/json": {
9484
+ "schema": {
9485
+ "$ref": "#/components/schemas/PostHelpRequestsIdRepliesResponse"
9486
+ }
9487
+ }
9488
+ }
9489
+ },
9490
+ "400": {
9491
+ "$ref": "#/components/responses/ValidationFailed"
9492
+ },
9493
+ "401": {
9494
+ "$ref": "#/components/responses/Unauthorized"
9495
+ },
9496
+ "403": {
9497
+ "$ref": "#/components/responses/Forbidden"
9498
+ },
9499
+ "404": {
9500
+ "$ref": "#/components/responses/NotFound"
9501
+ }
9502
+ },
9503
+ "security": [
9504
+ {
9505
+ "builderSession": []
9506
+ }
9507
+ ]
9508
+ }
9509
+ },
9510
+ "/inbox": {
9511
+ "get": {
9512
+ "operationId": "get_inbox",
9513
+ "tags": [
9514
+ "inbox"
9515
+ ],
9516
+ "summary": "GET /inbox",
9517
+ "description": "read open ideas; skill-friction rows filtered by viewer rank.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
9518
+ "x-rank": "any-builder",
9519
+ "x-source": "modules/ideas/routes/inbox.js",
9520
+ "responses": {
9521
+ "200": {
9522
+ "description": "Success.",
9523
+ "content": {
9524
+ "application/json": {
9525
+ "schema": {
9526
+ "$ref": "#/components/schemas/GetInboxResponse"
9527
+ }
9528
+ }
9529
+ }
9530
+ },
9531
+ "400": {
9532
+ "$ref": "#/components/responses/BadRequest"
9533
+ },
9534
+ "401": {
9535
+ "$ref": "#/components/responses/Unauthorized"
9536
+ },
9537
+ "403": {
9538
+ "$ref": "#/components/responses/Forbidden"
9539
+ }
9540
+ },
9541
+ "security": [
9542
+ {
9543
+ "builderSession": []
9544
+ }
9545
+ ]
9546
+ },
9547
+ "post": {
8692
9548
  "operationId": "post_inbox",
8693
9549
  "tags": [
8694
9550
  "inbox"
@@ -10219,30 +11075,106 @@
10219
11075
  "tags": [
10220
11076
  "me"
10221
11077
  ],
10222
- "summary": "PATCH /me/event-sound-prefs",
10223
- "description": "PATCH a partial set of event sound overrides. Body shape: { \"<event-id>\": \"<variant-id>\" | null, ... } null clears the override (falls back to the event's default). Unknown event IDs, non-string variants, or variants that don't belong to the event → 422. rank: any-builder — self-service profile edit; mirrors the sound-prefs route.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
11078
+ "summary": "PATCH /me/event-sound-prefs",
11079
+ "description": "PATCH a partial set of event sound overrides. Body shape: { \"<event-id>\": \"<variant-id>\" | null, ... } null clears the override (falls back to the event's default). Unknown event IDs, non-string variants, or variants that don't belong to the event → 422. rank: any-builder — self-service profile edit; mirrors the sound-prefs route.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
11080
+ "x-rank": "any-builder",
11081
+ "x-source": "modules/builder-settings/routes/settings.js",
11082
+ "requestBody": {
11083
+ "required": false,
11084
+ "content": {
11085
+ "application/json": {
11086
+ "schema": {
11087
+ "type": "object",
11088
+ "additionalProperties": true
11089
+ }
11090
+ }
11091
+ },
11092
+ "x-validated": false,
11093
+ "x-body-undocumented": true
11094
+ },
11095
+ "responses": {
11096
+ "200": {
11097
+ "description": "Success.",
11098
+ "content": {
11099
+ "application/json": {
11100
+ "schema": {
11101
+ "$ref": "#/components/schemas/PatchMeEventSoundPrefsResponse"
11102
+ }
11103
+ }
11104
+ }
11105
+ },
11106
+ "400": {
11107
+ "$ref": "#/components/responses/BadRequest"
11108
+ },
11109
+ "401": {
11110
+ "$ref": "#/components/responses/Unauthorized"
11111
+ },
11112
+ "403": {
11113
+ "$ref": "#/components/responses/Forbidden"
11114
+ }
11115
+ },
11116
+ "security": [
11117
+ {
11118
+ "builderSession": []
11119
+ }
11120
+ ]
11121
+ }
11122
+ },
11123
+ "/me/guild-requests": {
11124
+ "get": {
11125
+ "operationId": "get_me_guild_requests",
11126
+ "tags": [
11127
+ "me"
11128
+ ],
11129
+ "summary": "GET /me/guild-requests",
11130
+ "description": "MY queue: invites addressed to me, each carrying the card (the guild's current visibility, which an accept must echo back), and the join requests I sent. Stamps seen_at on the invites.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
11131
+ "x-rank": "any-builder",
11132
+ "x-source": "modules/platform-identity/routes/guilds.js",
11133
+ "responses": {
11134
+ "200": {
11135
+ "description": "Success.",
11136
+ "content": {
11137
+ "application/json": {
11138
+ "schema": {
11139
+ "$ref": "#/components/schemas/GetMeGuildRequestsResponse"
11140
+ }
11141
+ }
11142
+ }
11143
+ },
11144
+ "400": {
11145
+ "$ref": "#/components/responses/BadRequest"
11146
+ },
11147
+ "401": {
11148
+ "$ref": "#/components/responses/Unauthorized"
11149
+ },
11150
+ "403": {
11151
+ "$ref": "#/components/responses/Forbidden"
11152
+ }
11153
+ },
11154
+ "security": [
11155
+ {
11156
+ "builderSession": []
11157
+ }
11158
+ ]
11159
+ }
11160
+ },
11161
+ "/me/guilds": {
11162
+ "get": {
11163
+ "operationId": "get_me_guilds",
11164
+ "tags": [
11165
+ "me"
11166
+ ],
11167
+ "summary": "GET /me/guilds",
11168
+ "description": "MY guilds, each with my own two switches. count is the array's own length.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
10224
11169
  "x-rank": "any-builder",
10225
- "x-source": "modules/builder-settings/routes/settings.js",
10226
- "requestBody": {
10227
- "required": false,
10228
- "content": {
10229
- "application/json": {
10230
- "schema": {
10231
- "type": "object",
10232
- "additionalProperties": true
10233
- }
10234
- }
10235
- },
10236
- "x-validated": false,
10237
- "x-body-undocumented": true
10238
- },
11170
+ "x-source": "modules/platform-identity/routes/guilds.js",
10239
11171
  "responses": {
10240
11172
  "200": {
10241
11173
  "description": "Success.",
10242
11174
  "content": {
10243
11175
  "application/json": {
10244
11176
  "schema": {
10245
- "$ref": "#/components/schemas/PatchMeEventSoundPrefsResponse"
11177
+ "$ref": "#/components/schemas/GetMeGuildsResponse"
10246
11178
  }
10247
11179
  }
10248
11180
  }
@@ -12657,7 +13589,7 @@
12657
13589
  "projects"
12658
13590
  ],
12659
13591
  "summary": "POST /projects/invite",
12660
- "description": "own-or-metic in effect: invite a builder into a project (ADR 0141 §5, task #1002115), called by a `project.curate` holder OR the project's OWN OWNER (task 1003580; the ownership proof is `provisioning_instances.owner_builder_id`, reached through a port — see invite-authz.js). The wizard's audience is any signed-in GitHub user, typically a xenos who used to get 403 on their own project. A caller who is neither gets the unchanged permission refusal, identical whether or not the project exists. Pre-seeds a 'pending' membership the invitee accepts from My Projects. V1 invites by github_login and requires the invitee to already have a platform account (they build on some project); inviting someone entirely new to the platform (GitHub-id resolution) is a follow-up.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `project.curate` (all required).",
13592
+ "description": "own-or-metic in effect: invite a builder into a project (ADR 0141 §5, task 1002115), called by a `project.curate` holder OR the project's OWN OWNER (task 1003580; the ownership proof is `provisioning_instances.owner_builder_id`, reached through a port — see invite-authz.js). The wizard's audience is any signed-in GitHub user, typically a xenos who used to get 403 on their own project. A caller who is neither gets the unchanged permission refusal, identical whether or not the project exists. Pre-seeds a 'pending' membership the invitee accepts from My Projects. V1 invites by github_login and requires the invitee to already have a platform account (they build on some project); inviting someone entirely new to the platform (GitHub-id resolution) is a follow-up.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `project.curate` (all required).",
12661
13593
  "x-rank": "metic+archon",
12662
13594
  "x-source": "modules/platform-identity/routes/projects.js",
12663
13595
  "x-permissions": [
@@ -19401,6 +20333,42 @@
19401
20333
  "removed"
19402
20334
  ]
19403
20335
  },
20336
+ "DeleteGuildRequestsIdResponse": {
20337
+ "type": "object",
20338
+ "properties": {
20339
+ "outcome": {}
20340
+ },
20341
+ "required": [
20342
+ "outcome"
20343
+ ]
20344
+ },
20345
+ "DeleteGuildsSlugMembersHandleResponse": {
20346
+ "type": "object",
20347
+ "properties": {
20348
+ "outcome": {}
20349
+ },
20350
+ "required": [
20351
+ "outcome"
20352
+ ]
20353
+ },
20354
+ "DeleteGuildsSlugMembershipResponse": {
20355
+ "type": "object",
20356
+ "properties": {
20357
+ "outcome": {}
20358
+ },
20359
+ "required": [
20360
+ "outcome"
20361
+ ]
20362
+ },
20363
+ "DeleteGuildsSlugResponse": {
20364
+ "type": "object",
20365
+ "properties": {
20366
+ "outcome": {}
20367
+ },
20368
+ "required": [
20369
+ "outcome"
20370
+ ]
20371
+ },
19404
20372
  "DeleteInboxIdSparkResponse": {
19405
20373
  "type": "object",
19406
20374
  "properties": {
@@ -19928,6 +20896,25 @@
19928
20896
  "why"
19929
20897
  ]
19930
20898
  },
20899
+ "GetCommunityLeaderboardResponse": {
20900
+ "type": "object",
20901
+ "properties": {
20902
+ "window": {},
20903
+ "since": {},
20904
+ "discipline": {},
20905
+ "count": {},
20906
+ "results": {},
20907
+ "page": {}
20908
+ },
20909
+ "required": [
20910
+ "window",
20911
+ "since",
20912
+ "discipline",
20913
+ "count",
20914
+ "results",
20915
+ "page"
20916
+ ]
20917
+ },
19931
20918
  "GetCommunitySearchResponse": {
19932
20919
  "type": "object",
19933
20920
  "properties": {
@@ -20190,6 +21177,49 @@
20190
21177
  "builders"
20191
21178
  ]
20192
21179
  },
21180
+ "GetGuildsSlugMembersResponse": {
21181
+ "type": "object",
21182
+ "properties": {
21183
+ "guild": {},
21184
+ "you": {},
21185
+ "members": {},
21186
+ "count": {}
21187
+ },
21188
+ "required": [
21189
+ "guild",
21190
+ "you",
21191
+ "members",
21192
+ "count"
21193
+ ]
21194
+ },
21195
+ "GetGuildsSlugRequestsResponse": {
21196
+ "type": "object",
21197
+ "properties": {
21198
+ "incoming": {},
21199
+ "outgoing": {},
21200
+ "incoming_count": {},
21201
+ "outgoing_count": {}
21202
+ },
21203
+ "required": [
21204
+ "incoming",
21205
+ "outgoing",
21206
+ "incoming_count",
21207
+ "outgoing_count"
21208
+ ]
21209
+ },
21210
+ "GetGuildsSlugResponse": {
21211
+ "type": "object",
21212
+ "properties": {
21213
+ "guild": {},
21214
+ "members": {},
21215
+ "size": {}
21216
+ },
21217
+ "required": [
21218
+ "guild",
21219
+ "members",
21220
+ "size"
21221
+ ]
21222
+ },
20193
21223
  "GetHealthzResponse": {
20194
21224
  "type": "object",
20195
21225
  "properties": {
@@ -20432,6 +21462,32 @@
20432
21462
  "events"
20433
21463
  ]
20434
21464
  },
21465
+ "GetMeGuildRequestsResponse": {
21466
+ "type": "object",
21467
+ "properties": {
21468
+ "incoming": {},
21469
+ "outgoing": {},
21470
+ "incoming_count": {},
21471
+ "outgoing_count": {}
21472
+ },
21473
+ "required": [
21474
+ "incoming",
21475
+ "outgoing",
21476
+ "incoming_count",
21477
+ "outgoing_count"
21478
+ ]
21479
+ },
21480
+ "GetMeGuildsResponse": {
21481
+ "type": "object",
21482
+ "properties": {
21483
+ "guilds": {},
21484
+ "count": {}
21485
+ },
21486
+ "required": [
21487
+ "guilds",
21488
+ "count"
21489
+ ]
21490
+ },
20435
21491
  "GetMeRecentShipsResponse": {
20436
21492
  "type": "object",
20437
21493
  "properties": {
@@ -21583,6 +22639,41 @@
21583
22639
  "changed"
21584
22640
  ]
21585
22641
  },
22642
+ "PatchGuildsSlugMembershipRequest": {
22643
+ "type": "object",
22644
+ "properties": {
22645
+ "shown_publicly": {
22646
+ "type": "boolean"
22647
+ },
22648
+ "counted_in_totals": {}
22649
+ },
22650
+ "additionalProperties": false
22651
+ },
22652
+ "PatchGuildsSlugRequest": {
22653
+ "type": "object",
22654
+ "properties": {
22655
+ "name": {
22656
+ "type": "string",
22657
+ "minLength": 1
22658
+ },
22659
+ "description": {
22660
+ "type": "string"
22661
+ },
22662
+ "visibility": {
22663
+ "type": "string"
22664
+ }
22665
+ },
22666
+ "additionalProperties": false
22667
+ },
22668
+ "PatchGuildsSlugResponse": {
22669
+ "type": "object",
22670
+ "properties": {
22671
+ "guild": {}
22672
+ },
22673
+ "required": [
22674
+ "guild"
22675
+ ]
22676
+ },
21586
22677
  "PatchHelpRequestsIdRequest": {
21587
22678
  "type": "object",
21588
22679
  "properties": {
@@ -24086,6 +25177,125 @@
24086
25177
  "rank"
24087
25178
  ]
24088
25179
  },
25180
+ "PostGuildRequestsIdAcceptRequest": {
25181
+ "type": "object",
25182
+ "properties": {
25183
+ "visibility": {
25184
+ "type": "string"
25185
+ },
25186
+ "counted_in_totals": {}
25187
+ },
25188
+ "additionalProperties": false
25189
+ },
25190
+ "PostGuildRequestsIdAcceptResponse": {
25191
+ "type": "object",
25192
+ "properties": {
25193
+ "status": {}
25194
+ },
25195
+ "required": [
25196
+ "status"
25197
+ ]
25198
+ },
25199
+ "PostGuildsRequest": {
25200
+ "type": "object",
25201
+ "properties": {
25202
+ "slug": {
25203
+ "type": "string",
25204
+ "minLength": 1,
25205
+ "maxLength": 39
25206
+ },
25207
+ "name": {
25208
+ "type": "string",
25209
+ "minLength": 1
25210
+ },
25211
+ "description": {
25212
+ "type": "string"
25213
+ },
25214
+ "visibility": {
25215
+ "type": "string"
25216
+ }
25217
+ },
25218
+ "required": [
25219
+ "slug",
25220
+ "name",
25221
+ "visibility"
25222
+ ],
25223
+ "additionalProperties": false
25224
+ },
25225
+ "PostGuildsResponse": {
25226
+ "type": "object",
25227
+ "properties": {
25228
+ "guild": {}
25229
+ },
25230
+ "required": [
25231
+ "guild"
25232
+ ]
25233
+ },
25234
+ "PostGuildsSlugInvitesRequest": {
25235
+ "type": "object",
25236
+ "properties": {
25237
+ "handles": {
25238
+ "type": "array",
25239
+ "items": {
25240
+ "type": "string"
25241
+ }
25242
+ }
25243
+ },
25244
+ "required": [
25245
+ "handles"
25246
+ ],
25247
+ "additionalProperties": false
25248
+ },
25249
+ "PostGuildsSlugInvitesResponse": {
25250
+ "type": "object",
25251
+ "properties": {
25252
+ "results": {},
25253
+ "count": {}
25254
+ },
25255
+ "required": [
25256
+ "results",
25257
+ "count"
25258
+ ]
25259
+ },
25260
+ "PostGuildsSlugRequestsRequest": {
25261
+ "type": "object",
25262
+ "properties": {
25263
+ "counted_in_totals": {}
25264
+ },
25265
+ "additionalProperties": false
25266
+ },
25267
+ "PostGuildsSlugRequestsResponse": {
25268
+ "type": "object",
25269
+ "properties": {
25270
+ "status": {}
25271
+ },
25272
+ "required": [
25273
+ "status"
25274
+ ]
25275
+ },
25276
+ "PostGuildsSlugTransferRequest": {
25277
+ "type": "object",
25278
+ "properties": {
25279
+ "handle": {
25280
+ "type": "string",
25281
+ "minLength": 1,
25282
+ "maxLength": 39
25283
+ }
25284
+ },
25285
+ "required": [
25286
+ "handle"
25287
+ ],
25288
+ "additionalProperties": false
25289
+ },
25290
+ "PostGuildsSlugTransferResponse": {
25291
+ "type": "object",
25292
+ "properties": {
25293
+ "outcome": {}
25294
+ },
25295
+ "required": [
25296
+ "outcome"
25297
+ ]
25298
+ },
24089
25299
  "PostHelpRequestsIdRepliesRequest": {
24090
25300
  "type": "object",
24091
25301
  "properties": {
@@ -26572,9 +27782,9 @@
26572
27782
  "description": "A required dependency/feature is not configured or is temporarily down."
26573
27783
  }
26574
27784
  },
26575
- "x-endpoint-count": 419,
26576
- "x-schema-count": 456,
27785
+ "x-endpoint-count": 437,
27786
+ "x-schema-count": 479,
26577
27787
  "x-undocumented-bodies": 9,
26578
- "x-response-schemas": 311,
27788
+ "x-response-schemas": 327,
26579
27789
  "x-generated-by": "scripts/gds/gen-api-docs.js"
26580
27790
  }