@bongos/core 1.25.11 → 1.25.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.bongos-core.json CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/bongos/package-core.js",
5
- "core_version": "1.25.11",
6
- "core_contract": "1.25.11",
7
- "source_commit": "194e92717e58d01a532c98aad3a1830af53c9e9b",
5
+ "core_version": "1.25.12",
6
+ "core_contract": "1.25.12",
7
+ "source_commit": "5eb4ec735850cf6ebc5665a9231ef3f808303216",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-11-13T14:15:49.907Z",
9
+ "built_at": "2026-11-13T19:09:33.917Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 655,
13
- "agent_docs_stubbed": 32,
14
- "functional_verbatim": 4000,
12
+ "docs_redacted": 656,
13
+ "agent_docs_stubbed": 33,
14
+ "functional_verbatim": 4009,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 4688,
20
- "tree_sha256": "781d0de253334c2d9bbc539ee9323c04a0a79f299e00efb1f2c3e615f65d9cf8",
19
+ "file_count": 4699,
20
+ "tree_sha256": "39c06d7795f2cf9046050b08d20f5f8ada423a1efe3064d0791ba6a22fd03846",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -332,12 +332,12 @@
332
332
  {
333
333
  "path": "clients/bongos-client/README.md",
334
334
  "mode": "0000644",
335
- "sha256": "096e7b47b8ddaa1e8067b1d3d8a87d2725330e1d3b497dc51e9760eda1899bbc"
335
+ "sha256": "975f22a318be1e185d76e76fdcba623e00318e5dc2cbc257a36192a6df3066e4"
336
336
  },
337
337
  {
338
338
  "path": "clients/bongos-client/bongos-client.global.js",
339
339
  "mode": "0000644",
340
- "sha256": "0b8b0ff1235f38c838d311580acc74b08d10e48c7973dd3022d6112d0fb772f5"
340
+ "sha256": "140924d60108d727a9652a94ff8ede3dc515976b508bf07a54b2c6327a2c80bc"
341
341
  },
342
342
  {
343
343
  "path": "clients/bongos-client/examples/hello-world.mjs",
@@ -347,17 +347,17 @@
347
347
  {
348
348
  "path": "clients/bongos-client/index.cjs",
349
349
  "mode": "0000644",
350
- "sha256": "80e9d530f8a4aec8211460203f9b58aec5800d9ba42bdb2e50b909749854ba52"
350
+ "sha256": "fa491e142cfd9941d5779ca483b4032c06aed4c46365cb2c1b5d1b1ea0c73a6d"
351
351
  },
352
352
  {
353
353
  "path": "clients/bongos-client/index.d.ts",
354
354
  "mode": "0000644",
355
- "sha256": "9da9ff5395c9b56a97f5834363ef859871d4e902e14dedbc974d439e8097218d"
355
+ "sha256": "8ffb0c1faa3c5dcd128058bdc5f127333c06e916da6daaa94d4d24ea3f37edf0"
356
356
  },
357
357
  {
358
358
  "path": "clients/bongos-client/index.mjs",
359
359
  "mode": "0000644",
360
- "sha256": "b0f3cf95ef0d37ba97d7783cc0a0dfe034701e99a44a96b1aeedbba1f2ba262d"
360
+ "sha256": "99d5d3c4fe3265405d7446409d171e084432c4cf330c4476fdc3f5c93bc6056b"
361
361
  },
362
362
  {
363
363
  "path": "clients/bongos-client/package.json",
@@ -2489,20 +2489,25 @@
2489
2489
  "mode": "0000644",
2490
2490
  "sha256": "1d8b8c6782ca394e6f85072355140136eff97e1a26ace0fcf392f1206b7a77ec"
2491
2491
  },
2492
+ {
2493
+ "path": "docs/adr/0406-every-project-gets-a-sandbox-that-follows-main-and-live-moves-only-when-a-person-promotes.md",
2494
+ "mode": "0000644",
2495
+ "sha256": "aedd6b7dc8ba2955c48d44dd6d7ee24b42219b22ef034db1f1b27e23bc04f30c"
2496
+ },
2492
2497
  {
2493
2498
  "path": "docs/adr/README.md",
2494
2499
  "mode": "0000644",
2495
- "sha256": "567b8b3dcf30ff69de7c484aa84e142bccd2753c9c89fd1c1ab941c9519a0560"
2500
+ "sha256": "50221b2eb63f103558dfce9e164cf2a25ea60cfbc4171d8f23d8db72ca08be97"
2496
2501
  },
2497
2502
  {
2498
2503
  "path": "docs/api-reference.md",
2499
2504
  "mode": "0000644",
2500
- "sha256": "1a109cf49ad611b0780cc8ca885571a5e9424b38e6b33ce005d2c03dfda07b4c"
2505
+ "sha256": "1835d64e19290c34c53e25da3ef0fdafad5112ea39a89387aef859784523680c"
2501
2506
  },
2502
2507
  {
2503
2508
  "path": "docs/api/openapi.json",
2504
2509
  "mode": "0000644",
2505
- "sha256": "07123a12f24c66c7050703e3405624166c47938a936da67f48d21b11f177e702"
2510
+ "sha256": "3b07133efee8bde9714e1e3828d5d0508e030f669e432c83ca0d60000876336b"
2506
2511
  },
2507
2512
  {
2508
2513
  "path": "docs/architecture.md",
@@ -3077,7 +3082,7 @@
3077
3082
  {
3078
3083
  "path": "docs/file-map.md",
3079
3084
  "mode": "0000644",
3080
- "sha256": "2964adcfd7f172aabba831b548d2a82f6f8cdbefde3302e609362d1d45517643"
3085
+ "sha256": "4cd32a0888cf2216e43dea2ada3986d214eb6f391d7484c7ed35931d11bdd0ee"
3081
3086
  },
3082
3087
  {
3083
3088
  "path": "docs/handoff-template.md",
@@ -3087,7 +3092,7 @@
3087
3092
  {
3088
3093
  "path": "docs/module-api-changelog.md",
3089
3094
  "mode": "0000644",
3090
- "sha256": "c91c98112b041d25b65e75103120b2585ab83d864df137dd9c252499323b48f4"
3095
+ "sha256": "6b0d0e8cde4b5dcd1366775407312925bf68f1fc93fad031cec90fb12dffa46e"
3091
3096
  },
3092
3097
  {
3093
3098
  "path": "docs/modules-contract.md",
@@ -5514,6 +5519,51 @@
5514
5519
  "mode": "0000644",
5515
5520
  "sha256": "065588e708b12864390aa4a3453f648556f3c2f9547952a9b0eb69be9cf37c2f"
5516
5521
  },
5522
+ {
5523
+ "path": "modules/front-door-guide/CLAUDE.md",
5524
+ "mode": "0000644",
5525
+ "sha256": "7cb9dac9cb87b679e1ae50e4112b3a7928a38ebb9902c8947a74d17bec4d9009"
5526
+ },
5527
+ {
5528
+ "path": "modules/front-door-guide/claude.js",
5529
+ "mode": "0000644",
5530
+ "sha256": "4ef75dd80af31db062183ea3b4446358bc68d2740e111ff93a92d2640fbe5477"
5531
+ },
5532
+ {
5533
+ "path": "modules/front-door-guide/db.js",
5534
+ "mode": "0000644",
5535
+ "sha256": "2ed6652a13137fde3aa7cdc381e299e3223551f3ba27cf4b3be5bb15b5f13a5f"
5536
+ },
5537
+ {
5538
+ "path": "modules/front-door-guide/guide.js",
5539
+ "mode": "0000644",
5540
+ "sha256": "0264d44c5c026b65ff7be2be5cabec5b44dc0cec2c4f8c8dbef0ed39969d779b"
5541
+ },
5542
+ {
5543
+ "path": "modules/front-door-guide/limits.js",
5544
+ "mode": "0000644",
5545
+ "sha256": "b8819228f1246bbeabb04612f1a6722ea29e63e12a371300f45e421a596b33c7"
5546
+ },
5547
+ {
5548
+ "path": "modules/front-door-guide/migrations/front_door_guide_001_conversations.sql",
5549
+ "mode": "0000644",
5550
+ "sha256": "aad5ccdcfcdc275e5f3448d9e90c4982d5f382fb1e4d015c4709ddb4354837a7"
5551
+ },
5552
+ {
5553
+ "path": "modules/front-door-guide/module.json",
5554
+ "mode": "0000644",
5555
+ "sha256": "7f3844d43cc45d566be8200444324815f2e5a65e33789d640d9aac14dd129861"
5556
+ },
5557
+ {
5558
+ "path": "modules/front-door-guide/routes/front-door-guide.js",
5559
+ "mode": "0000644",
5560
+ "sha256": "798c216746610784077a50f24096d633a2443bfc80bdb862dc382471d0ff5913"
5561
+ },
5562
+ {
5563
+ "path": "modules/front-door-guide/settings.js",
5564
+ "mode": "0000644",
5565
+ "sha256": "dcb4af540fe925eedb9a3e28f5118fbe70c379b9b328c02a1bf026491d8b652a"
5566
+ },
5517
5567
  {
5518
5568
  "path": "modules/government/CLAUDE.md",
5519
5569
  "mode": "0000644",
@@ -10322,7 +10372,7 @@
10322
10372
  {
10323
10373
  "path": "modules/provisioning/starter-bundles.js",
10324
10374
  "mode": "0000644",
10325
- "sha256": "8e27e15ccd19497eb68186c3b4ea086859954887484a29578d75fa9565807874"
10375
+ "sha256": "7b7f866a439b873f28f6ae6667964eb98c886d02ff1ffcc51067aaf2ef5a4cbb"
10326
10376
  },
10327
10377
  {
10328
10378
  "path": "modules/provisioning/terms-ack.js",
@@ -12247,12 +12297,12 @@
12247
12297
  {
12248
12298
  "path": "package-lock.json",
12249
12299
  "mode": "0000644",
12250
- "sha256": "4e138ed0a63ae44467b6568659c5a2e210b16c90ec0c866ed74b235f31962d3c"
12300
+ "sha256": "222aede17920090083f580d3f9e9163bee43e6a268ca31638253a55f53695874"
12251
12301
  },
12252
12302
  {
12253
12303
  "path": "package.json",
12254
12304
  "mode": "0000644",
12255
- "sha256": "90fd7072761eace69417b0db93d0227b963f7c1ed6929b87bd377d47be816231"
12305
+ "sha256": "47b5416c11fe0fd7b4475f39909b9891b9f3aa0ab3490498e2191803cb1f4b99"
12256
12306
  },
12257
12307
  {
12258
12308
  "path": "public-docs/index.html",
@@ -12272,7 +12322,7 @@
12272
12322
  {
12273
12323
  "path": "release-notes.json",
12274
12324
  "mode": "0000644",
12275
- "sha256": "bec8a9ace282975f751a0806533b89abae33008ca8a26e422f1b481fd51b2c3f"
12325
+ "sha256": "7ff1f4117ef6c899824b56c4780177db1cd5979234db26b70e27d932ba58e29f"
12276
12326
  },
12277
12327
  {
12278
12328
  "path": "scripts/bongos/CLAUDE.md",
@@ -13407,7 +13457,7 @@
13407
13457
  {
13408
13458
  "path": "scripts/bongos/publish-manifest.js",
13409
13459
  "mode": "0000644",
13410
- "sha256": "7229ea4d05d216d9b4f8b410467c0ca4638e2e0ea215e45157491d57311cdd2d"
13460
+ "sha256": "d7d803f0b61423ae35a2db5d06b2a04850f0055d2d0aba38c17ad78c463892e9"
13411
13461
  },
13412
13462
  {
13413
13463
  "path": "scripts/bongos/publish-watch.js",
@@ -15927,7 +15977,7 @@
15927
15977
  {
15928
15978
  "path": "src/bongos/module-scope-map.js",
15929
15979
  "mode": "0000644",
15930
- "sha256": "7c8c4ba9e2ddcbeb3736457a00af6f5796ae900fc12d961442d3987de155af42"
15980
+ "sha256": "74b9c7908fd548adb9a718cc3c4daa8d3d0c13ac5a9d5367e726632c34dadfb1"
15931
15981
  },
15932
15982
  {
15933
15983
  "path": "src/bongos/module-store.js",
@@ -16282,7 +16332,7 @@
16282
16332
  {
16283
16333
  "path": "src/module-api.js",
16284
16334
  "mode": "0000644",
16285
- "sha256": "e9d40aa4560009177bab1b5aa23788bbf758ce884741267cd7c7fbb9fd83c2ea"
16335
+ "sha256": "32107c8946e5617f4daf8a8974065aa78a010471b7d8c1f4fdac155df7bae0e1"
16286
16336
  },
16287
16337
  {
16288
16338
  "path": "src/module-loader/catalog.js",
@@ -18279,6 +18329,11 @@
18279
18329
  "mode": "0000644",
18280
18330
  "sha256": "37e3cfc000f7ab17b04a5b145a8d209bfe911664bbb272340aabbe7152ad6c3a"
18281
18331
  },
18332
+ {
18333
+ "path": "tests/front_door_guide.mjs",
18334
+ "mode": "0000644",
18335
+ "sha256": "d4f0af7817c3e0f031fc00d8b77d2b94ddb994a39d6a03e3ba9badabae362f7a"
18336
+ },
18282
18337
  {
18283
18338
  "path": "tests/funded_project_pays_proof_db.mjs",
18284
18339
  "mode": "0000644",
@@ -20637,7 +20692,7 @@
20637
20692
  {
20638
20693
  "path": "tests/module_loader.mjs",
20639
20694
  "mode": "0000644",
20640
- "sha256": "3e5e7056784924718922c442894e6f66574fdb3aae67a4412cb7b1a128a9db92"
20695
+ "sha256": "5321c7c72df0c6901b78d6d099e30eeca3e9a7858e47c12c81edb647fa9cf515"
20641
20696
  },
20642
20697
  {
20643
20698
  "path": "tests/module_manifest.mjs",
@@ -5,7 +5,7 @@ A **generated**, zero-dependency typed client for the Bongos API — produced fr
5
5
  by hand; it regenerates when the spec changes, so it can never drift from the routes.
6
6
 
7
7
  - API version: **v1** (served at `/api/bongos/v1`)
8
- - 702 operations across 85 resource groups
8
+ - 706 operations across 86 resource groups
9
9
 
10
10
  ## Use it from your project
11
11
 
@@ -486,6 +486,16 @@ function createClient(opts = {}) {
486
486
  // POST /founding/start — rank: any-builder — POST /founding/start
487
487
  postFoundingStart: (args) => request("POST", "/founding/start", { hasBody: true }, args),
488
488
  },
489
+ "frontDoorGuide": {
490
+ // POST /front-door-guide/conversations — rank: public — POST /front-door-guide/conversations
491
+ postFrontDoorGuideConversations: (args) => request("POST", "/front-door-guide/conversations", { hasBody: true }, args),
492
+ // GET /front-door-guide/conversations/{id} — rank: public — GET /front-door-guide/conversations/:id
493
+ getFrontDoorGuideConversationsId: (args) => request("GET", "/front-door-guide/conversations/{id}", { hasBody: false }, args),
494
+ // POST /front-door-guide/conversations/{id}/messages — rank: public — POST /front-door-guide/conversations/:id/messages
495
+ postFrontDoorGuideConversationsIdMessages: (args) => request("POST", "/front-door-guide/conversations/{id}/messages", { hasBody: true }, args),
496
+ // GET /front-door-guide/status — rank: public — GET /front-door-guide/status
497
+ getFrontDoorGuideStatus: (args) => request("GET", "/front-door-guide/status", { hasBody: false }, args),
498
+ },
489
499
  "github": {
490
500
  // GET /github/orgs — rank: any-builder — GET /github/orgs
491
501
  getGithubOrgs: (args) => request("GET", "/github/orgs", { hasBody: false }, args),
@@ -485,6 +485,16 @@ function createClient(opts = {}) {
485
485
  // POST /founding/start — rank: any-builder — POST /founding/start
486
486
  postFoundingStart: (args) => request("POST", "/founding/start", { hasBody: true }, args),
487
487
  },
488
+ "frontDoorGuide": {
489
+ // POST /front-door-guide/conversations — rank: public — POST /front-door-guide/conversations
490
+ postFrontDoorGuideConversations: (args) => request("POST", "/front-door-guide/conversations", { hasBody: true }, args),
491
+ // GET /front-door-guide/conversations/{id} — rank: public — GET /front-door-guide/conversations/:id
492
+ getFrontDoorGuideConversationsId: (args) => request("GET", "/front-door-guide/conversations/{id}", { hasBody: false }, args),
493
+ // POST /front-door-guide/conversations/{id}/messages — rank: public — POST /front-door-guide/conversations/:id/messages
494
+ postFrontDoorGuideConversationsIdMessages: (args) => request("POST", "/front-door-guide/conversations/{id}/messages", { hasBody: true }, args),
495
+ // GET /front-door-guide/status — rank: public — GET /front-door-guide/status
496
+ getFrontDoorGuideStatus: (args) => request("GET", "/front-door-guide/status", { hasBody: false }, args),
497
+ },
488
498
  "github": {
489
499
  // GET /github/orgs — rank: any-builder — GET /github/orgs
490
500
  getGithubOrgs: (args) => request("GET", "/github/orgs", { hasBody: false }, args),
@@ -75,6 +75,7 @@ export interface GetCoreUpgradesResponse { ok: boolean; running: unknown; limit:
75
75
  export interface GetDiscordChannelsPlanResponse { ok: boolean; guild_id: unknown; op_count: unknown; ops: unknown; warnings: unknown }
76
76
  export interface GetDiscordChannelsSnapshotResponse { ok: boolean; guild_id: unknown; config: unknown; warnings: unknown }
77
77
  export interface GetDoneWhenPendingReviewResponse { criteria: unknown; count: unknown }
78
+ export interface GetFrontDoorGuideConversationsIdResponse { ok: boolean; conversation: unknown; transcript: unknown }
78
79
  export interface GetGithubOrgsOrgResponse { org: unknown }
79
80
  export interface GetGithubRepoResponse { ok: boolean; repo: unknown; private_deployable: boolean }
80
81
  export interface GetGithubReposResponse { repos: unknown }
@@ -389,6 +390,8 @@ export interface PostDoneWhenCriterionIdUnsatisfyResponse { criterion: unknown }
389
390
  export interface PostFeedbackReceiptRequest { sha256: string; kind?: "bug" | "idea" }
390
391
  export interface PostFeedbackReceiptResponse { ok: boolean; receipt: unknown; hub: unknown }
391
392
  export interface PostFoundingPlanRequest { days?: Record<string, unknown>; passes?: number; advisors?: string; government?: string; expected_builders?: number; existing?: boolean }
393
+ export interface PostFrontDoorGuideConversationsIdMessagesRequest { token: string; text: string; website?: string }
394
+ export interface PostFrontDoorGuideConversationsRequest { website?: string }
392
395
  export interface PostGithubRepoInviteRequest { github_login: string; permission?: string }
393
396
  export interface PostGithubRepoVisibilityRequest { repo: string; visibility: "public" | "private" }
394
397
  export interface PostGithubRepoVisibilityResponse { ok: boolean; repo: unknown; private_deployable: boolean }
@@ -1158,6 +1161,16 @@ export interface BongosClient {
1158
1161
  /** POST /founding/start — rank: any-builder */
1159
1162
  postFoundingStart(args?: RequestArgs): Promise<ApiResponse>;
1160
1163
  };
1164
+ "frontDoorGuide": {
1165
+ /** POST /front-door-guide/conversations — rank: public */
1166
+ postFrontDoorGuideConversations(args?: RequestArgs & { body?: PostFrontDoorGuideConversationsRequest }): Promise<ApiResponse>;
1167
+ /** GET /front-door-guide/conversations/{id} — rank: public */
1168
+ getFrontDoorGuideConversationsId(args?: RequestArgs): Promise<GetFrontDoorGuideConversationsIdResponse>;
1169
+ /** POST /front-door-guide/conversations/{id}/messages — rank: public */
1170
+ postFrontDoorGuideConversationsIdMessages(args: RequestArgs & { body: PostFrontDoorGuideConversationsIdMessagesRequest }): Promise<ApiResponse>;
1171
+ /** GET /front-door-guide/status — rank: public */
1172
+ getFrontDoorGuideStatus(args?: RequestArgs): Promise<ApiResponse>;
1173
+ };
1161
1174
  "github": {
1162
1175
  /** GET /github/orgs — rank: any-builder */
1163
1176
  getGithubOrgs(args?: RequestArgs): Promise<ApiResponse>;
@@ -482,6 +482,16 @@ export function createClient(opts = {}) {
482
482
  // POST /founding/start — rank: any-builder — POST /founding/start
483
483
  postFoundingStart: (args) => request("POST", "/founding/start", { hasBody: true }, args),
484
484
  },
485
+ "frontDoorGuide": {
486
+ // POST /front-door-guide/conversations — rank: public — POST /front-door-guide/conversations
487
+ postFrontDoorGuideConversations: (args) => request("POST", "/front-door-guide/conversations", { hasBody: true }, args),
488
+ // GET /front-door-guide/conversations/{id} — rank: public — GET /front-door-guide/conversations/:id
489
+ getFrontDoorGuideConversationsId: (args) => request("GET", "/front-door-guide/conversations/{id}", { hasBody: false }, args),
490
+ // POST /front-door-guide/conversations/{id}/messages — rank: public — POST /front-door-guide/conversations/:id/messages
491
+ postFrontDoorGuideConversationsIdMessages: (args) => request("POST", "/front-door-guide/conversations/{id}/messages", { hasBody: true }, args),
492
+ // GET /front-door-guide/status — rank: public — GET /front-door-guide/status
493
+ getFrontDoorGuideStatus: (args) => request("GET", "/front-door-guide/status", { hasBody: false }, args),
494
+ },
485
495
  "github": {
486
496
  // GET /github/orgs — rank: any-builder — GET /github/orgs
487
497
  getGithubOrgs: (args) => request("GET", "/github/orgs", { hasBody: false }, args),
@@ -0,0 +1,108 @@
1
+ # 0406 — Every project gets a sandbox that follows main, and live moves only when a person promotes
2
+
3
+ - **Status:** Accepted. The model is settled; §6 lists two cost choices the gated tasks ask the owner when they reach them, and neither changes the model.
4
+ - **Date:** 2026-11-13
5
+ - **Task:** [task 1005288](https://cloudbongos.com/builders#/task/1005288) (goal 1000106 — working area 1, Project creation)
6
+ - **Decided by:** the owner's direction of 2026-11-13 (quoted in §1), with the shape checked against the code and Render's own docs by Claude, who wrote the record.
7
+ - **Builds on:** [ADR 0345](0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md) (we host the hall; the app runs on the owner's Render account) · [ADR 0327](0327-a-cloud-host-runs-on-the-owners-account-and-the-key-is-borrowed.md) (the Render key is borrowed, never stored) · [ADR 0161](0161-publish-on-merge.md) (for Cloud Bongos itself a merge is not live until a pin moves) · [ADR 0369](0369-ship-mode-decides-who-starts-a-ship-not-how-it-lands.md) (ship mode says who starts a ship, not where it lands)
8
+ - **Distinct from:** [ADR 0044](0044-per-box-live-game-preview.md) / [ADR 0046](0046-sandbox-first-review-gate.md) — the builder's private **local preview** (`scripts/bongos/sandbox-stage.js`, `scripts/bongos/local-preview.js`). That is one builder's look *before* shipping, on their own machine. This ADR is about the project's shared, hosted sandbox *after* work lands.
9
+ - **Gates:** [task 1005289](https://cloudbongos.com/builders#/task/1005289) (set it up) · [task 1005290](https://cloudbongos.com/builders#/task/1005290) (land on the sandbox, promote to live) · [task 1005291](https://cloudbongos.com/builders#/task/1005291) (safety) · [task 1005292](https://cloudbongos.com/builders#/task/1005292) (Cloud Bongos's own) · [task 1005293](https://cloudbongos.com/builders#/task/1005293) (the walk)
10
+
11
+ ## 1. The owner's words
12
+
13
+ > "each project would have a sandbox, so yes cloudbongos would have one, but so would every site someone creates. maybe its a second github or just a different branch and we host it on the render free version... essentially a local host but has fake data or sandbox keys that dont actually change anything. maybe it uses the real keys for the data but is gated to not actually send anything back."
14
+
15
+ ## 2. The decision
16
+
17
+ Every project whose app runs on Render has **two apps from one repository**:
18
+
19
+ | | **Sandbox** | **Live (the main site)** |
20
+ |---|---|---|
21
+ | What it runs | whatever has landed on `main`, automatically | the commit a person last promoted |
22
+ | Render service | a second web service on the owner's account, **free plan** | the owner's existing service, on the plan they chose |
23
+ | Updates when | every merge (Render auto-deploy from `main`) | only when a person presses **promote** (auto-deploy switched off) |
24
+ | Data | its own database with seeded fake data (§4) | the real data |
25
+ | Keys | each provider's sandbox/test keys; real keys only when lent, behind the gate (§5) | the real keys |
26
+ | Marked | every page carries a visible "sandbox" band | — |
27
+
28
+ **Promotion is a person's act.** The owner (or whoever the project's rules allow) sees which landed commits are on the sandbox and not on live, and presses promote. That asks Render to deploy that exact commit to the live service. It needs the owner's Render key typed in for that one request, as every Render action already does (`modules/render-deploy/render.js`, ADR 0327 §2) — so no stored credential can ever move live by itself. This is the project law *"Land only. Ask before going live"* made physical: agents land, and landing reaches only the sandbox.
29
+
30
+ **Ship mode is untouched.** Auto mode still ships hands-off and Manual still asks before each ship (ADR 0369). Both only ever land, and landing now reaches the sandbox; neither can promote.
31
+
32
+ ## 3. Why "the sandbox follows main" and not a sandbox branch
33
+
34
+ The starting point offered a `sandbox` branch, promoted by merging it into `main`. Checked against the code, that is the costlier shape:
35
+
36
+ - **Everything already lands on `main`.** `ship.js`, the grader, the merge queue, the strand fixer and the release pipeline all key on `main`. A sandbox branch means every one of them learns a second target, and every promotion is a merge that can conflict.
37
+ - **Render can already deploy a chosen commit.** A service set to `autoDeploy: no` is deployed by an explicit request that names the commit. The client has `triggerDeploy`, `listDeploys` and `rollbackTo` today (`scripts/bongos/render-api.js`); promotion adds a commit id to the first.
38
+ - **It is the model Cloud Bongos already runs.** For the platform, a merge is not live until a published release carries it and the hub's pin moves (ADR 0161, [`docs/recipes/core-release-pipeline.md`](../recipes/core-release-pipeline.md)). "The sandbox follows what landed; live holds a pin a person moves" is the same rule, so the platform and every project now share one model (§7).
39
+
40
+ So: **one repository, no second repository, no sandbox branch.** The owner's "maybe a second github" is declined for the reason the brief gave — two copies drift and double the upkeep.
41
+
42
+ What changes in code (for [task 1005289](https://cloudbongos.com/builders#/task/1005289) and [task 1005290](https://cloudbongos.com/builders#/task/1005290)): today the one service is created with `branch: 'main'` and `autoDeploy: 'yes'` (`scripts/bongos/render-payload.js`), so every merge already goes straight to the main site. Under this ADR the sandbox service takes that payload, and the live service is switched to `autoDeploy: 'no'`.
43
+
44
+ ## 4. The sandbox's database
45
+
46
+ Render's free database is **not** usable here (Render docs, read 2026-11-13: [render.com/docs/free](https://render.com/docs/free)):
47
+
48
+ - it **expires 30 days after creation**, with 14 days' grace before Render deletes it;
49
+ - a workspace may have **only one** free database;
50
+ - it has no backups.
51
+
52
+ The platform already refuses it for the same reasons (`modules/provisioning/render-standup.js`: "The database has no free plan here"). So:
53
+
54
+ - **The app has a paid database** → the sandbox gets **a second database inside that same database server**, filled with seeded fake data. No extra Render bill; it shares the server's capacity. ([task 1005289](https://cloudbongos.com/builders#/task/1005289) confirms on a real account that Render allows a second database in one server; if it does not, the fallback is the owner's choice in §6.)
55
+ - **The app has no database** → the sandbox has none either.
56
+ - The sandbox **never** points at the live database. Its writes would be real writes.
57
+
58
+ ## 5. Safety: fake by default, and a gate when real keys are lent
59
+
60
+ 1. **Default:** seeded fake data and each provider's sandbox/test keys (Stripe test mode, and so on). Nothing the sandbox does can reach a real customer.
61
+ 2. **Real keys, only when the owner lends them** (so the sandbox can show real data), and then:
62
+ - **Prefer a read-only key.** Where the provider offers a restricted or read-only key, that is the key to lend. This is the only part of the gate that cannot be coded around, so it is the floor.
63
+ - **The outbound gate.** The project's app runs with `BONGOS_SANDBOX=1`, and the project's starter carries a small outbound wrapper that, for each known provider, lets reads through and refuses sends, charges and writes, logging each refusal where the owner can see it.
64
+ - **Said plainly:** the app is the owner's own code. The gate covers calls that go through the wrapper; code written to bypass it is not caught. That is why a read-only key comes first.
65
+ 3. A side effect worth knowing: Render's free plan already blocks outgoing mail ports (25, 465, 587), so a sandbox on the free plan cannot send email over SMTP at all.
66
+
67
+ ## 6. Cost, and what the owner chooses
68
+
69
+ **Cost to the owner** (Render's docs, read 2026-11-13):
70
+
71
+ - **Sandbox app: $0.** Free plan: 0.1 CPU, 512 MB memory. It **sleeps after 15 minutes with no visitors and takes about a minute to wake**, which is fine for a place to look at work before it goes live. Free apps share **750 running hours a month per Render workspace**; a sleeping app uses none. Past that, every free app on the workspace pauses until the next month. Local files are lost on every sleep and restart (no disks), which is why the sandbox's data lives in the database.
72
+ - **Sandbox database: $0** when it sits inside the app's existing paid database (§4).
73
+ - Free apps count against the workspace's shared bandwidth and build minutes; a busy sandbox could use some of them up.
74
+
75
+ **Cost to us: nothing in hosting** — the sandbox runs on the owner's account (ADR 0327, ADR 0345). Our cost is the runner's time setting it up and the work in the sibling tasks.
76
+
77
+ 🔀 **Owner choices still open** (not blocking this ADR; each is asked in the task that needs it):
78
+
79
+ 1. **If Render will not hold a second database in one server**, the sandbox's database is either (a) the cheapest paid Render database, a few dollars a month on the owner's bill, *(recommended)*, or (b) none — the sandbox shows the app with no data. Asked in [task 1005289](https://cloudbongos.com/builders#/task/1005289).
80
+ 2. **Where Cloud Bongos's own sandbox runs** (§7): (a) on the box the hub already uses — no new spend, but it takes memory from the box every project hall shares — or (b) a small separate server, a few dollars a month. Asked in [task 1005292](https://cloudbongos.com/builders#/task/1005292). Recommended: (b), because the shared box is already near its memory wall ([task 1003875](https://cloudbongos.com/builders#/task/1003875)).
81
+
82
+ ## 7. Cases
83
+
84
+ - **A project with no app yet** gets no hosted sandbox. There is nothing to run. Its builders keep the private local preview, and the sandbox is set up the moment the app is connected to Render, at creation or later from settings ([task 1005289](https://cloudbongos.com/builders#/task/1005289)).
85
+ - **An app that deploys somewhere other than Render** (ADR 0345 lets the owner choose) gets no automatic sandbox. The model is the same, but we only set it up on Render.
86
+ - **An app linked to an existing Render service** ("adopt") keeps that service as live; the sandbox is a new free service copying its build settings.
87
+ - **Tearing a project down** removes the sandbox service and the sandbox database only — never the owner's live service (ADR 0327 §3: we never destroy an owner's infrastructure without being asked).
88
+ - **Cloud Bongos itself** runs the same rule one level up. Its "main" is the published core release (ADR 0161). Its sandbox is a second Cloud Bongos instance that takes **every** release as it is published and carries a throwaway test project ([`modules/provisioning/test-project.js`](../../modules/provisioning/test-project.js)), so a full project-creation walk costs nothing real. The live hub moves its pin only when a person promotes. Today the hub's patch subscription moves its pin to each patch release by itself (task 1005020); once the sandbox exists, whether patches still go live hands-off is the owner's call in [task 1005292](https://cloudbongos.com/builders#/task/1005292).
89
+
90
+ ## 8. Considered and not done
91
+
92
+ - **A second repository.** Doubles upkeep and drifts.
93
+ - **A `sandbox` branch, promoted by merge.** Every landing tool would need a second target, and promotions could conflict (§3).
94
+ - **Render's free database for the sandbox.** Expires in 30 days, one per workspace (§4).
95
+ - **The sandbox reading the live database.** Any write would be real.
96
+ - **An outbound gate with no read-only key behind it, sold as "safe".** It only covers code that goes through it; we say so instead (§5).
97
+
98
+ ## 9. The sibling tasks, re-read against this record (2026-11-13)
99
+
100
+ Each sibling task's description now opens with a dated note, *"Re-read against ADR 0406 … What the decision changes here"*, written through `PATCH /api/bongos/tasks/<id>`; the owner's original words are kept below it.
101
+
102
+ | Task | What changed |
103
+ |---|---|
104
+ | [task 1005289](https://cloudbongos.com/builders#/task/1005289) | Retitled *"Give every project a sandbox: a free Render app that follows main, at creation or later"* (the sandbox branch is gone). Note: free service auto-deploys from `main`, live switched to `autoDeploy: 'no'`; never Render's free database, a second database in the paid server instead (confirm first, else owner choice 1); adopt, no-app and teardown cases. |
105
+ | [task 1005290](https://cloudbongos.com/builders#/task/1005290) | Landing stays on `main`; promote deploys one chosen commit to live, `triggerDeploy` gains a commit id, the owner types the Render key for that request, each promotion is recorded; ship mode untouched. |
106
+ | [task 1005291](https://cloudbongos.com/builders#/task/1005291) | A read-only key is the floor; the gate is an outbound wrapper in the starter active under `BONGOS_SANDBOX=1`, with its limit said plainly; free plan blocks mail ports. |
107
+ | [task 1005292](https://cloudbongos.com/builders#/task/1005292) | Same rule over the release pin; a second instance takes every release and carries a throwaway test project; owner choice 2 and the patch auto-move question are asked here. |
108
+ | [task 1005293](https://cloudbongos.com/builders#/task/1005293) | Added checks: no live deploy after a ship, promote refuses without the key, the sandbox band shows, sandbox data is fake, and the lent key is confirmed read-only. |
@@ -521,3 +521,4 @@ These 20 numbers are each shared by exactly two files. They are **accepted histo
521
521
  | 0403 | [**In game mode the visor is the control and the scene is the view** ([task 1005203](https://cloudbongos.com/builders#/task/1005203), goal 1000142; owner 2026-11-11). Every main call to action you can see in game mode lives on the visor (the accent button, the key hints, the Actions sheet); the city, the sky and Home only show the project and can be moved through, never carry buttons of their own. A scene offers its actions to the visor through `OTBVisor.scene`. Found not yet keeping it: the city's decide button and door, the sky's top row and composer, Home's gallery links, the Artist's feedback tab.](0403-in-game-mode-the-visor-is-the-control-and-the-scene-is-the-view.md) | hall / game frame |
522
522
  | 0404 | [**A party is a room, and rooms register as kinds** ([task 1005220](https://cloudbongos.com/builders#/task/1005220), goal 1000148). Any group that talks together is a room: a free-form party (cap 8), a chain party (ADR 0400), a scoping session (cap 40, owned by `lifecycle`) or a decision room (cap 40, `government`). Each owner contributes its kind to the `room.kinds` contribution point (a port is single-provider, so four owners would fail the boot) with `members`/`isMember`/optional `invite`/`accept`, and fires `room.opened`/`room.member.joined`/`room.member.left`/`room.closed`; consumers never read an owner's tables and reconcile against `members()`. Free-form party: one per builder, leader handover to the longest-standing member, disband on last leave. A chain room follows an open `chain_party` row. Unlinked players get everything but voice. V3 polls (3 s, cursor + 304, paused when hidden, per-builder limiter) rather than pushing through `/live`. With discord off, no voice key or voice state; chat still works. A room is never authority. Rejected: one port, a port per kind, a rooms module reading other modules' tables, a second party roster, push via `/live`, speaking indicators.](0404-a-party-is-a-room-and-rooms-register-as-kinds.md) | game frame / modules / Discord |
523
523
  | 0405 | [**Each character has one colour, and the visor wears it** ([task 1005218](https://cloudbongos.com/builders#/task/1005218), goal 1000142; owner 2026-11-12). The Artisan is ember (the accent), the Artist violet, the Patron gold, each mixed from the hall’s tokens and none typed in. In game mode the visor’s lines, name, picture rim and accent are the held character’s colour, and your picture on the glass is your character wearing the visor. The characters are redrawn at 64 by 64 as two-animal aliens, drawn by a script.](0405-each-character-has-one-colour-and-the-visor-wears-it.md) | hall / game frame |
524
+ | 0406 | [**Every project gets a sandbox that follows main, and live moves only when a person promotes** ([task 1005288](https://cloudbongos.com/builders#/task/1005288), goal 1000106; owner 2026-11-13). One repository, no sandbox branch: a second Render web service on the free plan auto-deploys every landing on `main`, and the live service stops auto-deploying and takes only the commit a person promotes (with the Render key typed in for that request). Fake data and test keys by default; lent real keys should be read-only, behind an outbound gate that refuses sends, charges and writes. Render's free database expires in 30 days, so the sandbox gets a second database in the app's paid one. A project with no app gets no hosted sandbox; Cloud Bongos runs the same rule over its release pin.](0406-every-project-gets-a-sandbox-that-follows-main-and-live-moves-only-when-a-person-promotes.md) | provisioning / hosting |