@bongos/core 1.19.1075 → 1.19.1077

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 (87) hide show
  1. package/.bongos-core.json +104 -114
  2. package/.claude/skills/blocker-solve/SKILL.md +1 -1
  3. package/README.md +1 -1
  4. package/clients/bongos-client/README.md +1 -1
  5. package/clients/bongos-client/bongos-client.global.js +2 -0
  6. package/clients/bongos-client/index.cjs +2 -0
  7. package/clients/bongos-client/index.d.ts +2 -0
  8. package/clients/bongos-client/index.mjs +2 -0
  9. package/docs/adr/0031-cloud-dev-environments-for-builders.md +1 -1
  10. package/docs/adr/0035-builder-onboarding-three-paths.md +1 -1
  11. package/docs/adr/0044-per-box-live-game-preview.md +1 -1
  12. package/docs/adr/0045-devbox-desktop-app.md +1 -1
  13. package/docs/adr/0046-sandbox-first-review-gate.md +1 -1
  14. package/docs/adr/0052-sandbox-for-everyone-game-only-preview.md +1 -1
  15. package/docs/adr/0053-scoped-dev-box-session.md +1 -1
  16. package/docs/adr/0055-server-mediated-branch-publish.md +1 -1
  17. package/docs/adr/0057-container-cost-ledger.md +1 -1
  18. package/docs/adr/0059-single-approval-remove-devbox-approval-gate.md +1 -1
  19. package/docs/adr/0071-box-confirm-before-destroyed-and-drift-reconcile.md +1 -1
  20. package/docs/adr/0072-bongos-app-mac-signed-first-windows-deferred.md +1 -1
  21. package/docs/adr/0072-dev-box-code-staleness-visibility.md +1 -1
  22. package/docs/adr/0104-trust-gds-api-channel-in-auto-mode.md +1 -1
  23. package/docs/adr/0123-box-idle-sweep-autosave-before-destroy.md +1 -1
  24. package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +1 -1
  25. package/docs/adr/0145-devbox-app-branding-driven-module.md +1 -1
  26. package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +7 -0
  27. package/docs/adr/0148-task-scoped-box-source-access.md +2 -2
  28. package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +1 -1
  29. package/docs/adr/0193-pause-task-scoped-box-slices.md +1 -1
  30. package/docs/adr/0277-a-box-is-in-use-only-while-a-human-is-attached.md +1 -1
  31. package/docs/adr/0346-dev-boxes-are-retired.md +69 -0
  32. package/docs/adr/README.md +1 -0
  33. package/docs/api/openapi.json +22 -1
  34. package/docs/api-reference.md +3 -2
  35. package/docs/architecture.md +5 -6
  36. package/docs/branding-contract.md +2 -2
  37. package/docs/canonical-permissions.md +10 -18
  38. package/docs/copy-inventory.md +3 -3
  39. package/docs/copy-registry.json +3 -3
  40. package/docs/design/gate-navigation-direction.md +1 -1
  41. package/docs/design/hall-direction-v2.md +3 -3
  42. package/docs/design/modular-architecture/00-research-report.md +1 -1
  43. package/docs/design/modular-architecture/01-architecture.md +3 -3
  44. package/docs/design/modular-architecture/02-module-map.md +1 -2
  45. package/docs/design/modular-architecture/03-builder-flows.md +22 -20
  46. package/docs/design/modular-architecture/04-module-lifecycle.md +1 -1
  47. package/docs/design/modular-architecture/README.md +4 -2
  48. package/docs/design/reviews/hall-v2/README.md +2 -2
  49. package/docs/design/vanilla-hall-ui-redesign-scope.md +4 -4
  50. package/docs/file-map.md +5 -5
  51. package/docs/handoff-template.md +1 -1
  52. package/docs/module-api-changelog.md +8 -0
  53. package/docs/modules-contract.md +11 -14
  54. package/docs/page-readings.json +63 -63
  55. package/docs/recipes/bongos-cli-release.md +1 -2
  56. package/docs/recipes/multi-builder-merge.md +1 -1
  57. package/docs/recipes/ops-gotchas.md +0 -9
  58. package/docs/recipes/self-host.md +1 -1
  59. package/docs/recipes/windows-builders.md +1 -1
  60. package/migrations/core_258_drop_dev_box_tables.sql +3 -0
  61. package/modules/hall-ui/public/palette.js +143 -3
  62. package/package-lock.json +2 -2
  63. package/package.json +1 -1
  64. package/release-notes.json +28 -0
  65. package/scripts/gds/dev-box-guard.js +165 -0
  66. package/scripts/gds/fitness-lib.js +10 -0
  67. package/scripts/gds/fitness.js +2 -10
  68. package/scripts/gds/run-unit-tests.js +4 -0
  69. package/src/bongos/auth.js +8 -1
  70. package/src/bongos/middleware/rate-limit.js +1 -1
  71. package/src/bongos/module-scope-map.js +1 -1
  72. package/src/bongos/routes/auth.js +271 -73
  73. package/src/module-api.js +1 -1
  74. package/tests/core_258_box_session_claims_db.mjs +180 -0
  75. package/tests/fitness.mjs +89 -0
  76. package/tests/hall_palette.mjs +224 -11
  77. package/tests/hub_cookie_host_only.mjs +279 -0
  78. package/tests/landing_page.mjs +4 -1
  79. package/tests/provisioning_recommendations.mjs +1 -1
  80. package/tests/session_rename_fallback.mjs +19 -11
  81. package/tests/subdomain_auth.mjs +17 -15
  82. package/docs/design/reviews/hall-v2/harbor--archon.webp +0 -0
  83. package/docs/design/reviews/hall-v2/harbor--metic.webp +0 -0
  84. package/docs/design/reviews/hall-v2/harbor--xenos.webp +0 -0
  85. package/docs/design/reviews/hall-v2/pair--archon.webp +0 -0
  86. package/docs/design/reviews/hall-v2/pair--metic.webp +0 -0
  87. package/docs/design/reviews/hall-v2/pair--xenos.webp +0 -0
@@ -92,6 +92,6 @@ Lars is the prompter (CLAUDE.md §2). Frame around the outcome, not the plumbing
92
92
 
93
93
  This skill is the **reusable core** of the blocker-solve idea. Future entry points call into this same flow rather than reinventing it:
94
94
  - a hall **"Solve this"** button on the Archon blocker card (records an intent), and
95
- - a **cloud / autonomous runner** that spins the session up server-side (mirroring the `box_intents` control-plane pattern, [ADR 0031](../../../docs/adr/0031-cloud-dev-environments-for-builders.md)), pinging the owner only for the **[needs you]** steps.
95
+ - a **cloud / autonomous runner** that spins the session up server-side (mirroring the `provisioning_intents` control-plane request queue), pinging the owner only for the **[needs you]** steps.
96
96
 
97
97
  Both reuse the gather → do/guide → verify → auto-resolve loop above. (Filed under GDS-V4; see the task that shipped this skill.)
package/README.md CHANGED
@@ -11,7 +11,7 @@ The durable framing for AI agents — methodology, working rules, session protoc
11
11
  ## What's in here
12
12
 
13
13
  - **The GDS** (`src/`, `modules/`, `scripts/gds/`) — the build state machine: tasks, claims, versions, goals, dependencies, the claim→ship→grade→credit lifecycle, and the `/builder-*` CLI.
14
- - **Platform modules** (`modules/`) — lifecycle, grading, economy, memory, onboarding, ideas, sessions, autonomy, security, the builders' hall + status dashboard, dev-box, and Discord integration. Each is configurable (and most are toggleable) per instance.
14
+ - **Platform modules** (`modules/`) — lifecycle, grading, economy, memory, onboarding, ideas, sessions, autonomy, security, the builders' hall + status dashboard, and Discord integration. Each is configurable (and most are toggleable) per instance.
15
15
  - **Neutral branding template** (`config/*.neutral.json`, `docs/project-context.template.md`) — the identity scaffold a new instance fills in ([`docs/branding-contract.md`](./docs/branding-contract.md)). The vanilla pack in `config/branding.neutral.json` is the identity Cloud Bongos itself runs on.
16
16
  - **Methodology + decisions** (`CLAUDE.md`, `docs/adr/`, `docs/recipes/`, `.claude/skills/`).
17
17
 
@@ -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
- - 433 operations across 67 resource groups
8
+ - 434 operations across 67 resource groups
9
9
 
10
10
  ## Use it from your project
11
11
 
@@ -163,6 +163,8 @@ function createClient(opts = {}) {
163
163
  getAuthWebAdmissionStatus: (args) => request("GET", "/auth/web/admission-status", { hasBody: false }, args),
164
164
  // GET /auth/web/callback — rank: public — GET /auth/web/callback
165
165
  getAuthWebCallback: (args) => request("GET", "/auth/web/callback", { hasBody: false }, args),
166
+ // GET /auth/web/handoff — rank: public — GET /auth/web/handoff
167
+ getAuthWebHandoff: (args) => request("GET", "/auth/web/handoff", { hasBody: false }, args),
166
168
  // GET /auth/web/start — rank: public — GET /auth/web/start
167
169
  getAuthWebStart: (args) => request("GET", "/auth/web/start", { hasBody: false }, args),
168
170
  },
@@ -162,6 +162,8 @@ function createClient(opts = {}) {
162
162
  getAuthWebAdmissionStatus: (args) => request("GET", "/auth/web/admission-status", { hasBody: false }, args),
163
163
  // GET /auth/web/callback — rank: public — GET /auth/web/callback
164
164
  getAuthWebCallback: (args) => request("GET", "/auth/web/callback", { hasBody: false }, args),
165
+ // GET /auth/web/handoff — rank: public — GET /auth/web/handoff
166
+ getAuthWebHandoff: (args) => request("GET", "/auth/web/handoff", { hasBody: false }, args),
165
167
  // GET /auth/web/start — rank: public — GET /auth/web/start
166
168
  getAuthWebStart: (args) => request("GET", "/auth/web/start", { hasBody: false }, args),
167
169
  },
@@ -562,6 +562,8 @@ export interface BongosClient {
562
562
  getAuthWebAdmissionStatus(args?: RequestArgs): Promise<GetAuthWebAdmissionStatusResponse>;
563
563
  /** GET /auth/web/callback — rank: public */
564
564
  getAuthWebCallback(args?: RequestArgs): Promise<ApiResponse>;
565
+ /** GET /auth/web/handoff — rank: public */
566
+ getAuthWebHandoff(args?: RequestArgs): Promise<ApiResponse>;
565
567
  /** GET /auth/web/start — rank: public */
566
568
  getAuthWebStart(args?: RequestArgs): Promise<ApiResponse>;
567
569
  };
@@ -159,6 +159,8 @@ export function createClient(opts = {}) {
159
159
  getAuthWebAdmissionStatus: (args) => request("GET", "/auth/web/admission-status", { hasBody: false }, args),
160
160
  // GET /auth/web/callback — rank: public — GET /auth/web/callback
161
161
  getAuthWebCallback: (args) => request("GET", "/auth/web/callback", { hasBody: false }, args),
162
+ // GET /auth/web/handoff — rank: public — GET /auth/web/handoff
163
+ getAuthWebHandoff: (args) => request("GET", "/auth/web/handoff", { hasBody: false }, args),
162
164
  // GET /auth/web/start — rank: public — GET /auth/web/start
163
165
  getAuthWebStart: (args) => request("GET", "/auth/web/start", { hasBody: false }, args),
164
166
  },
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-04
4
4
  **Context:** GDS-V3, task [#595](https://example.com/builders#/task/595) (claim `#477`). Feeds done-when criteria **C1** (`onboarding ≤ 2h`) and **C8** (`cloneable repo / local-first memory, server-canonical on ship`). Companion to [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (trust boundary — same server-enforced posture), [ADR 0018](0018-three-rank-model-goes-live.md) (Xenos → Metic → Archon), [ADR 0022](0022-secrets-policy.md) (no secrets in repo — precondition for a cloneable box), [ADR 0024](0024-cloneable-repo-local-first-memory.md) (memory follows the builder — unchanged by where the box runs), and [ADR 0002](0002-digitalocean-over-hetzner.md) (we already run on DigitalOcean).
5
- **Status:** Accepted. *Decision recorded this session; implementation tasks to be seeded separately (see the Implementation sub-tasks at the end).*
5
+ **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted. *Decision recorded this session; implementation tasks to be seeded separately (see the Implementation sub-tasks at the end).*
6
6
  **Track:** `internal` (dev-system architecture).
7
7
 
8
8
  > This is a decision record, not an implementation. Nothing here is built yet. The point is to lock the shape so a future session can build it without relitigating the design.
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-06
4
4
  **Context:** GDS-V3, task [#715](https://example.com/builders#/task/715). Feeds done-when criterion **C1** (`a Metic onboards in ≤ 2h`). This ADR is the *onboarding-as-experience* companion to [ADR 0031](0031-cloud-dev-environments-for-builders.md), which locked the **substrate** (the containerized DO box, auto-suspend, rank-gated source access) but never consolidated how a person actually *arrives*. Builds on [ADR 0034](0034-thetes-graduated-newcomer-rank.md) (the `xenos < thetes < metic < archon` ladder), [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (server-enforced permissions), [ADR 0022](0022-secrets-policy.md) (no secrets in repo), and the box onboarding middleware shipped in [#701](https://example.com/builders#/task/701) (§9.4 of ADR 0031).
5
- **Status:** Accepted — §2 (the first-box Archon approval gate) superseded by [ADR 0059](0059-single-approval-remove-devbox-approval-gate.md). *Decision record — locks the onboarding shape and three new policy decisions. The box-lane machinery already exists (ADR 0031 + [#701](https://example.com/builders#/task/701)); the deltas this ADR introduces (the Metic+ local gate, the first-box approval gate, the single hosted guide surface, the primer rewrite) are implementation sub-tasks listed at the end and are NOT yet built.*
5
+ **Status:** Superseded in part by [ADR 0346](0346-dev-boxes-are-retired.md) (the DO-box setup path). Was: Accepted — §2 (the first-box Archon approval gate) superseded by [ADR 0059](0059-single-approval-remove-devbox-approval-gate.md). *Decision record — locks the onboarding shape and three new policy decisions. The box-lane machinery already exists (ADR 0031 + [#701](https://example.com/builders#/task/701)); the deltas this ADR introduces (the Metic+ local gate, the first-box approval gate, the single hosted guide surface, the primer rewrite) are implementation sub-tasks listed at the end and are NOT yet built.*
6
6
  **Track:** `internal` (dev-system / builder onboarding).
7
7
 
8
8
  > **⚠️ Superseded in part by [ADR 0059](0059-single-approval-remove-devbox-approval-gate.md) (task [#1141](https://example.com/builders#/task/1141)).** Decision **§2 — "A newcomer's FIRST box requires Archon approval"** is **removed**: with admission now invite-gated ([ADR 0050](0050-device-flow-admission-invite-gated-by-default.md)), member-join is the sole human gate, and an admitted builder's first box now provisions hands-off. §1 (Metic+ local clone) and §3 (the single hosted guide) stand unchanged. Read §2 below as historical.
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-10
4
4
  **Context:** GDS-V3, task [#897](https://example.com/builders#/task/897) (this ADR + the tunnel mechanism), wired into provisioning under [#898](https://example.com/builders#/task/898), hardware-verified under [#899](https://example.com/builders#/task/899). Companion to [ADR 0031](0031-cloud-dev-environments-for-builders.md) (per-builder DigitalOcean box substrate), [ADR 0038](0038-chromebook-ttyd-cloudflare-tunnel.md) (the box's ttyd terminal over a Cloudflare Tunnel — the mechanism this extends), and [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (the web tier only *asks*; the control plane holds infra power).
5
- **Status:** Accepted. Mechanism implemented under [#897](https://example.com/builders#/task/897).
5
+ **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted. Mechanism implemented under [#897](https://example.com/builders#/task/897).
6
6
  **Track:** `internal` (dev-system / builder experience).
7
7
 
8
8
  ## Problem
@@ -1,6 +1,6 @@
1
1
  # ADR 0045 — The Dev Box desktop app: a native switch, app-pairing auth, and off-repo distribution
2
2
 
3
- - **Status:** accepted
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: accepted
4
4
  - **Date:** 2026-06-10
5
5
  - **Task:** [#904](https://example.com/builders#/task/904)
6
6
  - **Track:** internal
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-11
4
4
  **Context:** GDS-V3, task [#927](https://example.com/builders#/task/927). Builds directly on [ADR 0044](0044-per-box-live-game-preview.md) (the per-box live game preview / "sandbox") and the three-state ship lifecycle in `scripts/gds/ship.js` (Phase 5). Companion to [ADR 0031](0031-cloud-dev-environments-for-builders.md) (dev boxes) and [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (the gate is a workflow aid, not an authority surface).
5
- **Status:** Accepted.
5
+ **Status:** Superseded in part by [ADR 0346](0346-dev-boxes-are-retired.md) (the dev-box half; the local sandbox review survives). Was: Accepted.
6
6
  **Track:** `internal` (dev-system / builder experience).
7
7
 
8
8
  ## Problem
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-13
4
4
  **Context:** GDS-V3, task [#1001](https://example.com/builders#/task/1001) (clone-scope + ungate) and task [#1002](https://example.com/builders#/task/1002) (hardware-verify). Builds on [ADR 0044](0044-per-box-live-game-preview.md) (the per-box live game preview / "sandbox") and [ADR 0031](0031-cloud-dev-environments-for-builders.md) (cloud dev boxes + rank-scoped source). Companion to [ADR 0046](0046-sandbox-first-review-gate.md) (sandbox-first review) and [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (authority is server-enforced — source visibility grants nothing). **Reverses the full-clone gate from task [#903](https://example.com/builders#/task/903).** Decision by Lars, 2026-06-13.
5
- **Status:** Accepted. Mechanism implemented under task [#1001](https://example.com/builders#/task/1001) (game-only entry `src/preview-server.js` + shared `registerGameRooms()` helper, `STARTER_SPARSE_PATHS` widened by `src/world`/`src/rooms`/`public`, `box-game-preview.service` repointed at the game-only entry, `box-source-fetch.sh` ungated); **hardware-verified under task [#1002](https://example.com/builders#/task/1002) (2026-06-14)** — a fresh **Xenos starter-scope** box (`LarsCode`) served the playable game over the public tunnel at `sandbox-<login>.example.com` (HTTP 200, Phaser client + `/game/*` assets) while **(b)** `/api/gds/*` returned 404 through that host (game-only entry does not mount the internal API) and **(c)** `migrations/` was absent on the box (the game paths `src/world`/`src/rooms`/`public` were present). Box deprovisioned after the run.
5
+ **Status:** Superseded in part by [ADR 0346](0346-dev-boxes-are-retired.md) (the per-box half; the local game-only preview survives). Was: Accepted. Mechanism implemented under task [#1001](https://example.com/builders#/task/1001) (game-only entry `src/preview-server.js` + shared `registerGameRooms()` helper, `STARTER_SPARSE_PATHS` widened by `src/world`/`src/rooms`/`public`, `box-game-preview.service` repointed at the game-only entry, `box-source-fetch.sh` ungated); **hardware-verified under task [#1002](https://example.com/builders#/task/1002) (2026-06-14)** — a fresh **Xenos starter-scope** box (`LarsCode`) served the playable game over the public tunnel at `sandbox-<login>.example.com` (HTTP 200, Phaser client + `/game/*` assets) while **(b)** `/api/gds/*` returned 404 through that host (game-only entry does not mount the internal API) and **(c)** `migrations/` was absent on the box (the game paths `src/world`/`src/rooms`/`public` were present). Box deprovisioned after the run.
6
6
  **Track:** `internal` (dev boxes / builder experience).
7
7
 
8
8
  ## Problem
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-13
4
4
  **Context:** GDS, security reports SR[#2](https://example.com/builders#/task/2) (HIGH) + SR[#11](https://example.com/builders#/task/11) (HIGH) from the 2026-06-08 server audit ([#870](https://example.com/builders#/task/870)); task [#919](https://example.com/builders#/task/919). Owner decision 2026-06-09: do the proper redesign, not the interim named-tunnel disable. Governed by the trust-boundary rule in [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) and the box subsystem in [ADR 0031](0031-cloud-dev-environments-for-builders.md) / [ADR 0038](0038-chromebook-ttyd-cloudflare-tunnel.md) / [ADR 0044](0044-per-box-live-game-preview.md).
5
- **Status:** Accepted.
5
+ **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted.
6
6
  **Track:** `internal` (development system / builder dev boxes + access control).
7
7
 
8
8
  ## Problem
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-13
4
4
  **Context:** GDS, task [#1025](https://example.com/builders#/task/1025). A live "can I publish from a dev box?" test failed even after CI self-deploy ([#859](https://example.com/builders#/task/859) / [ADR 0042](0042-builder-self-deploy-ci-auto-merge.md)) went live. Sits at the intersection of [ADR 0042](0042-builder-self-deploy-ci-auto-merge.md) (ci deploy mode), [ADR 0053](0053-scoped-dev-box-session.md) (box-scoped session, [#919](https://example.com/builders#/task/919)), and the box on-ramp ([ADR 0038](0038-chromebook-ttyd-cloudflare-tunnel.md) browser-only builders, [ADR 0044](0044-per-box-live-game-preview.md), [ADR 0046](0046-sandbox-first-review-gate.md) sandbox-first ship gate / [#927](https://example.com/builders#/task/927), which expects `/builder-ship` to run *on* a box). Governed by the trust boundary in [ADR 0016](0016-trust-boundary-server-enforced-permissions.md).
5
- **Status:** Accepted.
5
+ **Status:** Superseded in part by [ADR 0346](0346-dev-boxes-are-retired.md) (the dev box as its reason — the publish mechanism SURVIVES and now serves any checkout with no push credential). Was: Accepted.
6
6
  **Track:** `internal` (development system / builder dev boxes + ship pipeline).
7
7
 
8
8
  ## Problem
@@ -1,7 +1,7 @@
1
1
  # ADR 0057 — Per-builder container cost ledger (org-funded → self-fund tracking)
2
2
 
3
3
  **Date:** 2026-06-15
4
- **Status:** Accepted
4
+ **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted
5
5
  **Track:** `internal` (dev-system / cost oversight).
6
6
  **Context:** GDS-V3, task [#602](https://example.com/builders#/task/602), criterion C-cost. Implements the ledger half of ADR 0031 §5 (cost passthrough). Companion to [ADR 0031](0031-cloud-dev-environments-for-builders.md) (the dev box) and the early-stage billing policy ("tracked, not settled" — both example earnings and container costs are recorded but not actually paid/charged yet).
7
7
 
@@ -1,6 +1,6 @@
1
1
  # ADR 0059 — Single approval: remove the dev-box (first-box) Archon approval gate
2
2
 
3
- - **Status:** Accepted
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted
4
4
  - **Date:** 2026-06-16
5
5
  - **Track:** internal
6
6
  - **Supersedes:** [ADR 0035](0035-builder-onboarding-three-paths.md) §2 (the first-box Archon approval gate, [#717](https://example.com/builders#/task/717) / migration 071) and the **box half** of [ADR 0057](0057-discord-archon-approval-channels.md) (the `#box-approvals` channel + the box-intent approve/deny wiring)
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-20
4
4
  **Context:** GDS / dev boxes, task [#1289](https://example.com/builders#/task/1289). Builds on [ADR 0031](0031-cloud-dev-environments-for-builders.md) (cloud dev environments) and [ADR 0053](0053-scoped-dev-box-session.md) (scoped dev-box session). Touches `scripts/gds/box.js`, `src/bongos/boxes.js`, `src/bongos/do-api.js`, `infra/box-drift-reconcile.{service,timer}`.
5
- **Status:** Accepted.
5
+ **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted.
6
6
  **Track:** `internal` (development system / builder dev boxes).
7
7
 
8
8
  ## Problem — the "zombie" box
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Date:** 2026-06-20
4
4
  **Context:** Bongos App `BONGOS-V1` / criterion C1 (signed-installable), task [#1296](https://example.com/builders#/task/1296) (`BV1.R01`). Builds on [ADR 0045](0045-devbox-desktop-app.md) (the Dev Box desktop app) and [ADR 0004](0004-example-name-and-trademark-acceptance.md) (brand discipline). The bongos app forks the Dev Box app's Electron shell ([#1298](https://example.com/builders#/task/1298) `BV1.R03`), so it inherits that app's signing machinery; this ADR pins which parts it inherits now and which it defers.
5
- **Status:** Accepted.
5
+ **Status:** Superseded in part by [ADR 0346](0346-dev-boxes-are-retired.md) (absorbing the Dev Box app, which was deleted). Was: Accepted.
6
6
  **Track:** `internal` (development system → the Cloud Bongos desktop app line; bongos succeeds the GDS after GDS-V4).
7
7
 
8
8
  ## Problem — an unsigned mic + screen app reads as hostile
@@ -1,6 +1,6 @@
1
1
  # ADR 0072 — Dev-box code-staleness: make it visible, never auto-reset
2
2
 
3
- - **Status:** Accepted
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted
4
4
  - **Date:** 2026-06-20
5
5
  - **Track:** internal
6
6
  - **Builds on:** [ADR 0031](0031-cloud-dev-environments-for-builders.md) (per-builder dev boxes), the `box-source-fetch.sh` self-update machinery ([#600](https://example.com/builders#/task/600), [#985](https://example.com/builders#/task/985)), and task [#1187](https://example.com/builders#/task/1187) (the box host-key reporter — the report-up cron pattern this mirrors)
@@ -21,7 +21,7 @@ Two prior decisions were supposed to prevent this and don't fully:
21
21
 
22
22
  ## Decision
23
23
 
24
- **Pre-allow the generic GDS API channel.** Add `Bash(node scripts/gds/api.js:*)` to the project `.claude/settings.json` `permissions.allow`. It ships in the repo, so it reaches the box, laptops, and **every permission mode** at once, and it is locked against silent regression by [`tests/box_ship_permissions.mjs`](../../tests/box_ship_permissions.mjs).
24
+ **Pre-allow the generic GDS API channel.** Add `Bash(node scripts/gds/api.js:*)` to the project `.claude/settings.json` `permissions.allow`. It ships in the repo, so it reaches the box, laptops, and **every permission mode** at once, and it is locked against silent regression by [`tests/box_ship_permissions.mjs`](../../tests/cli_allowlist_permissions.mjs).
25
25
 
26
26
  The owner chose **"trust the whole channel"** over a narrower "reads + a few specific write paths" split — the split is brittle (breaks when a skill uses a slightly different path or flag order) and buys only illusory least-privilege, because the server already gates every write by rank.
27
27
 
@@ -1,6 +1,6 @@
1
1
  # ADR 0123 — Idle-swept dev boxes lose uncommitted work: an on-box autosave-push, not a VM snapshot
2
2
 
3
- - **Status:** Accepted (the approach + rejected alternatives are decided; the cron script + operator credential wiring are task-pending — see §Consequences for the seeded follow-up). **No autosave code ships in this ADR — it is a SPIKE.**
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted (the approach + rejected alternatives are decided; the cron script + operator credential wiring are task-pending — see §Consequences for the seeded follow-up). **No autosave code ships in this ADR — it is a SPIKE.**
4
4
  - **Date:** 2026-07-05
5
5
  - **Track:** internal (dev-box lifecycle)
6
6
  - **Task:** [#837](https://example.com/builders#/task/837) — "SPIKE: don't lose uncommitted work when a dev box is reclaimed (idle sweep deprovisions, no snapshot)."
@@ -1,6 +1,6 @@
1
1
  # 0144 — Re-home the dev-box fleet onto the cloudbongos.com plane (native runners, local DB, isolated fleet tag)
2
2
 
3
- - **Status:** Accepted — Phases 1–3 built in task 1002239. **Phase 4 part (a) (stop legacy `cohost-box-*` runners) done in task 1002248** (2026-07-15); part (b) (disable dev-box on example.com) flagged to owner — not reachable from the cloudbongos control plane.
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted — Phases 1–3 built in task 1002239. **Phase 4 part (a) (stop legacy `cohost-box-*` runners) done in task 1002248** (2026-07-15); part (b) (disable dev-box on example.com) flagged to owner — not reachable from the cloudbongos control plane.
4
4
  - **Date:** 2026-07-15
5
5
  - **Deciders:** Lars (owner/Archon) chose "re-home properly, not a legacy band-aid" + pre-approved box spend; Claude designed + executed autonomously.
6
6
  - **Task:** [#1002239](https://cloudbongos.com/builders#/task/1002239) · supersedes stale incident [#1002031](https://cloudbongos.com/builders#/task/1002031) · **Goal:** 1000025 (builder experience — hall, dev box, skills).
@@ -1,6 +1,6 @@
1
1
  # 0145 — The Dev Box app becomes a branding-driven module (one source, per-instance builds)
2
2
 
3
- - **Status:** Accepted — Phase 1 built in task [#1002255](https://cloudbongos.com/builders#/task/1002255). Downloads (1002257), CI + releases repo + signing (1002258), and end-to-end verify (1002256) follow.
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted — Phase 1 built in task [#1002255](https://cloudbongos.com/builders#/task/1002255). Downloads (1002257), CI + releases repo + signing (1002258), and end-to-end verify (1002256) follow.
4
4
  - **Date:** 2026-07-15
5
5
  - **Deciders:** Lars (owner) — "re-set up the dev box app so it's properly installable for cloudbongos; the design should not be linked to a specific project but to the Cloud Bongos design; it should live on cloudbongos and be maintained there as a module, and Example should borrow it."
6
6
  - **Extends:** ADR 0045 (the Dev Box desktop app), ADR 0072 (app signing posture), ADR 0083 (module system), ADR 0004/0062 (brand discipline — no hardcoded host identity), [branding contract](../branding-contract.md).
@@ -1,5 +1,12 @@
1
1
  # 0145 — A free "own-repo" hosted project tier needs domain separation + per-project isolation (not a bare cloudbongos.com subdomain)
2
2
 
3
+ - **Status update 2026-09-29 — Finding 1 is FIXED by host-only hub cookies ([task 1004357](https://cloudbongos.com/builders#/task/1004357); build sequence step 1, the cookie half).** Option A was chosen over a separate registrable domain for halls: it needs no new domain, no DNS or TLS work and no change to any project's address, and it closes the hole for every hall already live. What changed, all in `src/bongos/routes/auth.js`:
4
+ - **Nothing the hub sets carries a `Domain` attribute any more.** The session is `__Host-bongos_session` over https (a browser only accepts a `__Host-` cookie that is Secure, `Path=/` and host-only), plain `bongos_session` on local http. The OAuth and Discord handshake cookies are host-only too.
5
+ - **`builders.` / `status.` / `www.` get the session through a one-time handoff.** A sign-in begun on one of them restarts on the canonical host (the only host the GitHub callback reaches). The canonical host then redirects back with a single-use, 60-second code bound to that one host, and `GET /auth/web/handoff` trades it for that host's own host-only copy of the same token. A builder already signed in on the apex who opens `builders.` skips GitHub entirely. A code is only ever minted for one of the hub's four hosts, never a hall.
6
+ - **Existing browsers are moved over.** Every hub API request that carries a pre-fix session cookie and no `__Host-` one re-issues the same token host-only and expires the apex-scoped copy.
7
+ - **Proof:** `tests/hub_cookie_host_only.mjs` runs the real router through an RFC 6265 cookie jar and asserts a hall at `<handle>.<apex>` receives no hub cookie after the full flow.
8
+ - **Still open from step 1:** CSRF tokens on state-changing hub endpoints. `SameSite=Lax` does not separate a hall from the hub, because they are the same site. The legacy session names are still ACCEPTED on read (so nobody was signed out at deploy), which leaves a hall able to plant a cookie under an old name. Retiring those names (task 1003706's Part 4) closes that. A token copied by a hall BEFORE this shipped stays valid until it expires or its builder signs out.
9
+
3
10
  - **Status update 2026-09-28 — Finding 1 is OPEN again, and permanently ([ADR 0345](0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md) decision 6).** Project halls now stay hosted on our box, so ADR 0323's "closed by removing its precondition" below no longer holds. Measured: the hub still sets `Domain=.cloudbongos.com`, and a hall loads modules from its own repository, so a hall at one of our addresses can read a visiting hub builder's session. The sign-in hardening is a **blocking prerequisite again** for any new hall at one of our addresses, filed as [task 1004357](https://cloudbongos.com/builders#/task/1004357). A hall on the owner's own domain is not exposed.
4
11
  - **Status:** **Superseded in part by [ADR 0323](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md)** (2026-09-21). Three of this ADR's decisions are replaced: the free tier becomes a **capped trial**, not all-free; customer projects leave the shared box for a managed platform, so **container isolation on our own box is no longer the plan**; and **Finding 1 is closed by removing its precondition** — with no customer code on a `cloudbongos.com` subdomain there is no tenant to receive the hub session cookie, so the sign-in hardening in the build sequence below is no longer a blocking prerequisite (it remains worth doing on its own merits). The two security FINDINGS below stand as analysis and are the reason the new direction is safe.
5
12
  - **Status:** Accepted — owner decided 2026-07-15: **keep `cloudbongos.com`** (no separate domain), **container** isolation, **all-free** for now. Choosing the literal same domain makes hardening core sign-in a required prerequisite (see "Decisions" + "Required build sequence"). Build unblocked ([task 1002251](https://cloudbongos.com/builders#/task/1002251)); blocker 1000080 resolved.
@@ -1,9 +1,9 @@
1
1
  # 0148 — Task-scoped box source access (rank-scope → claim-scope)
2
2
 
3
- - **Status:** Accepted (spike R90 / task 1002378 — the architecture pass for goal 1000051). Confirms the build shape of R92–R95.
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted (spike R90 / task 1002378 — the architecture pass for goal 1000051). Confirms the build shape of R92–R95.
4
4
  - **Date:** 2026-07-18
5
5
  - **Deciders:** Lars (owner) — "claim a task → the box pulls only that module's code, on a base Bongos instance; the GitHub token stays on the server." Corrected the earlier "bring-your-own-GitHub" framing.
6
- - **Extends:** ADR 0031 §6 (box source-access, task 600), ADR 0086 (goals + `scope_modules` scope wall), ADR 0093 (the `lifecycle` port), ADR 0052 (sandbox-for-everyone starter scope). Spec: [`docs/specs/<redacted>.md`](../specs/<redacted>.md).
6
+ - **Extends:** ADR 0031 §6 (box source-access, task 600), ADR 0086 (goals + `scope_modules` scope wall), ADR 0093 (the `lifecycle` port), ADR 0052 (sandbox-for-everyone starter scope). Spec: `docs/specs/<redacted>.md` (deleted with the dev box by task 1003898; read it in git history).
7
7
 
8
8
  ## Context
9
9
 
@@ -1,6 +1,6 @@
1
1
  # 0151 — Governance: permissions as the atom, ranks as seeded roles
2
2
 
3
- - **Status:** Accepted
3
+ - **Status:** Superseded in part by [ADR 0346](0346-dev-boxes-are-retired.md) (§3 — the narrowing session scope was removed; the declared-source rule survives). Was: Accepted
4
4
  - **Date:** 2026-07-24
5
5
  - **Deciders:** Lars (owner / Archon), Claude
6
6
  - **Track:** `internal` (GDS / methodology — a Cloud Bongos platform capability)
@@ -1,6 +1,6 @@
1
1
  # 0193 — Pause task-scoped box slices: a box serves its rank scope (the full repo for Metic+)
2
2
 
3
- - **Status:** Accepted
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: Accepted
4
4
  - **Date:** 2026-08-25
5
5
  - **Tasks:** [#1003285](https://cloudbongos.com/builders#/task/1003285) (the pause), from the owner's 2026-08-25 dev-box efficiency review; the review's other findings are [#1003286](https://cloudbongos.com/builders#/task/1003286)–[#1003291](https://cloudbongos.com/builders#/task/1003291) and shipped [#1003253](https://cloudbongos.com/builders#/task/1003253).
6
6
  - **Pauses, does not repeal:** [ADR 0148](0148-task-scoped-box-source-access.md) / goal 1000051 — the claim-driven slice. The machinery stays; the default flips.
@@ -1,6 +1,6 @@
1
1
  # ADR 0277 — A box is "in use" only while a human is attached, and the claim expires
2
2
 
3
- - **Status:** accepted
3
+ - **Status:** Superseded by [ADR 0346](0346-dev-boxes-are-retired.md) — the dev box was removed (goal 1000120). Was: accepted
4
4
  - **Date:** 2026-09-10
5
5
  - **Task:** [1003507](https://cloudbongos.com/builders#/task/1003507) (goal 1000095 — *Working area 6*, criterion `wa6-role-experience`)
6
6
  - **Supersedes the "never touch a box with a claude process" rule** in [ADR 0031](0031-cloud-dev-environments-for-builders.md) §5(a); the park/preserve guarantee of tasks 1002726/1002727 is unchanged and is what makes this safe.
@@ -0,0 +1,69 @@
1
+ # ADR 0346 — Dev boxes are retired; a builder builds from their own checkout
2
+
3
+ - **Status:** accepted
4
+ - **Date:** 2026-09-28
5
+ - **Task:** [task 1003898](https://cloudbongos.com/builders#/task/1003898) (goal 1000120 — Dev-box removal)
6
+ - **Deciders:** Lars (Archon) decided the removal and its three rulings on 2026-09-13. Claude carried out the removal and wrote the record.
7
+ - **Supersedes:** [0031](0031-cloud-dev-environments-for-builders.md) · [0044](0044-per-box-live-game-preview.md) · [0045](0045-devbox-desktop-app.md) · [0053](0053-scoped-dev-box-session.md) · [0057](0057-container-cost-ledger.md) · [0059](0059-single-approval-remove-devbox-approval-gate.md) · [0071](0071-box-confirm-before-destroyed-and-drift-reconcile.md) · [0072 (code staleness)](0072-dev-box-code-staleness-visibility.md) · [0123](0123-box-idle-sweep-autosave-before-destroy.md) · [0144](0144-devbox-rehome-onto-cloudbongos-plane.md) · [0145 (desktop-app branding)](0145-devbox-app-branding-driven-module.md) · [0148](0148-task-scoped-box-source-access.md) · [0193](0193-pause-task-scoped-box-slices.md) · [0277](0277-a-box-is-in-use-only-while-a-human-is-attached.md)
8
+ - **Supersedes in part:** [0035](0035-builder-onboarding-three-paths.md) (the DO-box path) · [0046](0046-sandbox-first-review-gate.md) (the box half) · [0052](0052-sandbox-for-everyone-game-only-preview.md) (the box half) · [0055](0055-server-mediated-branch-publish.md) (the box as its reason; the mechanism survives) · [0072 (Bongos app)](0072-bongos-app-mac-signed-first-windows-deferred.md) (absorbing the Dev Box app) · [0151](0151-governance-permissions-as-atom-ranks-as-roles.md) §3 (the narrowing session scope)
9
+
10
+ ## Context
11
+
12
+ ADR 0031 gave every builder a hosted cloud machine, the **dev box**: a DigitalOcean droplet built from `.devcontainer/`, switched on and off by a desktop app (0045), reached over SSH or a browser terminal, holding a box-scoped session (0053) and a task-scoped sparse checkout (0148), swept when idle (0123), and landing its work through the server because it held no push credential (0055). It grew to roughly 22,000 lines, and about a third of it lived in the core rather than in `modules/dev-box/`.
13
+
14
+ By 2026-09-13 it had no users. The fleet was cold: 0 live droplets, 4 destroyed, 1 parked. `/downloads/devbox/*` already answered 404, because cloudbongos.com had no `config/devbox-app.json`. Every live builder builds from a checkout of the project repo on their own machine. The owner decided to remove it outright rather than keep maintaining it.
15
+
16
+ ## Decision
17
+
18
+ We removed the dev box and everything that existed only for it. What a builder does instead is the path that already worked for everyone: **clone the project repo, sign the CLI in with `/builder-setup`, and build locally** (`/builder-stage` for a local preview). A cloud replacement, such as Claude's own cloud sessions working from a clone, is separate later work and is deliberately not designed here.
19
+
20
+ **The owner's three rulings (2026-09-13):**
21
+
22
+ 1. **Drop the box's stored data now.** The historic box compute spend is not worth preserving as box state.
23
+ 2. **Delete `.devcontainer/`** with the rest. It was the box as code; a later cloud plan defines its own environment.
24
+ 3. **Remove `bongos box`, `bongos shell` and `bongos code` entirely**, including their Metic-only `--local` modes.
25
+
26
+ **What was removed** (goal 1000120, one linear chain, each link leaving `main` green):
27
+
28
+ | Task | Removed |
29
+ |---|---|
30
+ | [1003889](https://cloudbongos.com/builders#/task/1003889) | the three CLI verbs |
31
+ | [1003890](https://cloudbongos.com/builders#/task/1003890) | the Harbor page, the box panel and the pair page |
32
+ | [1003891](https://cloudbongos.com/builders#/task/1003891) | the Electron desktop app, its CI workflow and download route |
33
+ | [1003896](https://cloudbongos.com/builders#/task/1003896) | `.devcontainer/` |
34
+ | [1003893](https://cloudbongos.com/builders#/task/1003893) | the box control-plane and bring-up scripts (`scripts/gds/box*`, `infra/box-*`), with the last box recipe |
35
+ | [1003892](https://cloudbongos.com/builders#/task/1003892) | `modules/dev-box/` and its registrations |
36
+ | [1004310](https://cloudbongos.com/builders#/task/1004310) | desktop-app pairing, the onboarding box step, and box fields in the core API |
37
+ | [1004311](https://cloudbongos.com/builders#/task/1004311) | the hall's box surfaces, preview fixtures, and the DEV BOX subgraph of the architecture diagram |
38
+ | [1003894](https://cloudbongos.com/builders#/task/1003894) | the box session scope and `box.source.full` / `box.fleet.manage` / `box.manage.own` (migration `government_019`) |
39
+ | [1003895](https://cloudbongos.com/builders#/task/1003895) | `builder_boxes`, `box_events`, `box_intents`, `builder_ssh_keys` and the `'box'` session source (migration `core_258`) |
40
+ | [1003897](https://cloudbongos.com/builders#/task/1003897) | the residual mentions in code, tests and fixtures |
41
+ | [1003899](https://cloudbongos.com/builders#/task/1003899) | (the fitness check that keeps it out) |
42
+
43
+ **Kept on purpose.** These were born for the box and are now load-bearing for everyone:
44
+
45
+ - **Server-mediated publish** (0055: `modules/lifecycle/github-push.js`, `publish-reconciler.js`, `POST /tasks/:id/publish-branch`, `ship-land.js`'s `ciLandServer`). It serves any checkout with no GitHub push credential. Only its reason changed.
46
+ - **`src/bongos-downloads.js`**, the `bongos` CLI binary downloads (the Dev Box app's own downloads were removed).
47
+ - **`src/bongos/secret-box.js`**, the AES secret store. It is unrelated to dev boxes despite the name.
48
+ - **`scripts/gds/do-api.js`**, which instance provisioning still uses.
49
+ - **The session-source registry** (`modules/government/session-scopes.json`). Its narrowing form had no user left and was removed (task 1003894). Its fail-closed rule, that a session whose source the registry does not declare is refused on every route and page, is what kept the leftover box tokens out until `core_258` deleted them.
50
+
51
+ **Deliberate retirement guards.** These still name the box, and that is correct:
52
+
53
+ - `RETIRED_MODULE_KEYS` in `src/modules.js`: an instance whose config still says `"dev-box": true` boots with a one-line warning instead of failing.
54
+ - `allowBoxScope` stays on the module doorway as a no-op until the next MAJOR. Removing an export is a MAJOR, and modules declare `^1`.
55
+ - `config/devbox-app.json` stays in the publish manifest's instance-exclude list, because instance repos still carry the file.
56
+
57
+ ## Alternatives considered
58
+
59
+ - **Keep the module, switched off.** Rejected: a third of it lived in the core (auth, migrations, the ship path, the hall), so "off" still cost maintenance and review attention on every change near it, for zero users.
60
+ - **Replace it with a cloud-session product in the same goal.** Rejected by the owner: the replacement is its own design question, and coupling it would have held the removal hostage to it.
61
+ - **One big deletion.** Rejected in the 2026-09-27 re-scope: each link deletes its own tests and regenerates its own artifacts, so `main` stays green at every step and any one link can be reverted alone.
62
+ - **Drop the four `builders.box_blocked*` columns with the tables.** Deferred to [task 1004360](https://cloudbongos.com/builders#/task/1004360). A deploy migrates while the previous server is still up, and a failed upgrade rolls back code but never schema. Every previous core reads those columns in `getBuilderById`.
63
+
64
+ ## Consequences
65
+
66
+ - A builder's only setup is a local checkout. The hall's welcome panel, the primer and the architecture diagram now describe that path. The welcome panel's own-machine steps had been hidden by a stylesheet rule written for the box's Desktop/Online toggle; they now show.
67
+ - Nothing user-facing regressed: the fleet was cold and the downloads already 404'd.
68
+ - Owner follow-ups outside the code: reclaim or verify absent the DigitalOcean and Cloudflare resources (task [1003887](https://cloudbongos.com/builders#/task/1003887), blocker 1000159, including the one parked droplet snapshot). Remove `"dev-box"` from each instance repo's `config/modules.json` and `config/devbox-app.json` when convenient; the retirement guard makes both harmless meanwhile.
69
+ - The historical ADRs above keep their bodies; each carries a status line pointing here.
@@ -437,3 +437,4 @@ This keeps the decision history honest and traceable.
437
437
  | 0343 | [**A module's score is a security gate, then an average of parts every buyer can see** ([task 1003790](https://cloudbongos.com/builders#/task/1003790), goal 1000091 — working area 5, owner Will). No module score existed; the floor, ranking and cap all derive from this one. **D1:** five parts, each 0–100 per version — Security and Tests day one; Install, Reliability (from real installs) and Tester feedback later. **D2 (owner):** failing Security means not listed, never averaged in. **D3 (owner):** overall = unweighted average of the other parts that have data; a missing part is left out, not zero. **D4 (owner):** every part's score is shown to buyers beside the overall, so they can pick the module strongest where they care (tasks 1003798/1003799). **D5:** a version without real-world data is labelled "New". **D6:** price is never an input; the Metic+ override/delist sits on top and is audited. Rejected: security in the average, day-one weights, a single number, missing-as-zero.](0343-a-module-score-is-a-security-gate-then-an-average-of-visible-parts.md) | modules / store / quality |
438
438
  | 0344 | [**A tester is a project that opts in once; an untested version reaches everyone after seven days** ([task 1003801](https://cloudbongos.com/builders#/task/1003801), goal 1000091 — working area 5, owner Will). Settles "willing user" for criterion `wa5-staged-rollout`. **D1 (owner):** a project's own admin switches on "tester" once and gets tester versions of every module it has, with a per-module "general only" override; never on by default; a builder cannot opt in someone else's project. **D2:** first releases and updates alike go to testers first. **D3 (owner):** a version moves to general after seven days if it still passes the security gate and tests, tested or not; the promotion gate may promote earlier or hold on evidence, never forever. **D4:** testers pay the normal price; any tester discount is area 8's call, named not decided. **D5:** tester installs, errors and crashes feed the score (ADR 0343). Rejected: per-module-only opt-in, one all-or-nothing switch, waiting for a tester, releasing at once.](0344-a-tester-is-a-project-that-opts-in-once.md) | modules / store / rollout |
439
439
  | 0345 | [**We host every project's hall, and its app deploys where the owner chooses** ([task 1004349](https://cloudbongos.com/builders#/task/1004349), goal 1000106 — working area 1, owner Lars). Reverses ADR 0323 §2 and the hall half of ADR 0327. **D1 (owner):** every project's Builders Hall runs on our shared server; `cloud-host` now means "hosted by us". **D2 (owner):** the hall's address is ours or the owner's own domain. **D3 (owner):** the setup wizard asks where the APP deploys; Render only at launch; choosing it creates the app on the owner's own Render account with ADR 0327's borrowed key. **D4 (owner):** "decide later" is allowed. **D5 (owner):** no project is moved. **D6 (measured):** ADR 0145 Finding 1 is live — the hub cookie is `Domain=.cloudbongos.com` and a hall loads modules from its own repo — so our-address halls need task 1004357; an owner domain is safe. **D7 (measured):** at the ceiling the create route refuses `box_full` up front, and the resize is not a prerequisite. Task 1004184 re-scoped, task 1004185 on hold.](0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md) | hosting / provisioning / security |
440
+ | 0346 | [**Dev boxes are retired; a builder builds from their own checkout** ([task 1003898](https://cloudbongos.com/builders#/task/1003898), goal 1000120 — owner Lars). Records the whole removal, link by link, the owner's three rulings of 2026-09-13 (drop the data, delete `.devcontainer/`, remove `bongos box`/`shell`/`code`), what was kept on purpose (server-mediated publish, the CLI downloads, `secret-box`, `do-api`, the declared-source rule), and the retirement guards that still name the box. Supersedes 0031, 0044, 0045, 0053, 0057, 0059, 0071, 0072 (code staleness), 0123, 0144, 0145 (desktop-app branding), 0148, 0193, 0277; supersedes in part 0035, 0046, 0052, 0055, 0072 (Bongos app), 0151 §3.](0346-dev-boxes-are-retired.md) | builder environment / removal |
@@ -1662,6 +1662,27 @@
1662
1662
  "security": []
1663
1663
  }
1664
1664
  },
1665
+ "/auth/web/handoff": {
1666
+ "get": {
1667
+ "operationId": "get_auth_web_handoff",
1668
+ "tags": [
1669
+ "auth"
1670
+ ],
1671
+ "summary": "GET /auth/web/handoff",
1672
+ "description": "GET /auth/web/handoff?code=&return= — the far side of the one-time session handoff (task 1004357). The canonical host minted `code` for THIS host after a sign-in (or for a builder already signed in there); trading it sets this host's own host-only copy of the session, then lands on `return` — a relative path only, so the handoff can never become an open redirect. rank: public — auth establishment; the one-time code is the bearer, bound to this exact host, single-use and alive for HANDOFF_TTL_MS.\n\n**Rank:** `public` — No authentication — any caller.",
1673
+ "x-rank": "public",
1674
+ "x-source": "src/bongos/routes/auth.js",
1675
+ "responses": {
1676
+ "200": {
1677
+ "description": "Success."
1678
+ },
1679
+ "400": {
1680
+ "$ref": "#/components/responses/BadRequest"
1681
+ }
1682
+ },
1683
+ "security": []
1684
+ }
1685
+ },
1665
1686
  "/auth/web/start": {
1666
1687
  "get": {
1667
1688
  "operationId": "get_auth_web_start",
@@ -27716,7 +27737,7 @@
27716
27737
  "description": "A required dependency/feature is not configured or is temporarily down."
27717
27738
  }
27718
27739
  },
27719
- "x-endpoint-count": 433,
27740
+ "x-endpoint-count": 434,
27720
27741
  "x-schema-count": 466,
27721
27742
  "x-undocumented-bodies": 11,
27722
27743
  "x-response-schemas": 319,
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Bongos API reference
4
4
 
5
- > **Generated from the live route files** — the route file is authoritative. 433 endpoints across 75 route files.
5
+ > **Generated from the live route files** — the route file is authoritative. 434 endpoints across 75 route files.
6
6
  > Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
7
7
 
8
8
  Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust-boundary-server-enforced-permissions.md)): `public` < `any-builder` < `metic+archon` < `archon`.
@@ -61,7 +61,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
61
61
  |---|---|---|---|---|
62
62
  | GET | `/api/bongos/audit-log` | `metic+archon` | — | GET /api/bongos/audit-log?limit=&offset=&builder_id=&method= Paginated, newest-first. |
63
63
 
64
- ## `auth` (14)
64
+ ## `auth` (15)
65
65
 
66
66
  | Method | Path | Rank | Body | Description |
67
67
  |---|---|---|---|---|
@@ -78,6 +78,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
78
78
  | POST | `/api/bongos/auth/revoke-all` | `archon` | `builder_id` | POST /auth/revoke-all — V3.R38 / task 259. |
79
79
  | GET | `/api/bongos/auth/web/admission-status` | `public` | — | GET /auth/web/admission-status?login=X — the probe renderAccessPendingPage polls (task 1002097). |
80
80
  | GET | `/api/bongos/auth/web/callback` | `public` | — | OAuth callback; the state cookie + code are the bearer. |
81
+ | GET | `/api/bongos/auth/web/handoff` | `public` | — | GET /auth/web/handoff?code=&return= — the far side of the one-time session handoff (task 1004357). |
81
82
  | GET | `/api/bongos/auth/web/start` | `public` | — | /auth/web — browser flow (used by the /builders page). |
82
83
 
83
84
  ## `autonomy` (8)
@@ -11,7 +11,7 @@
11
11
 
12
12
  - **Domain:** `example.com` — registered at Cloudflare Registrar. DNS A records (apex + `www`) point to the droplet at `REDACTED_IP`. Cloudflare proxy is **ON (orange cloud)**. CF terminates TLS at the edge; the **Cloudflare Origin Certificate** handles the CF↔origin leg (valid through 2041-05-04). A `status` subdomain is added at PMS-V1 deploy time (see `docs/pms-v1-deploy.md` (instance-side)) with the same proxy/cert posture; Origin Cert needs `*.example.com` SAN. Real client IPs come in via `CF-Connecting-IP` / `X-Forwarded-For`, wired into Caddy's `trusted_proxies static` (the Cloudflare IP ranges) in the `infra/Caddyfile` global options block.
13
13
  - **Production droplet (co-hosting box):** DigitalOcean, hostname `example`, IPv4 `REDACTED_IP`, IPv6 `REDACTED_IP`. NYC3, Premium Intel 2 GB / 1 vCPU / 60 GB NVMe. Ubuntu 24.04 LTS. ~$14/mo. Hosts the OTB game + co-hosted project instances (`emersonian-circles.`, `staging.`).
14
- - **Cloud Bongos control-plane droplet (NEW 2026-07-05, [ADR 0126](adr/0126-dedicated-cloudbongos-control-plane-droplet.md), task [#2063](https://example.com/builders#/task/2063)):** DigitalOcean, hostname `cloudbongos`, IPv4 `REDACTED_IP`, NYC3, `s-1vcpu-2gb` (2 GB / 1 vCPU), Ubuntu 24.04, ~$12/mo, tag `cloudbongos-control`. Runs **cloudbongos.com + `builders.` + `status.`** (`src/platform-server.js`, Cloud Bongos brand, port 3002, own `cloudbongos` DB, checkout `/home/lars/cloudbongos-instance`, the root subscribed to the nightly core-patch sweep) and is the **sole token-holding control plane** — a SECOND root, `/home/lars/cloudbongos`, survives only to vendor `infra/` (the box cloud-init the core package does not ship, ADR 0150); it is not subscribed, so nothing may resolve core code from it (task 1002738, [ADR 0144](adr/0144-devbox-rehome-onto-cloudbongos-plane.md) follow-up 5) — `/etc/cloudbongos/box.env` holds the account-wide Cloudflare token (IP-locked to this box) + the DigitalOcean token; this is where `provision.js` provisions instance subdomains. **Structurally separate SSH** (own `cloudbongos_ed25519` key). IPv6 egress disabled so the IPv4-locked CF token can't be spuriously rejected. cloudbongos.com was previously a co-tenant on the co-hosting box; that unit was decommissioned at cutover. **Follow-ups:** deploy automation (task [#2064](https://example.com/builders#/task/2064)) + fully stripping cloud tokens off the co-hosting box (task [#2065](https://example.com/builders#/task/2065)).
14
+ - **Cloud Bongos control-plane droplet (NEW 2026-07-05, [ADR 0126](adr/0126-dedicated-cloudbongos-control-plane-droplet.md), task [#2063](https://example.com/builders#/task/2063)):** DigitalOcean, hostname `cloudbongos`, IPv4 `REDACTED_IP`, NYC3, `s-1vcpu-2gb` (2 GB / 1 vCPU), Ubuntu 24.04, ~$12/mo, tag `cloudbongos-control`. Runs **cloudbongos.com + `builders.` + `status.`** (`src/platform-server.js`, Cloud Bongos brand, port 3002, own `cloudbongos` DB, checkout `/home/lars/cloudbongos-instance`, the root subscribed to the nightly core-patch sweep) and is the **sole token-holding control plane** — a SECOND root, `/home/lars/cloudbongos`, is left over from the retired dev-box bring-up, which vendored `infra/` from it ([ADR 0346](adr/0346-dev-boxes-are-retired.md)); it is not subscribed, so nothing may resolve core code from it (task 1002738) — `/etc/cloudbongos/box.env` holds the account-wide Cloudflare token (IP-locked to this box) + the DigitalOcean token; this is where `provision.js` provisions instance subdomains. **Structurally separate SSH** (own `cloudbongos_ed25519` key). IPv6 egress disabled so the IPv4-locked CF token can't be spuriously rejected. cloudbongos.com was previously a co-tenant on the co-hosting box; that unit was decommissioned at cutover. **Follow-ups:** deploy automation (task [#2064](https://example.com/builders#/task/2064)) + fully stripping cloud tokens off the co-hosting box (task [#2065](https://example.com/builders#/task/2065)).
15
15
  - **Where projects run ([ADR 0345](adr/0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md), 2026-09-28):** every project's **Builders Hall** is hosted by us on the shared control-plane box (`hosting_shape='cloud-host'`, labelled "hosted by us"), at one of our addresses or the owner's own domain. The project's **app** deploys wherever its owner chooses; the setup wizard offers Render first, creating the app on the owner's own Render account with a borrowed, never-stored key (ADR 0327 §2). ADR 0323's plan to move the halls to Render is reversed. Halls at our addresses receive the hub cookie until task 1004357 ships.
16
16
  - **Server stack:** Node.js 22 LTS · PostgreSQL 16 · Caddy 2.11.2. `example.service` (systemd) runs `node server.js` as user `lars` on `127.0.0.1:3000`; restart-on-failure.
17
17
  - **Hardening:** `lars` user with passwordless sudo; SSH key-only (root login disabled, password auth disabled); ufw allows 22/80/443; fail2ban active; unattended-upgrades enabled; timezone `America/New_York`.
@@ -35,7 +35,6 @@ The product is organized as a small kernel (`src/`) + **19 modules** under `modu
35
35
  - **The loader** (`src/module-loader/loader.js`) — discovers `modules/*/module.json` at boot, validates manifests, checks `coreVersion` compatibility, and mounts each enabled module's routes behind kernel-composed auth. No core file changes to add or remove a module.
36
36
  - **Enablement** — `config/modules.json` (instance config) or `<PREFIX>_MODULE_<KEY>=1` (env override). Vanilla = all off.
37
37
  - **Feature modules** (optional — off on a vanilla instance, enabled per-instance via `config/modules.json`):
38
- - `dev-box` — per-builder cloud dev environments; ports: `box.hasEverConnected`
39
38
  - `game` — Phaser client + Colyseus rooms + world/terrain; ports: `game.staticRoot`, `game.registerRooms`, `game.precreateWorldRoom`
40
39
  - `discord` — ship broadcast + inbound bot + role-sync; port: `discord.isLinked`
41
40
  - `art-pipeline` — pixel-art generation + artist discipline; port: `art.sharedKeyConfigured`
@@ -67,7 +66,7 @@ The product is organized as a small kernel (`src/`) + **19 modules** under `modu
67
66
  **Code & deploy:**
68
67
 
69
68
  - **Code repo:** [github.com/example-owner/example](https://github.com/example-owner/example) — **private**. Clone URL: `git@github.com:example-owner/example.git`. Default branch: `main`. GitHub user: `example-owner`.
70
- - **Deploy:** two modes, selected by `config/deploy.json` `mode` (env `<PREFIX>_DEPLOY_MODE` overrides — `BONGOS_DEPLOY_MODE` on a vanilla instance; the legacy `OTB_DEPLOY_MODE` is still read), currently **`ci`** (flipped from `laptop` 2026-06-13, [#859](https://example.com/builders#/task/859)) — see [ADR 0042](adr/0042-builder-self-deploy-ci-auto-merge.md). **`laptop`** (legacy): `git push origin main` from the Mac → `ssh lars@REDACTED_IP ~/deploy.sh`. The droplet has a read-only deploy key at `~/.ssh/github_deploy` registered on the repo. `~/deploy.sh` does: `git fetch + reset --hard origin/main` → `npm ci --omit=dev` if `package-lock.json` changed → `./scripts/migrate.sh` → `sudo systemctl restart example` → `curl /healthz` smoke (**retried up to 15× @ 1s** — `systemctl` reports "active" before the Node process binds the port, so a single curl raced the bind and false-failed healthy deploys: [ADR 0056](adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md) / [#1026](https://example.com/builders#/task/1026)). Droplet checkout: `/home/lars/example/`. `~/deploy.sh` is hand-maintained on the droplet but now has a byte-faithful, reviewable repo mirror at `scripts/deploy/deploy-prod.sh` (instance-side) — **not** auto-installed (the ci deploy key is forced-command-locked, ADR 0042/0043; reinstall by hand on change: `scp scripts/deploy/deploy-prod.sh lars@…:deploy.sh`). **`ci`** (current, armed via [#679](https://example.com/builders#/task/679)): no builder box holds the droplet key — `ship.js` opens a PR + enables GitHub auto-merge, and `.github/workflows/deploy-prod.yml` (instance-side) runs the same `~/deploy.sh` from Actions on merge to `main`. Cutover preconditions (incl. a server-recorded **CI grade-gate**) are in ADR 0042.
69
+ - **Deploy:** two modes, selected by `config/deploy.json` `mode` (env `<PREFIX>_DEPLOY_MODE` overrides — `BONGOS_DEPLOY_MODE` on a vanilla instance; the legacy `OTB_DEPLOY_MODE` is still read), currently **`ci`** (flipped from `laptop` 2026-06-13, [#859](https://example.com/builders#/task/859)) — see [ADR 0042](adr/0042-builder-self-deploy-ci-auto-merge.md). **`laptop`** (legacy): `git push origin main` from the Mac → `ssh lars@REDACTED_IP ~/deploy.sh`. The droplet has a read-only deploy key at `~/.ssh/github_deploy` registered on the repo. `~/deploy.sh` does: `git fetch + reset --hard origin/main` → `npm ci --omit=dev` if `package-lock.json` changed → `./scripts/migrate.sh` → `sudo systemctl restart example` → `curl /healthz` smoke (**retried up to 15× @ 1s** — `systemctl` reports "active" before the Node process binds the port, so a single curl raced the bind and false-failed healthy deploys: [ADR 0056](adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md) / [#1026](https://example.com/builders#/task/1026)). Droplet checkout: `/home/lars/example/`. `~/deploy.sh` is hand-maintained on the droplet but now has a byte-faithful, reviewable repo mirror at `scripts/deploy/deploy-prod.sh` (instance-side) — **not** auto-installed (the ci deploy key is forced-command-locked, ADR 0042/0043; reinstall by hand on change: `scp scripts/deploy/deploy-prod.sh lars@…:deploy.sh`). **`ci`** (current, armed via [#679](https://example.com/builders#/task/679)): no builder machine holds the droplet key — `ship.js` opens a PR + enables GitHub auto-merge, and `.github/workflows/deploy-prod.yml` (instance-side) runs the same `~/deploy.sh` from Actions on merge to `main`. Cutover preconditions (incl. a server-recorded **CI grade-gate**) are in ADR 0042.
71
70
  - **Post-merge branch cleanup (standard practice):** Every feature branch that lands on main via `--no-ff` merge gets deleted on origin once the deploy succeeds. The merge commit itself preserves the branch's history in main, so the named ref isn't carrying any signal — keeping it just clutters GitHub's branch list and tricks the UI into offering empty PRs against zero-diff branches. The auto-merge in [`scripts/gds/ship.js`](../scripts/gds/ship.js) does this automatically after `~/deploy.sh` returns 0; the manual [`/merge-mode`](../.claude/skills/merge-mode/SKILL.md) flow follows the same step 5b. Local refs and worktree directories stay — only the origin branch and the local tracking ref (cleaned on next `git fetch --prune`) go.
72
71
  - **Local dev machine:** Lars's MacBook Air (Apple Silicon, macOS Sequoia). Tooling installed via Homebrew at `/opt/homebrew/`. Node v26.0.0 locally; `package.json` declares `"engines": { "node": ">=22" }`. Single ed25519 SSH key (`~/.ssh/id_ed25519`) authenticates to GitHub and droplet.
73
72
  - **Smoke tests:** lightweight scripts in `/tmp/colyseus-*-smoketest.js` verify joinOrCreate, move protocol, interact, identity, queue. Run any after deploy with `node /tmp/colyseus-<name>-smoketest.js`.
@@ -238,7 +237,7 @@ Bongos views: `claimable_tasks` (rebuilt in 006 to use SELECT t.*; rebuilt again
238
237
 
239
238
  **Bongos API (`/api/bongos/*`, mounted in the same Node process):**
240
239
 
241
- - **Canonical path is `/api/bongos` (task 1919); `/api/gds` is a PERMANENT alias.** The router + the discovery index are dual-mounted at both by `serve-internal.js`, driven by the single `src/bongos/api-prefix.js` constant (`API_PREFIX` + `LEGACY_API_PREFIXES`). The legacy `/api/gds` alias never goes away — shipped Dev Box binaries, the cached status mirror, and live boxes call it. **Exception:** the GitHub OAuth callback stays pinned to `/api/gds/auth/web/callback` (registered on the OAuth app; `src/bongos/routes/auth.js`). Not yet flipped (still ride the permanent alias): `src/**` internal callers + the deeper internal names (folders, `GDS_*` env, `gds_session` cookie) — the deferred internal-rename pass.
240
+ - **Canonical path is `/api/bongos` (task 1919); `/api/gds` is a PERMANENT alias.** The router + the discovery index are dual-mounted at both by `serve-internal.js`, driven by the single `src/bongos/api-prefix.js` constant (`API_PREFIX` + `LEGACY_API_PREFIXES`). The legacy `/api/gds` alias never goes away — shipped older CLI binaries, the cached status mirror, and older instances call it. **Exception:** the GitHub OAuth callback stays pinned to `/api/gds/auth/web/callback` (registered on the OAuth app; `src/bongos/routes/auth.js`). Not yet flipped (still ride the permanent alias): `src/**` internal callers + the deeper internal names (folders, `GDS_*` env, `gds_session` cookie) — the deferred internal-rename pass.
242
241
  - **Self-describing (task 1918, [ADR 0109](adr/0109-self-describing-openapi-and-hosted-docs.md)).** The whole surface is documented in the standard **OpenAPI 3.1** format at [`docs/api/openapi.json`](api/openapi.json), **generated from the live route files** by `scripts/gds/gen-api-docs.js` (reusing the same `route-rank-check.js` introspection the ship-time rank gate uses — so it can't drift; a fitness `--check` + ship-time regen enforce freshness). A rendered docs site (vendored Redoc) is served at **`/docs`** on the apex of every instance (`cloudbongos.com/docs` canonical); `GET /api/bongos` returns a machine-discovery index pointing at it. The generated [`docs/api-reference.md`](api-reference.md) supersedes the old hand-maintained `routes-permissions.md`. The mount prefix is the single `src/bongos/api-prefix.js` constant.
243
242
 
244
243
  - Auth: `POST /auth/device/start`, `/poll` (CLI Device Flow); `GET /auth/web/start`, `/web/callback` (browser); `POST /auth/logout`.
@@ -246,9 +245,9 @@ Bongos views: `claimable_tasks` (rebuilt in 006 to use SELECT t.*; rebuilt again
246
245
  - Authenticated CLI/web: `GET /me` (returns `rank`), `GET /versions[/progress]`, `GET /tasks[?version=&status=&kind=&discipline=]`, `GET /tasks/claimable[?version=&discipline=]`, `GET /tasks/:id`, `POST /tasks` (accepts `discipline` + `criterion_ids`; **metic+** — [ADR 0090](adr/0090-metic-task-authoring.md)), `PATCH /tasks/:id` (supports `parent_task_id`, `kind`, `discipline`, `goal_id` — (re)assign the task's goal; must be a goal in the task's own version, task 1763; **metic+**), `POST /tasks/:id/promote` (**metic+**), `POST /claims`, `POST /claims/:id/resolve`, `POST /cost`.
247
246
  - Criterion rollup + task↔criterion links ([ADR 0025](adr/0025-structured-criterion-task-link.md), [#435](https://example.com/builders#/task/435)/[#438](https://example.com/builders#/task/438)): `GET /versions/:id/progress` rolls up each done-when criterion → its gating tasks (via `task_criteria`) → live status counts + the not-yet-shipped `remaining[]` + `unattributed_tasks` (the read behind the `/status` skill, in `src/bongos/done-when.js criterionProgress`). The link CRUD mirrors the dependency endpoints: `GET /tasks/:id/criteria`, `POST /tasks/:id/criteria` (**metic+** — [ADR 0090](adr/0090-metic-task-authoring.md)) and `DELETE /tasks/:id/criteria/:criterionId` (**metic+**). A criterion ref is a numeric `done_when_criteria.id`, a positional `"Cn"` token, or a `criterion_id` slug, resolved against the task's version (`POST /tasks` links at create time so `/status` counts the task with no backfill).
248
247
  - Module store publish (ADR 0338 D1, task 1004271): `POST /store/modules/:key/versions` (`requireBuilder` + `requirePermission('module.submit')`, Metic floor). The body is the raw gzip tarball `bongos module publish` builds (own `express.raw` parser, 5 MB cap; 16 MB / 2000 files unpacked). `scripts/gds/module-artifact.js` re-verifies every hash and the publish gate, then `src/bongos/module-store.js` keeps the file under `var/module-store/` and inserts the `store_module_versions` row in one transaction. The first publish makes the caller the author; later versions are author-only, newer than the last and never overwritten; a delisted key takes none.
249
- - Server-mediated branch publish (ADR 0055 / [#1025](https://example.com/builders#/task/1025) — lets a dev box with no GitHub push credential ship). Owner-gated like ship: `POST /tasks/:id/publish-branch` (`requireBuilder` + `gateTaskOwnership`, **NOT** `allowBoxScope` → a box-scoped session is `403 box_scope`, so the builder must `/builder-reauth` first; own 32 MB json parser for the base64 thin-bundle body, 20 MB decoded cap; the server pushes the branch + opens the PR + auto-merges with a server-side push credential — a **GitHub App** installation token (short-lived, repo-scoped; preferred, [ADR 0055](adr/0055-server-mediated-branch-publish.md) update / task 1028) or the `GITHUB_PUSH_TOKEN` PAT fallback; `503 push_unconfigured` when neither is set; `400 tip_mismatch` if the bundle tip ≠ the claimed `head_sha`) and `GET /tasks/:id/publish-status?branch=…` (polls PR + deploy-prod state derived live from GitHub). `branch` is optional: without it the server finds the task's own PR by its `task <id>: …` title, and the answer names the `branch` it used and its `branch_source` (`query` / `task_pr`, or `null` with a `branch_hint` when there is no such PR) — never a `400` (task 1003764). When the PR is still open it also carries `merge_driver`, whose `reason` says why it has not merged; on a red PR `failing_tests` names the failing unit tests (read from the `unit-report` commit status the `unit` workflow posts) and `checks_unreadable: true` says the check list is blind because the App lacks `Checks: read` — so an empty `checks[]` is never read as "nothing failed" (task 1003988, ADR 0301). Because the server's credential is the PR's AUTHOR, publish also **assigns the PR to the task's claim holder** and names them in the body (`modules/lifecycle/pr-assign.js`, task 1003991) — best-effort: the 201 carries `pr_assignee` (the login, or `null` when GitHub would not assign it) and a failed assignment never fails the publish. `ship.js` uses these in `ci` mode only when `pushVia()` resolves to server (a box, or `<PREFIX>_PUSH_VIA_SERVER=1`); a laptop with `gh` keeps the local push path unchanged. Implementation in `src/bongos/github-push.js`.
248
+ - Server-mediated branch publish (ADR 0055 / [#1025](https://example.com/builders#/task/1025) — lets a checkout with no GitHub push credential ship). Owner-gated like ship: `POST /tasks/:id/publish-branch` (`requireBuilder` + `gateTaskOwnership`; own 32 MB json parser for the base64 thin-bundle body, 20 MB decoded cap; the server pushes the branch + opens the PR + auto-merges with a server-side push credential — a **GitHub App** installation token (short-lived, repo-scoped; preferred, [ADR 0055](adr/0055-server-mediated-branch-publish.md) update / task 1028) or the `GITHUB_PUSH_TOKEN` PAT fallback; `503 push_unconfigured` when neither is set; `400 tip_mismatch` if the bundle tip ≠ the claimed `head_sha`) and `GET /tasks/:id/publish-status?branch=…` (polls PR + deploy-prod state derived live from GitHub). `branch` is optional: without it the server finds the task's own PR by its `task <id>: …` title, and the answer names the `branch` it used and its `branch_source` (`query` / `task_pr`, or `null` with a `branch_hint` when there is no such PR) — never a `400` (task 1003764). When the PR is still open it also carries `merge_driver`, whose `reason` says why it has not merged; on a red PR `failing_tests` names the failing unit tests (read from the `unit-report` commit status the `unit` workflow posts) and `checks_unreadable: true` says the check list is blind because the App lacks `Checks: read` — so an empty `checks[]` is never read as "nothing failed" (task 1003988, ADR 0301). Because the server's credential is the PR's AUTHOR, publish also **assigns the PR to the task's claim holder** and names them in the body (`modules/lifecycle/pr-assign.js`, task 1003991) — best-effort: the 201 carries `pr_assignee` (the login, or `null` when GitHub would not assign it) and a failed assignment never fails the publish. `ship.js` uses these in `ci` mode only when `pushVia()` resolves to server (no authenticated `gh` on the machine, or `<PREFIX>_PUSH_VIA_SERVER=1`); a machine with `gh` keeps the local push path unchanged. Implementation in `src/bongos/github-push.js`.
250
249
  - Archon monitoring reads backing the `/watch` page (ADR 0036): `GET /builders/roster`, `GET /grades/by-builder[?days=N]` (**archon-only**, [#726](https://example.com/builders#/task/726) — per-builder grade breakdown for the MARKS section; the project-wide aggregate stays public at `/public/grades`), `GET /audit-log`, `GET /override-requests`, `GET /access-requests`, `GET /security/reports`. Personal-prefs writes backing `/settings`: `GET/PATCH /me/skill-prefs`, `PATCH /me/disciplines`.
251
- - Per-builder "needs" + own Gemini key ([ADR 0073](adr/0073-builder-needs-signal-and-byok-gemini-key.md), [#1013](https://example.com/builders#/task/1013)): `GET /me` now also carries `needs` ({items, action_needed_count} — the consistent "the system needs an input from you" signal; `modules/builder-settings/builder-needs.js` is the SSOT — carved out in BV1.R80, resolved by `GET /me` via the `builder-settings` kernel port — rendered by the hall Standing card + the CLI `printNeedsNudge`). The own-key (pragmatic BYOK) endpoints, all own-scoped: `GET /me/art-key/own` (masked meta — last4 only), `PUT /me/art-key/own` (validate-on-save via a Google list-models call → encrypt with `src/bongos/secret-box.js` → store; `503 storage_not_configured` until `BUILDER_SECRET_KEY` is set, `422 invalid_key` on a bad key), `DELETE /me/art-key/own`. The existing `GET /me/art-key` (shared-key box-sync delivery) is extended to also deliver the decrypted own key, which `scripts/gds/fetch-art-key.js` syncs into the local `gemini_api_key` slot (own key wins in the `gen_api.py` cascade).
250
+ - Per-builder "needs" + own Gemini key ([ADR 0073](adr/0073-builder-needs-signal-and-byok-gemini-key.md), [#1013](https://example.com/builders#/task/1013)): `GET /me` now also carries `needs` ({items, action_needed_count} — the consistent "the system needs an input from you" signal; `modules/builder-settings/builder-needs.js` is the SSOT — carved out in BV1.R80, resolved by `GET /me` via the `builder-settings` kernel port — rendered by the hall Standing card + the CLI `printNeedsNudge`). The own-key (pragmatic BYOK) endpoints, all own-scoped: `GET /me/art-key/own` (masked meta — last4 only), `PUT /me/art-key/own` (validate-on-save via a Google list-models call → encrypt with `src/bongos/secret-box.js` → store; `503 storage_not_configured` until `BUILDER_SECRET_KEY` is set, `422 invalid_key` on a bad key), `DELETE /me/art-key/own`. The existing `GET /me/art-key` (shared-key delivery) is extended to also deliver the decrypted own key, which `scripts/gds/fetch-art-key.js` syncs into the local `gemini_api_key` slot (own key wins in the `gen_api.py` cascade).
252
251
  - Builder memory (ADR 0024 cloneable memory + ADR 0026 BFG). Self-scoped (owner is always `req.builder.id`, never input): `POST /memory/sync` (push own memory; own 64 MB parser, vs the global 64 KB), `GET /memory/files`, `GET /memory/file`, `DELETE /memory/file` (the BFG delete affordance, 6C.2). Cross-builder (input-owner) endpoints — all privileged + audit-logged fail-closed: `GET /memory/builders/:id/files`+`/file` (**archon forensic** read), `GET /memory/bfg/builders/:id/files`+`/file` (**BFG-principal only**, 6B.1 read), and the one cross-builder WRITE `POST /memory/builders/:id/bfg-write` (**BFG-principal only**, kill-switched `BFG_WRITE_ENABLED` default-OFF, namespaced `bfg/`, attributed `author: BFG`, write+audit in one tx — dreams + transparent corrections, 6C.1/6C.2).
253
252
  - Session corpus (BFG evaluator — 6D, ADR 0027): `POST /sessions` (upload own session digest; own **4 MB** json parser — capped, vs the global 64 KB — and the digest is per-turn metadata + short scrubbed snippets, never raw transcript content), `GET /sessions/mine` (self-serve), `GET /sessions/search` (cross-builder, **archon-gated** — the BFG-principal stand-in; the principal now exists (6C.1) and the *memory* cross-reads moved to it (6B.1), but these *session* reads still use the archon stand-in pending a retrofit, + audit_log row per read), `GET /sessions/:id` (own self-serve; cross-builder needs archon + audit; non-owner → 404).
254
253
  - Public (no auth, `Cache-Control: max-age=60`): `/public/versions`, `/public/progress`, `/public/cost-summary`, `/public/recent-shipped`, `/public/leaderboard`, `/public/grades[?days=N]`, `/public/tasks/:id` ([#604](https://example.com/builders#/task/604) — single-task resolver for `#NNN` doc deep-links; `title`/`value_summary`/`visual_url` returned **only** for `shipped`|`confirmed` work and withheld for in-flight tasks so unshipped/security task names can't leak; the status dashboard renders it at `#/task/:id`), `/public/ref-ids` ([#653](https://example.com/builders#/task/653) — `{tasks:[ids]}`, ids only, token-free; the doc-ref linkifier + its CI gate consult it to decide which bare `#NNN` are real task refs). These power `status.example.com`.