@bongos/core 1.25.20 → 1.25.21

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 (75) hide show
  1. package/.bongos-core.json +157 -62
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +10 -0
  4. package/clients/bongos-client/index.cjs +10 -0
  5. package/clients/bongos-client/index.d.ts +23 -8
  6. package/clients/bongos-client/index.mjs +10 -0
  7. package/docs/adr/0406-every-project-gets-a-sandbox-that-follows-main-and-live-moves-only-when-a-person-promotes.md +10 -0
  8. package/docs/adr/0407-a-project-grant-is-made-by-the-owner-not-witnessed-at-sign-in.md +3 -1
  9. package/docs/api/openapi.json +409 -36
  10. package/docs/api-reference.md +7 -2
  11. package/docs/copy-inventory.md +20 -20
  12. package/docs/copy-registry.json +23 -23
  13. package/docs/module-api-changelog.md +2 -0
  14. package/docs/page-readings.json +2 -2
  15. package/modules/government/board.js +8 -7
  16. package/modules/government/charter-advice.js +10 -3
  17. package/modules/government/charter.js +88 -4
  18. package/modules/government/migrations/government_039_vote_cast_via_filed.sql +22 -0
  19. package/modules/government/routes/government.js +12 -12
  20. package/modules/government/sole-member.js +75 -0
  21. package/modules/hall-ui/public/genesis-home.js +12 -5
  22. package/modules/onboarding/genesis-home.js +14 -2
  23. package/modules/platform-identity/module.json +1 -1
  24. package/modules/platform-identity/project-standing.js +49 -0
  25. package/modules/platform-identity/routes/my-projects.js +8 -0
  26. package/modules/provisioning/grants.js +126 -0
  27. package/modules/provisioning/migrations/provisioning_038_render_promote.sql +43 -0
  28. package/modules/provisioning/migrations/provisioning_039_project_grants.sql +40 -0
  29. package/modules/provisioning/module.json +2 -1
  30. package/modules/provisioning/project-refusal.js +107 -0
  31. package/modules/provisioning/provisioning.js +2 -2
  32. package/modules/provisioning/render-lookup.js +36 -1
  33. package/modules/provisioning/render-promote.js +80 -0
  34. package/modules/provisioning/route-helpers.js +70 -16
  35. package/modules/provisioning/routes/env-manifest.js +21 -5
  36. package/modules/provisioning/routes/grants.js +141 -0
  37. package/modules/provisioning/routes/provisioning.js +8 -7
  38. package/modules/provisioning/routes/render-standup.js +82 -3
  39. package/modules/public-landing/public/projects.html +99 -1
  40. package/package-lock.json +2 -2
  41. package/package.json +1 -1
  42. package/release-notes.json +18 -0
  43. package/scripts/bongos/genesis.js +11 -1
  44. package/scripts/bongos/government-walk-sole.js +41 -0
  45. package/scripts/bongos/government-walk.js +15 -16
  46. package/scripts/bongos/provision-render-common.js +48 -0
  47. package/scripts/bongos/provision-render-promote.js +93 -0
  48. package/scripts/bongos/provision-render-sandbox.js +14 -2
  49. package/scripts/bongos/provision-render.js +4 -36
  50. package/scripts/bongos/provision.js +1 -0
  51. package/scripts/bongos/render-api.js +17 -2
  52. package/scripts/gds/government-walk-sole.js +6 -0
  53. package/scripts/gds/provision-render-common.js +6 -0
  54. package/scripts/gds/provision-render-promote.js +6 -0
  55. package/src/module-api.js +1 -1
  56. package/tests/fleet_cost_view.mjs +2 -0
  57. package/tests/genesis_government.mjs +5 -1
  58. package/tests/genesis_home.mjs +17 -0
  59. package/tests/government_charter_advice.mjs +49 -0
  60. package/tests/government_charter_forms.mjs +14 -3
  61. package/tests/government_seed.mjs +1 -0
  62. package/tests/government_sole_member.mjs +153 -0
  63. package/tests/project_catalog_routes.mjs +2 -0
  64. package/tests/provisioning_env_manifest.mjs +4 -0
  65. package/tests/provisioning_env_manifest_edit_delete.mjs +2 -0
  66. package/tests/provisioning_project_grants.mjs +458 -0
  67. package/tests/provisioning_project_owner_gate.mjs +3 -1
  68. package/tests/provisioning_project_refusal.mjs +316 -0
  69. package/tests/provisioning_render_route.mjs +1 -1
  70. package/tests/provisioning_restart.mjs +2 -0
  71. package/tests/provisioning_settings.mjs +2 -0
  72. package/tests/provisioning_settings_apply.mjs +1 -1
  73. package/tests/provisioning_teardown_intent.mjs +2 -2
  74. package/tests/render_promote.mjs +309 -0
  75. package/tests/render_sandbox.mjs +9 -4
@@ -1317,6 +1317,12 @@ function createClient(opts = {}) {
1317
1317
  postProvisioningInstancesIdGithubApp: (args) => request("POST", "/provisioning/instances/{id}/github-app", { hasBody: true }, args),
1318
1318
  // POST /provisioning/instances/{id}/github-app/pick-up — rank: any-builder — POST /provisioning/instances/:id/github-app/pick-up
1319
1319
  postProvisioningInstancesIdGithubAppPickUp: (args) => request("POST", "/provisioning/instances/{id}/github-app/pick-up", { hasBody: true }, args),
1320
+ // GET /provisioning/instances/{id}/grants — rank: any-builder — GET /provisioning/instances/:id/grants
1321
+ getProvisioningInstancesIdGrants: (args) => request("GET", "/provisioning/instances/{id}/grants", { hasBody: false }, args),
1322
+ // POST /provisioning/instances/{id}/grants — rank: any-builder — POST /provisioning/instances/:id/grants
1323
+ postProvisioningInstancesIdGrants: (args) => request("POST", "/provisioning/instances/{id}/grants", { hasBody: true }, args),
1324
+ // DELETE /provisioning/instances/{id}/grants/{grantId} — rank: any-builder — DELETE /provisioning/instances/:id/grants/:grantId
1325
+ deleteProvisioningInstancesIdGrantsGrantId: (args) => request("DELETE", "/provisioning/instances/{id}/grants/{grantId}", { hasBody: false }, args),
1320
1326
  // GET /provisioning/instances/{id}/invite-suggestions — rank: any-builder — GET /provisioning/instances/:id/invite-suggestions
1321
1327
  getProvisioningInstancesIdInviteSuggestions: (args) => request("GET", "/provisioning/instances/{id}/invite-suggestions", { hasBody: false }, args),
1322
1328
  // DELETE /provisioning/instances/{id}/logo — rank: any-builder — DELETE /provisioning/instances/:id/logo
@@ -1331,6 +1337,10 @@ function createClient(opts = {}) {
1331
1337
  getProvisioningInstancesIdRender: (args) => request("GET", "/provisioning/instances/{id}/render", { hasBody: false }, args),
1332
1338
  // POST /provisioning/instances/{id}/render — rank: any-builder — POST /provisioning/instances/:id/render
1333
1339
  postProvisioningInstancesIdRender: (args) => request("POST", "/provisioning/instances/{id}/render", { hasBody: true }, args),
1340
+ // POST /provisioning/instances/{id}/render/promote — rank: any-builder — POST /provisioning/instances/:id/render/promote
1341
+ postProvisioningInstancesIdRenderPromote: (args) => request("POST", "/provisioning/instances/{id}/render/promote", { hasBody: true }, args),
1342
+ // POST /provisioning/instances/{id}/render/promote/look — rank: any-builder — POST /provisioning/instances/:id/render/promote/look
1343
+ postProvisioningInstancesIdRenderPromoteLook: (args) => request("POST", "/provisioning/instances/{id}/render/promote/look", { hasBody: true }, args),
1334
1344
  // POST /provisioning/instances/{id}/render/sandbox — rank: any-builder — POST /provisioning/instances/:id/render/sandbox
1335
1345
  postProvisioningInstancesIdRenderSandbox: (args) => request("POST", "/provisioning/instances/{id}/render/sandbox", { hasBody: true }, args),
1336
1346
  // POST /provisioning/instances/{id}/repo-private — rank: any-builder — POST /provisioning/instances/:id/repo-private
@@ -1316,6 +1316,12 @@ function createClient(opts = {}) {
1316
1316
  postProvisioningInstancesIdGithubApp: (args) => request("POST", "/provisioning/instances/{id}/github-app", { hasBody: true }, args),
1317
1317
  // POST /provisioning/instances/{id}/github-app/pick-up — rank: any-builder — POST /provisioning/instances/:id/github-app/pick-up
1318
1318
  postProvisioningInstancesIdGithubAppPickUp: (args) => request("POST", "/provisioning/instances/{id}/github-app/pick-up", { hasBody: true }, args),
1319
+ // GET /provisioning/instances/{id}/grants — rank: any-builder — GET /provisioning/instances/:id/grants
1320
+ getProvisioningInstancesIdGrants: (args) => request("GET", "/provisioning/instances/{id}/grants", { hasBody: false }, args),
1321
+ // POST /provisioning/instances/{id}/grants — rank: any-builder — POST /provisioning/instances/:id/grants
1322
+ postProvisioningInstancesIdGrants: (args) => request("POST", "/provisioning/instances/{id}/grants", { hasBody: true }, args),
1323
+ // DELETE /provisioning/instances/{id}/grants/{grantId} — rank: any-builder — DELETE /provisioning/instances/:id/grants/:grantId
1324
+ deleteProvisioningInstancesIdGrantsGrantId: (args) => request("DELETE", "/provisioning/instances/{id}/grants/{grantId}", { hasBody: false }, args),
1319
1325
  // GET /provisioning/instances/{id}/invite-suggestions — rank: any-builder — GET /provisioning/instances/:id/invite-suggestions
1320
1326
  getProvisioningInstancesIdInviteSuggestions: (args) => request("GET", "/provisioning/instances/{id}/invite-suggestions", { hasBody: false }, args),
1321
1327
  // DELETE /provisioning/instances/{id}/logo — rank: any-builder — DELETE /provisioning/instances/:id/logo
@@ -1330,6 +1336,10 @@ function createClient(opts = {}) {
1330
1336
  getProvisioningInstancesIdRender: (args) => request("GET", "/provisioning/instances/{id}/render", { hasBody: false }, args),
1331
1337
  // POST /provisioning/instances/{id}/render — rank: any-builder — POST /provisioning/instances/:id/render
1332
1338
  postProvisioningInstancesIdRender: (args) => request("POST", "/provisioning/instances/{id}/render", { hasBody: true }, args),
1339
+ // POST /provisioning/instances/{id}/render/promote — rank: any-builder — POST /provisioning/instances/:id/render/promote
1340
+ postProvisioningInstancesIdRenderPromote: (args) => request("POST", "/provisioning/instances/{id}/render/promote", { hasBody: true }, args),
1341
+ // POST /provisioning/instances/{id}/render/promote/look — rank: any-builder — POST /provisioning/instances/:id/render/promote/look
1342
+ postProvisioningInstancesIdRenderPromoteLook: (args) => request("POST", "/provisioning/instances/{id}/render/promote/look", { hasBody: true }, args),
1333
1343
  // POST /provisioning/instances/{id}/render/sandbox — rank: any-builder — POST /provisioning/instances/:id/render/sandbox
1334
1344
  postProvisioningInstancesIdRenderSandbox: (args) => request("POST", "/provisioning/instances/{id}/render/sandbox", { hasBody: true }, args),
1335
1345
  // POST /provisioning/instances/{id}/repo-private — rank: any-builder — POST /provisioning/instances/:id/repo-private
@@ -24,6 +24,7 @@ export interface DeleteMeSignInMethodsProviderResponse { ok: boolean }
24
24
  export interface DeleteMemoryFileResponse { deleted: boolean; builder_id: unknown; path: unknown }
25
25
  export interface DeleteNotesIdResponse { ok: boolean }
26
26
  export interface DeleteProvisioningInstancesIdEnvManifestVarIdResponse { ok: boolean; removed: boolean; message: unknown }
27
+ export interface DeleteProvisioningInstancesIdGrantsGrantIdResponse { ok: boolean; revoked: boolean }
27
28
  export interface DeleteSpecialitiesIdDocumentsDocIdResponse { ok: boolean }
28
29
  export interface DeleteTasksIdCriteriaCriterionIdResponse { ok: boolean; removed: unknown }
29
30
  export interface DeleteTasksIdDependenciesDepIdResponse { ok: boolean; removed: unknown }
@@ -164,10 +165,10 @@ export interface GetProvisioningGithubAppResumeResponse { ok: boolean; project:
164
165
  export interface GetProvisioningGithubAppStatusResponse { ok: boolean; status: unknown; failure: unknown; ready: unknown; terminal: unknown; domain: unknown; instance_status: unknown; app_slug: unknown; checklist: unknown; install_url: unknown; repo: unknown; verifying: unknown; app_org: unknown; pick_up: unknown }
165
166
  export interface GetProvisioningHealthResponse { module: unknown; ok: boolean; coreVersion: unknown }
166
167
  export interface GetProvisioningInstancesIdEnvManifestResponse { instance: unknown; env_vars: unknown; environments: unknown; values_held: boolean; note: unknown }
168
+ export interface GetProvisioningInstancesIdGrantsResponse { grants: unknown; levels: unknown; limit: unknown }
167
169
  export interface GetProvisioningInstancesIdInviteSuggestionsResponse { suggested: unknown; seeking: unknown; reason: unknown; unavailable?: unknown }
168
170
  export interface GetProvisioningInstancesIdLookResponse { ok: boolean; look: unknown; options: unknown; logo_max_bytes: unknown }
169
- export interface GetProvisioningInstancesIdRenderResponse { ok: boolean; app: unknown; sandbox: unknown; env_vars: unknown; values_held: boolean }
170
- export interface GetProvisioningInstancesIdResponse { instance: unknown; open_intent: unknown; github_app: unknown; github_setup: unknown }
171
+ export interface GetProvisioningInstancesIdRenderResponse { ok: boolean; app: unknown; sandbox: unknown; promote: unknown; env_vars: unknown; values_held: boolean }
171
172
  export interface GetProvisioningInstancesIdSettingsResponse { ok: boolean; settings: unknown; planet: unknown; options: unknown }
172
173
  export interface GetProvisioningInstancesResponse { instances: unknown }
173
174
  export interface GetProvisioningOnboardPlanResponse { mode: unknown; steps: unknown }
@@ -446,19 +447,19 @@ export interface PostGovernmentAsksMatterIdUnclearRequest { note?: string }
446
447
  export interface PostGovernmentAsksMatterIdUnclearResponse { ok: boolean; matter_id: unknown; flag: unknown }
447
448
  export interface PostGovernmentAssignmentsRequest { builder_id: number; rank_key: string }
448
449
  export interface PostGovernmentBoardCharterRequest { form?: string; franchise?: string; council_rank?: string; pass_rule?: string; window_minutes?: unknown; rationale_md?: string; acknowledge_self_removal?: boolean }
449
- export interface PostGovernmentBoardCharterResponse { ok: boolean; amendment: unknown; item: unknown; reads_as: unknown }
450
+ export interface PostGovernmentBoardCharterResponse { ok: boolean; amendment: unknown; item: unknown; reads_as: unknown; what_happens_next: unknown; decided: unknown }
450
451
  export interface PostGovernmentBoardDecisionRulesRequest { rules: unknown[]; rationale_md?: string }
451
- export interface PostGovernmentBoardDecisionRulesResponse { ok: boolean; amendment: unknown; item: unknown; rules: unknown }
452
+ export interface PostGovernmentBoardDecisionRulesResponse { ok: boolean; amendment: unknown; item: unknown; rules: unknown; decided: unknown }
452
453
  export interface PostGovernmentBoardItemsItemIdVotesRequest { direction: string; reason_md?: string; section_key?: string }
453
454
  export interface PostGovernmentBoardItemsItemIdWithdrawResponse { ok: boolean; item_closed: boolean; outcome: unknown; item: unknown }
454
455
  export interface PostGovernmentBoardItemsRequest { subject_type: string; proposal: Record<string, unknown>; rationale_md?: string; acknowledge_self_removal?: boolean }
455
- export interface PostGovernmentBoardItemsResponse { ok: boolean; amendment: unknown; item: unknown }
456
+ export interface PostGovernmentBoardItemsResponse { ok: boolean; amendment: unknown; item: unknown; decided: unknown }
456
457
  export interface PostGovernmentBoardLawsRequest { enact?: unknown[]; repeal?: unknown[]; rationale_md?: string }
457
- export interface PostGovernmentBoardLawsResponse { ok: boolean; amendment: unknown; item: unknown; proposal: unknown }
458
+ export interface PostGovernmentBoardLawsResponse { ok: boolean; amendment: unknown; item: unknown; proposal: unknown; decided: unknown }
458
459
  export interface PostGovernmentBoardMattersRequest { area: string; title: string; detail_md?: string; action?: Record<string, unknown> }
459
460
  export interface PostGovernmentBoardMattersResponse { ok: boolean; matter: unknown; item: unknown; decided: unknown }
460
461
  export interface PostGovernmentBoardPresetsRequest { preset: string }
461
- export interface PostGovernmentBoardPresetsResponse { ok: boolean; preset: unknown; amendment: unknown; item: unknown; proposal: unknown }
462
+ export interface PostGovernmentBoardPresetsResponse { ok: boolean; preset: unknown; amendment: unknown; item: unknown; proposal: unknown; decided: unknown }
462
463
  export interface PostGovernmentBuildoutsRequest { building: string; capsule: string; name?: string; line?: string }
463
464
  export interface PostGovernmentBuildoutsResponse { ok: boolean; path: unknown; buildout?: unknown; rights: unknown; matter?: unknown; item?: unknown; decided?: unknown }
464
465
  export interface PostGovernmentRankSetsApplyRequest { type?: string; headcount: number }
@@ -611,6 +612,10 @@ export interface PostProvisioningInstancesIdEnvManifestRequest { name: string; d
611
612
  export interface PostProvisioningInstancesIdForceTeardownResponse { ok: boolean; queued: unknown; action: unknown; status: unknown; superseded: unknown; message: unknown }
612
613
  export interface PostProvisioningInstancesIdGithubAppRequest { account?: string; org?: string }
613
614
  export interface PostProvisioningInstancesIdGithubAppResponse { state: unknown; account: unknown; post_url: unknown; personal_url: unknown; org_url: unknown; org_login: unknown; manifest: unknown; redirect_url: unknown; expires_at: unknown }
615
+ export interface PostProvisioningInstancesIdGrantsRequest { github_login: string }
616
+ export interface PostProvisioningInstancesIdRenderPromoteLookRequest { key: string }
617
+ export interface PostProvisioningInstancesIdRenderPromoteRequest { key: string; commit_id: string; expect_live_commit?: string }
618
+ export interface PostProvisioningInstancesIdRenderPromoteResponse { ok: boolean; queued: boolean; action: unknown; message: unknown }
614
619
  export interface PostProvisioningInstancesIdRenderRequest { key: string; owner_id: string; mode: string; service_id?: string; web_plan?: string; db_plan?: string; region?: string; runtime?: string; build_command?: string; start_command?: string; database?: boolean }
615
620
  export interface PostProvisioningInstancesIdRenderResponse { ok: boolean; queued: boolean; action: unknown; mode: unknown; with_setup: boolean; message: unknown }
616
621
  export interface PostProvisioningInstancesIdRenderSandboxRequest { key: string }
@@ -1982,7 +1987,7 @@ export interface BongosClient {
1982
1987
  /** POST /provisioning/instances — rank: any-builder */
1983
1988
  postProvisioningInstances(args: RequestArgs & { body: PostProvisioningInstancesRequest }): Promise<ApiResponse>;
1984
1989
  /** GET /provisioning/instances/{id} — rank: any-builder */
1985
- getProvisioningInstancesId(args?: RequestArgs): Promise<GetProvisioningInstancesIdResponse>;
1990
+ getProvisioningInstancesId(args?: RequestArgs): Promise<ApiResponse>;
1986
1991
  /** GET /provisioning/instances/{id}/core-upgrade — rank: archon */
1987
1992
  getProvisioningInstancesIdCoreUpgrade(args?: RequestArgs): Promise<ApiResponse>;
1988
1993
  /** POST /provisioning/instances/{id}/core-upgrade — rank: archon */
@@ -2009,6 +2014,12 @@ export interface BongosClient {
2009
2014
  postProvisioningInstancesIdGithubApp(args?: RequestArgs & { body?: PostProvisioningInstancesIdGithubAppRequest }): Promise<PostProvisioningInstancesIdGithubAppResponse>;
2010
2015
  /** POST /provisioning/instances/{id}/github-app/pick-up — rank: any-builder */
2011
2016
  postProvisioningInstancesIdGithubAppPickUp(args?: RequestArgs): Promise<ApiResponse>;
2017
+ /** GET /provisioning/instances/{id}/grants — rank: any-builder */
2018
+ getProvisioningInstancesIdGrants(args?: RequestArgs): Promise<GetProvisioningInstancesIdGrantsResponse>;
2019
+ /** POST /provisioning/instances/{id}/grants — rank: any-builder */
2020
+ postProvisioningInstancesIdGrants(args: RequestArgs & { body: PostProvisioningInstancesIdGrantsRequest }): Promise<ApiResponse>;
2021
+ /** DELETE /provisioning/instances/{id}/grants/{grantId} — rank: any-builder */
2022
+ deleteProvisioningInstancesIdGrantsGrantId(args?: RequestArgs): Promise<DeleteProvisioningInstancesIdGrantsGrantIdResponse>;
2012
2023
  /** GET /provisioning/instances/{id}/invite-suggestions — rank: any-builder */
2013
2024
  getProvisioningInstancesIdInviteSuggestions(args?: RequestArgs): Promise<GetProvisioningInstancesIdInviteSuggestionsResponse>;
2014
2025
  /** DELETE /provisioning/instances/{id}/logo — rank: any-builder */
@@ -2023,6 +2034,10 @@ export interface BongosClient {
2023
2034
  getProvisioningInstancesIdRender(args?: RequestArgs): Promise<GetProvisioningInstancesIdRenderResponse>;
2024
2035
  /** POST /provisioning/instances/{id}/render — rank: any-builder */
2025
2036
  postProvisioningInstancesIdRender(args: RequestArgs & { body: PostProvisioningInstancesIdRenderRequest }): Promise<PostProvisioningInstancesIdRenderResponse>;
2037
+ /** POST /provisioning/instances/{id}/render/promote — rank: any-builder */
2038
+ postProvisioningInstancesIdRenderPromote(args: RequestArgs & { body: PostProvisioningInstancesIdRenderPromoteRequest }): Promise<PostProvisioningInstancesIdRenderPromoteResponse>;
2039
+ /** POST /provisioning/instances/{id}/render/promote/look — rank: any-builder */
2040
+ postProvisioningInstancesIdRenderPromoteLook(args: RequestArgs & { body: PostProvisioningInstancesIdRenderPromoteLookRequest }): Promise<ApiResponse>;
2026
2041
  /** POST /provisioning/instances/{id}/render/sandbox — rank: any-builder */
2027
2042
  postProvisioningInstancesIdRenderSandbox(args: RequestArgs & { body: PostProvisioningInstancesIdRenderSandboxRequest }): Promise<PostProvisioningInstancesIdRenderSandboxResponse>;
2028
2043
  /** POST /provisioning/instances/{id}/repo-private — rank: any-builder */
@@ -1313,6 +1313,12 @@ export function createClient(opts = {}) {
1313
1313
  postProvisioningInstancesIdGithubApp: (args) => request("POST", "/provisioning/instances/{id}/github-app", { hasBody: true }, args),
1314
1314
  // POST /provisioning/instances/{id}/github-app/pick-up — rank: any-builder — POST /provisioning/instances/:id/github-app/pick-up
1315
1315
  postProvisioningInstancesIdGithubAppPickUp: (args) => request("POST", "/provisioning/instances/{id}/github-app/pick-up", { hasBody: true }, args),
1316
+ // GET /provisioning/instances/{id}/grants — rank: any-builder — GET /provisioning/instances/:id/grants
1317
+ getProvisioningInstancesIdGrants: (args) => request("GET", "/provisioning/instances/{id}/grants", { hasBody: false }, args),
1318
+ // POST /provisioning/instances/{id}/grants — rank: any-builder — POST /provisioning/instances/:id/grants
1319
+ postProvisioningInstancesIdGrants: (args) => request("POST", "/provisioning/instances/{id}/grants", { hasBody: true }, args),
1320
+ // DELETE /provisioning/instances/{id}/grants/{grantId} — rank: any-builder — DELETE /provisioning/instances/:id/grants/:grantId
1321
+ deleteProvisioningInstancesIdGrantsGrantId: (args) => request("DELETE", "/provisioning/instances/{id}/grants/{grantId}", { hasBody: false }, args),
1316
1322
  // GET /provisioning/instances/{id}/invite-suggestions — rank: any-builder — GET /provisioning/instances/:id/invite-suggestions
1317
1323
  getProvisioningInstancesIdInviteSuggestions: (args) => request("GET", "/provisioning/instances/{id}/invite-suggestions", { hasBody: false }, args),
1318
1324
  // DELETE /provisioning/instances/{id}/logo — rank: any-builder — DELETE /provisioning/instances/:id/logo
@@ -1327,6 +1333,10 @@ export function createClient(opts = {}) {
1327
1333
  getProvisioningInstancesIdRender: (args) => request("GET", "/provisioning/instances/{id}/render", { hasBody: false }, args),
1328
1334
  // POST /provisioning/instances/{id}/render — rank: any-builder — POST /provisioning/instances/:id/render
1329
1335
  postProvisioningInstancesIdRender: (args) => request("POST", "/provisioning/instances/{id}/render", { hasBody: true }, args),
1336
+ // POST /provisioning/instances/{id}/render/promote — rank: any-builder — POST /provisioning/instances/:id/render/promote
1337
+ postProvisioningInstancesIdRenderPromote: (args) => request("POST", "/provisioning/instances/{id}/render/promote", { hasBody: true }, args),
1338
+ // POST /provisioning/instances/{id}/render/promote/look — rank: any-builder — POST /provisioning/instances/:id/render/promote/look
1339
+ postProvisioningInstancesIdRenderPromoteLook: (args) => request("POST", "/provisioning/instances/{id}/render/promote/look", { hasBody: true }, args),
1330
1340
  // POST /provisioning/instances/{id}/render/sandbox — rank: any-builder — POST /provisioning/instances/:id/render/sandbox
1331
1341
  postProvisioningInstancesIdRenderSandbox: (args) => request("POST", "/provisioning/instances/{id}/render/sandbox", { hasBody: true }, args),
1332
1342
  // POST /provisioning/instances/{id}/repo-private — rank: any-builder — POST /provisioning/instances/:id/repo-private
@@ -114,3 +114,13 @@ Each sibling task's description now opens with a dated note, *"Re-read against A
114
114
  - **The live service's switch to manual deploys is left to the promote task** ([task 1005290](https://cloudbongos.com/builders#/task/1005290)), so that live never stops updating before there is a button that moves it.
115
115
  - **Teardown needs the owner's key to remove the sandbox**, typed on the teardown screen and used once. The Render client gains one delete, `deleteSandboxService`, which refuses anything not named `…-sandbox` on the free plan. Without the key the sandbox stays on the owner's account and the owner is told exactly what to remove.
116
116
 
117
+ ## 11. Amendment — how promotion was built (2026-11-13, [task 1005290](https://cloudbongos.com/builders#/task/1005290))
118
+
119
+ - **Where the owner promotes:** the project's own page on the hub, under the sandbox — where the sandbox's address and state already are. Not the hall's deploy page (`modules/render-deploy`): that module is never turned on in a project's hall today, so a promote button there would be unreachable.
120
+ - **Who may promote: the project's owner, decided on the server** (`POST /provisioning/instances/:id/render/promote`). The key and the live app are the owner's, so an archon on someone else's project gets the same 404 as a stranger — the rule every Render route here already follows (ADR 0327).
121
+ - **What waits, and the read it takes.** The owner sees the change the live app runs and every change that has run on the sandbox since, newest first. Reading that needs Render, so **ADR 0348's read-only exception is widened by exactly one look**: `lookupPromotion` in `modules/provisioning/render-lookup.js` may GET the project's two recorded services (live and sandbox) and their deploy lists, on the owner's key held for that one request. GET only, every service asserted to carry the project's workspace, nothing stored or returned.
122
+ - **The press** queues a `render-promote` intent holding the owner's encrypted key (the borrowed-key rule, ADR 0327 §2). The runner (`scripts/bongos/provision-render-promote.js`) checks the change ran on the sandbox and that live still runs what the owner was shown, **writes the promotion to `provisioning_promotions` before sending anything**, switches the live service to manual deploys, and asks Render to deploy exactly that commit (`triggerDeploy` now takes a `commitId`).
123
+ - **When the live app stops following `main`.** As soon as its sandbox is running (the sandbox setup switches it), and again on every promotion. Not before: a project whose sandbox failed keeps deploying every merge, so finished work always has somewhere to land.
124
+ - **Ship mode is untouched.** No ship, in Auto or Manual mode, reaches the promote leg; landing reaches the sandbox only.
125
+ - **What this cannot stop, said plainly.** The owner can still deploy the live service from Render's own dashboard, or turn its automatic deploys back on there. Bongos does not see or record those. "Nothing reaches the main site without a recorded promotion" holds for everything Bongos does, not for what the owner does in Render directly.
126
+
@@ -31,7 +31,7 @@ So no value of `membership_kind` means "the owner trusts this person with the ho
31
31
 
32
32
  ### 1. The grant is its own row, in the provisioning module, with one writer
33
33
 
34
- A new table, `provisioning_project_grants` (migration `<redacted>.sql`, additive, in the module's own namespace):
34
+ A new table, `provisioning_project_grants` (migration `provisioning_039_project_grants.sql`, additive, in the module's own namespace):
35
35
 
36
36
  | Column | Meaning |
37
37
  |---|---|
@@ -99,6 +99,8 @@ The word is documented as "has signed in here" (ADR 0328's amendment already say
99
99
  - **How the row is read.** Through a port, `platform-identity.projectMembershipKind` as ADR 0328 named it, and only after the gate has already refused. If the port is unregistered, throws, or answers an unknown value, the answer is the `404`: on doubt, say less. This read only picks the wording of a refusal and can never admit anyone.
100
100
  - **A grantee on a route without the opt-in** → `403 owner_only`, "Only the project's owner can do this." They already know the project exists.
101
101
 
102
+ **As built (task 1004162), three refinements, none wider than the above:** (1) a `member` or `owner` row on a **private or stealth** project gets the 404, not `project_access_required` — a `member` row comes from any device-flow sign-in, so it is no evidence a person may learn that a closed project's id is real; (2) a person with no row who **applied** inside the 30-day application window is told the owner decides (`project_membership_required`, `apply.state: 'applied'`); (3) a `pending` row is an invitation, so it is told to sign in to the project to accept. The routes that check ownership inline (look, demo, render, repo-private, the GitHub pick-up, core-upgrade) still answer the plain 404.
103
+
102
104
  ## Consequences
103
105
 
104
106
  - A team can share one hosted project's day-to-day running, once task 1004161 lands, without the owner handing over teardown, the address or their own accounts.