@bongos/core 1.17.0 → 1.17.1
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 +154 -83
- package/CLAUDE.md +11 -238
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +4 -4
- package/clients/bongos-client/bongos-client.global.js +14 -1
- package/clients/bongos-client/index.cjs +14 -1
- package/clients/bongos-client/index.d.ts +19 -2
- package/clients/bongos-client/index.mjs +11 -1
- package/clients/bongos-client/package.json +1 -1
- package/docs/adr/0133-github-app-manifest-onboarding-signin.md +2 -0
- package/docs/adr/0134-private-first-npm-distribution.md +20 -6
- package/docs/adr/0135-module-upstream-submission-interim-queue.md +34 -0
- package/docs/adr/README.md +5 -4
- package/docs/api/openapi.json +315 -6
- package/docs/api-reference.md +10 -5
- package/docs/architecture.md +1 -1
- package/docs/file-map.md +2 -2
- package/docs/modules-contract.md +2 -0
- package/docs/recipes/instance-deploy.md +4 -4
- package/docs/recipes/instance-first-run.md +51 -0
- package/docs/recipes/packaging-the-core.md +5 -5
- package/docs/recipes/private-npm-distribution.md +22 -12
- package/docs/recipes/self-host.md +1 -1
- package/docs/recipes/upgrading-the-core.md +8 -8
- package/migrations/core_191_module_submissions.sql +45 -0
- package/modules/autonomy/CLAUDE.md +11 -45
- package/modules/builder-settings/CLAUDE.md +11 -54
- package/modules/dev-box/CLAUDE.md +11 -34
- package/modules/discord/CLAUDE.md +11 -46
- package/modules/economy/CLAUDE.md +11 -44
- package/modules/grading/CLAUDE.md +11 -38
- package/modules/hall-ui/CLAUDE.md +11 -57
- package/modules/hall-ui/public/goals-page.js +227 -22
- package/modules/hall-ui/public/goals.html +16 -0
- package/modules/hall-ui/public/new-project-wizard.js +5 -4
- package/modules/ideas/CLAUDE.md +11 -33
- package/modules/lifecycle/CLAUDE.md +11 -59
- package/modules/lifecycle/routes/goals.js +80 -0
- package/modules/memory/CLAUDE.md +11 -36
- package/modules/onboarding/CLAUDE.md +11 -51
- package/modules/security/CLAUDE.md +11 -40
- package/modules/sessions/CLAUDE.md +11 -35
- package/modules/status-ui/CLAUDE.md +11 -43
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/create.html +58 -12
- package/public-landing/index.html +40 -21
- package/scripts/gds/CLAUDE.md +11 -140
- package/scripts/gds/claude-materialize.js +151 -18
- package/scripts/gds/claude-md-template.js +84 -0
- package/scripts/gds/cli-lib.js +112 -2
- package/scripts/gds/gen-api-client.js +10 -10
- package/scripts/gds/init.js +53 -13
- package/scripts/gds/migration-namespace.js +1 -1
- package/scripts/gds/module.js +118 -4
- package/scripts/gds/npmrc.js +93 -0
- package/scripts/gds/onboard.js +13 -24
- package/scripts/gds/package-core.js +56 -14
- package/scripts/gds/provision.js +102 -17
- package/scripts/gds/seed-adopt-tasks.js +1 -1
- package/scripts/gds/status.js +6 -1
- package/scripts/gds/task.js +166 -0
- package/scripts/gds/upgrade.js +58 -18
- package/src/bongos/CLAUDE.md +11 -60
- package/src/bongos/auth.js +11 -0
- package/src/bongos/module-submissions.js +56 -0
- package/src/bongos/route-rank-check.js +6 -0
- package/src/bongos/routes/CLAUDE.md +11 -39
- package/src/bongos/routes/auth.js +6 -1
- package/src/bongos/routes/modules.js +123 -21
- package/src/instance-config.js +1 -1
- package/src/module-api.js +13 -1
- package/tests/claude_materialize.mjs +105 -0
- package/tests/device_flow_start.mjs +73 -0
- package/tests/first_run_engine_check.mjs +79 -0
- package/tests/goal_membership_requests.mjs +33 -3
- package/tests/hook_user_agents.mjs +62 -0
- package/tests/init.mjs +59 -6
- package/tests/instance_mismatch_guard.mjs +101 -0
- package/tests/module_cli.mjs +130 -2
- package/tests/module_submissions.mjs +135 -0
- package/tests/npmrc.mjs +105 -0
- package/tests/onboard.mjs +49 -0
- package/tests/package_core.mjs +68 -22
- package/tests/platform_boot.mjs +5 -2
- package/tests/provision.mjs +107 -8
- package/tests/provisioning_github_app_routes.mjs +29 -2
- package/tests/served_paths_shipped.mjs +2 -2
- package/tests/task_cli.mjs +99 -0
- package/tests/upgrade.mjs +89 -25
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
- **Status:** Accepted — publish path + private-by-default config built in task 2089; the real publish is a gated owner step.
|
|
4
4
|
- **Date:** 2026-07-07
|
|
5
5
|
- **Deciders:** example-owner (owner/Archon), Claude. Owner asked to have the npm/CLI onboarding commands ready but kept private pre-launch, choosing paid private npm over the free GitHub Packages option.
|
|
6
|
-
- **Task:** [#2089](https://example.com/builders#/task/2089) · **Goal:** 35 (project-creation flow), criterion 165.
|
|
6
|
+
- **Task:** [#2089](https://example.com/builders#/task/2089) (publish path + private config) · [#2092](https://example.com/builders#/task/2092) (the `@cloudbongos` → `@bongos` scope rename + first real publish) · **Goal:** 35 (project-creation flow), criterion 165.
|
|
7
7
|
- **Refines:** [#2025](https://example.com/builders#/task/2025) (the 2026-07-04 *public* unscoped `cloudbongos` decision — now the launch-time go-public step, not superseded) · **Builds on:** [ADR 0108](<redacted>.md) (core-as-npm-dep), [#2053](https://example.com/builders#/task/2053) (vendored-tarball install).
|
|
8
8
|
|
|
9
9
|
## Context
|
|
10
10
|
|
|
11
|
-
A new owner stands up a standalone instance that depends on `@
|
|
11
|
+
A new owner stands up a standalone instance that depends on `@bongos/core` (ADR 0108), and external programs call the API through `@bongos/client` (ADR 0118). Today neither is on a registry: `package-core.js` produces a real, installable `.tgz`, and provisioning **vendors that tarball** into each instance repo (a `file:` pin `npm ci` resolves — task 2053). That works, but the onboarding experience isn't the clean `npm install @bongos/core` an owner expects, and the client's on-ramp doc referenced a nonexistent "internal registry."
|
|
12
12
|
|
|
13
13
|
The owner wants the npm/CLI commands **ready and usable pre-launch, but not public** — Cloud Bongos shouldn't be installable by the general public before launch. Two prior decisions bear on this: the *public* install name was set to the unscoped **`cloudbongos`** (task 2025, deferred), and the `bongos` + ~10 defensive npm orgs were reserved under the owner's login.
|
|
14
14
|
|
|
@@ -16,15 +16,17 @@ One npm rule forces the shape: **unscoped packages are always public — only *s
|
|
|
16
16
|
|
|
17
17
|
## Decision
|
|
18
18
|
|
|
19
|
-
Publish **`@
|
|
19
|
+
Publish **`@bongos/core`** and **`@bongos/client`** as **scoped, private** npm packages during pre-launch, on the owner's **paid** `bongos` npm org — the **canonical** brand org.
|
|
20
|
+
|
|
21
|
+
> **Scope correction (task 2092).** The publish path first shipped (task 2089) under the `@cloudbongos` scope, matching the name the repo already used. But when publishing, the paid org turned out to be **`bongos`** (the canonical brand the owner put on Teams), while `cloudbongos` — one of the ~10 defensive orgs — was left on the free tier. Since a private scoped publish requires the scope to match a *paid* org, and `@bongos` is the intended canonical brand anyway, we **renamed the core + client package identity `@cloudbongos/*` → `@bongos/*`** (a consistent rename across the build + vendored-install flow, verified by the test suite) rather than pay for a second org. Existing vendored consumers keep their pinned `@cloudbongos/core` tarball; only new scaffolds use `@bongos`.
|
|
20
22
|
|
|
21
23
|
- **Private by default in code, not just by flag.** The synthesized core `package.json` (`buildPackageJson`) and the generated client `package.json` (`gen-api-client.js`) both carry `publishConfig: { access: "restricted" }`. `package-core.js --publish` additionally passes `--access restricted` on the CLI — belt-and-suspenders so a stray config edit can't silently make the pre-launch artifact public.
|
|
22
24
|
- **The publish is a gated owner step; the tooling holds no token.** `package-core.js --publish` **dry-runs** by default; the real, permanent publish needs `--publish --live` plus the owner's `npm login` and the org on a paid plan. npm owns auth — this repo never stores a token.
|
|
23
25
|
- **Consumers authenticate with an env-fed `.npmrc`:** `//registry.npmjs.org/:_authToken=<redacted> The token expands from the environment, so it never lands in a committed file. Only members of the paid org can install.
|
|
24
26
|
- **The vendored-tarball path stays as the working default/fallback.** Rewiring provision/init to auto-pull the pinned core from the registry (env-fed token on the control plane) is a follow-up — it touches a proven, protected control-plane flow and isn't required for "commands ready + private." Publishing privately + documented auth already delivers a real `npm install`.
|
|
25
|
-
- **Going public at launch is one step**, chosen then: either `npm access public @
|
|
27
|
+
- **Going public at launch is one step**, chosen then: either `npm access public @bongos/core` (keeps the scoped name), or execute task 2025 to *also* publish the unscoped vanity `cloudbongos` for `npm install -g cloudbongos`. Task 2025 is therefore the launch-time go-public leg, not replaced by this.
|
|
26
28
|
|
|
27
|
-
The daily command stays `bongos` regardless of package name (the `bin` mapping in the synthesized package.json is name-independent), so `npm install -g @
|
|
29
|
+
The daily command stays `bongos` regardless of package name (the `bin` mapping in the synthesized package.json is name-independent), so `npm install -g @bongos/core` → `bongos init` works today, and the eventual public `cloudbongos` name changes nothing for users.
|
|
28
30
|
|
|
29
31
|
## Why paid private npm over the free GitHub Packages alternative
|
|
30
32
|
|
|
@@ -32,7 +34,19 @@ The owner chose it deliberately, knowing the trade-off. GitHub Packages is free
|
|
|
32
34
|
|
|
33
35
|
## Consequences
|
|
34
36
|
|
|
35
|
-
- Cost: a paid `
|
|
37
|
+
- Cost: a paid `bongos` npm org seat per person who needs to install during pre-launch. Gone at launch.
|
|
36
38
|
- Owner-only steps (out of the build session; capture as a blocker if they gate verification): put the org on a paid plan; generate an npm access token (automation — read-write to publish, read for CI/provision installs); run `package-core.js --publish --live` (permanent; login + 2FA).
|
|
37
39
|
- Follow-up: wire provision/init to install the pinned core from the private registry (env-fed `NPM_TOKEN`), keeping the vendored tarball as fallback.
|
|
38
40
|
- Full operator runbook — org, plan, token, publish, consumer `.npmrc`, and the go-public flip — is [`docs/recipes/private-npm-distribution.md`](../recipes/private-npm-distribution.md).
|
|
41
|
+
|
|
42
|
+
## Update — the provision/init registry path is wired (task 2090)
|
|
43
|
+
|
|
44
|
+
The follow-up above shipped. The wiring is **opt-in and fail-soft**, so the proven vendored-tarball path stays the default until the registry path is proven end-to-end on a real greenfield standup:
|
|
45
|
+
|
|
46
|
+
- **One source of truth**: `scripts/gds/npmrc.js` owns the env-fed `.npmrc` body (`@bongos:registry=…` + `_authToken=<redacted> and the gates (`hasNpmToken`, `registryCoreRequested`). The token expands from the environment at install time, so nothing secret is committed.
|
|
47
|
+
- **init** (`--registry-core`): keeps the caret pin and writes the `.npmrc`; with `NPM_TOKEN` set it also generates `package-lock.json` from the registry. Mutually exclusive with `--vendor-core`.
|
|
48
|
+
- **provision** (standalone scaffold): opt-in via `PROVISION_CORE_FROM_REGISTRY=1`; when a token is present it scaffolds via `init --registry-core` (no tarball build), and when the flag is set but no token is present it **falls back to the vendored tarball** — a tokenless standup still works.
|
|
49
|
+
- **upgrade** (`--to <v> --registry`): pins the exact registry version + ensures the `.npmrc` instead of vendoring; mutually exclusive with `--from`.
|
|
50
|
+
- **Verified — both legs green (task 2118, 2026-07-08).**
|
|
51
|
+
- *Tokenless:* a scaffolded instance with the caret pin + env-fed `.npmrc` and **no** `NPM_TOKEN` 404s cleanly on `npm install` (`E404 … @bongos/core … could not be found or you do not have permission to access it`) — it fails closed.
|
|
52
|
+
- *With token (the leg blocker 71 tracked):* a real `bongos init --registry-core` scaffold with a valid **read-only** token (paid `bongos` org) resolved `@bongos/core@1.17.0` from `registry.npmjs.org` and generated a reproducible `package-lock.json` (`resolved: https://registry.npmjs.org/@bongos/core/-/core-1.17.0.tgz`). The registry pull works with a token and fails closed without one — so the opt-in registry path is proven end-to-end, and blocker 71 is resolved. The vendored tarball remains the default until an owner chooses to flip a live provisioner to `PROVISION_CORE_FROM_REGISTRY=1`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 0135 — Module upstream submission: the review queue lives in the submitting instance's own DB (interim)
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-07-08
|
|
5
|
+
- **Task:** [#1770](https://example.com/builders#/task/1770) (BONGOS-V1) — `bongos module submit` + `POST /modules/:key/submit`.
|
|
6
|
+
- **Builds on:** [ADR 0107](<redacted>.md) (the upstreaming decision — submit → review → accept), [ADR 0103](<redacted>.md) (the core extracted to `github.com/example-owner/cloud-bongos`, private), [ADR 0100](<redacted>.md) §3 (per-repo GDS — each project runs its own DB).
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
ADR 0107 §1 describes `bongos module submit <key>` as packaging a module + attribution + a DCO sign-off and filing it "into a rank-gated review queue **on the core**" — without granting the submitting instance write access to core. Task 1770 depends on R88 (the core extraction, [#1727](https://example.com/builders#/task/1727), shipped) and originally also listed R85 (Mercury, the forcing-function second consumer). R85 was abandoned ([#1689](https://example.com/builders#/task/1689)): the two-repo model it was meant to prove is already proven end-to-end by a different standalone instance, so it dropped as a dependency.
|
|
11
|
+
|
|
12
|
+
That leaves a gap task 1770 has to resolve to be buildable at all: **what does "on the core" concretely mean today?** The extracted core repo (`github.com/example-owner/cloud-bongos`) is a **private git repository, not a running service** — it has no HTTP API, no reachable review-queue endpoint, and (per ADR 0100 §3) each Cloud Bongos project runs its **own** GDS database; there is no shared cross-repo GDS a submission could land in. Building a real cross-repo network submission would mean inventing a core-side API that doesn't exist yet, authenticating an instance against it, and defining a wire format — none of which is this task's job (that's ADR 0107 §8's *pipeline* stage, gated on task 1771, "core-side review & accept flow," which is still `backlog`).
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
**The review queue is a table (`module_submissions`, migration `<redacted>.sql`) in THIS instance's own Bongos DB** — the same database the module was built against — not a network call to a remote core.
|
|
17
|
+
|
|
18
|
+
- `bongos module submit <key>` packages the module (manifest + a full base64 file snapshot + the recorded DCO sign-off), re-runs the ADR 0107 §4 AGPL/publish pre-check, and **refuses (exit 1)** if the pre-check fails or no sign-off is on file — exactly the ADR's "done when" bar.
|
|
19
|
+
- The packaging + submission both flow through `POST /api/gds/modules/:key/submit` (rank: metic+archon) — the SAME route the hall "Modules" tab's submit button calls (ADR 0107 §9), so there is one authoritative implementation, not a CLI copy and a UI copy. The route re-runs the pre-check against **its own** on-disk copy of the module (the server's deployed checkout), which is the real gate — a builder's local pre-check pass is a courtesy/fail-fast, not proof, if their changes aren't committed + shipped + deployed yet.
|
|
20
|
+
- `GET /api/gds/modules/submissions` (rank: metic+archon) lists the queue — the surface task 1771's core-side review UI reads from.
|
|
21
|
+
|
|
22
|
+
This satisfies the letter of ADR 0107 §1 ("rank-gated review queue," "refuses if the pre-check fails," "captures attribution + sign-off") using infrastructure that actually exists today, and is forward-compatible: when a real core-side receiving endpoint exists (task 1771 or later), filing becomes a network hop to that endpoint instead of a local `INSERT`, with **no change to the CLI's or the hall UI's caller-facing contract** (both already only see `{ signed_by }` in, `{ submission }` out).
|
|
23
|
+
|
|
24
|
+
## Alternatives considered (rejected)
|
|
25
|
+
|
|
26
|
+
- **A live HTTP call to a core-hosted endpoint.** Rejected for now — no such endpoint exists; inventing one is task 1771's job (core-side review & accept), not the submit side's. Building it here would mean guessing at a contract 1771 might not honor.
|
|
27
|
+
- **A GitHub Issue/PR against `github.com/example-owner/cloud-bongos` via the GitHub API.** Considered (the ADR's "fork-and-PR remains a fallback" framing), but rejected as the *native* path: it would silently degrade every submission to the explicitly-non-canonical fallback, loses the structured queue (status/reviewed_by/reviewed_at) task 1771 needs, and the repo is private — the CLI/server have no standing GitHub credential scoped for that repo today.
|
|
28
|
+
- **A dry-run-only client (mirroring `do-api.js`'s DigitalOcean/Cloudflare pattern) that just logs "would submit" and writes a local file.** Rejected — a local orphan file is a worse "review queue" than a real, queryable DB table the instance already operates and backs up; it would need its own bespoke listing/status mechanism that duplicates what Postgres already gives us for free.
|
|
29
|
+
|
|
30
|
+
## Consequences
|
|
31
|
+
|
|
32
|
+
- **The queue is scoped per-instance, not global**, until a real cross-repo channel exists. Two instances submitting modules with the same key don't collide (each queue is local), which is fine for now (there is exactly one non-abandoned instance, this one) but is a real gap once a second live instance exists — revisit when task 1771 is scoped.
|
|
33
|
+
- **Task 1771** ("core-side review & accept flow") inherits a concrete shape to review against: read `module_submissions` (`status='pending'`), accept/reject with `reviewed_by`/`reviewed_at`/`review_notes`, and the accept action still has to do the ADR 0107 §6 checklist (land in `modules/<key>/`, `default:false`, allowlist, record attribution) — this ADR does not change any of that.
|
|
34
|
+
- **`scripts/gds/module.js` is required directly by `src/bongos/routes/modules.js`** (both resolve under `resolveCoreRoot()` — see `scripts/gds/CLAUDE.md`) to reuse `checkModulePublishability`/`recordSignOff`/`readSignOff`/`<redacted>` rather than re-implementing the pre-check server-side. `module.js` never statically requires the CLI session/fetch layer (`cli-lib.js`) — only `cmdSubmit`'s network leg lazy-requires it, and only when a human/agent actually runs the CLI — so this stays safe to load into the always-on server process (the same discipline `llm-cache.js` documents for the same reason).
|
package/docs/adr/README.md
CHANGED
|
@@ -175,10 +175,10 @@ This keeps the decision history honest and traceable.
|
|
|
175
175
|
| 0105 | [Instance/product seed data out of the core migration path — OTB scope-truth (`versions`/`goals`/`done_when_criteria`) embedded in ~16 core migrations crash-looped a vanilla self-host on a fresh DB (FK violations) or polluted it with OTB's versions/goals; task [#1264](https://example.com/builders#/task/1264) patched only the two crashers (172/173 `DO`-block guards). Structural fix (task [#1704](https://example.com/builders#/task/1704)): the 10 PURE seed files move to a declared `migrations/instance/` dir; `scripts/migrate.sh` applies instance seeds only for the OWNING instance and DEFAULTS OFF (opt-in `GDS_APPLY_INSTANCE_SEEDS=1`; hard-off under the now-revived `MEDUSA_GDS_ONLY`), so a vanilla OR branded self-host never inherits them. Both dirs merge-sort by basename so the global numeric order is preserved (018 before 019) and an OTB from-scratch replay is byte-identical (5 versions / 11 goals / 43 criteria). The unavoidable seed blocks in the mixed files 003/006 guard in place on the `app.seed_instance_data` GUC (achievements + the generic `bfg`/`discord_bot` system principals kept). A `selfhost-boot.yml` CI gate (authored; landing gated on blocker 52 — the App can't merge `.github/workflows/*`) boots a fresh `postgres:16` and asserts a clean vanilla boot with zero OTB rows, closing the C9-style un-gated-proof gap. Verified against a throwaway DB both ways.](<redacted>.md) | Cloud Bongos / architecture |
|
|
176
176
|
| 0106 | [Goal owner/manager authority — refines ADR 0086's membership roles into a fixed hierarchy (task [#1735](https://example.com/builders#/task/1735)): the goal OWNER is `goals.created_by` (a column, not a role row — set at create, moved only by the new owner-only `POST /goals/:id/transfer`; the new owner must be Metic+); MANAGER is the `goal_members.role='lead'` value (rendered "manager" in the hall), owner-equivalent EXCEPT demotion (owner-only) and transfer; the owner's row is never demotable (`owner_role_fixed`), enforced in the pure ordered gate (`authorizeMemberRoleChange`, the authorizeGoalTaskCreate precedent) AND atomically inside the UPDATE (`setGoalMemberRole`). Creators backfill as leads via module migration `<redacted>` — deliberately NO earliest-member fallback (fail-closed; ownerless migration-160 system goals stay Archon-run). No goal delete — archive unchanged. Replaces the task's original leads-as-equals "never below one lead" invariant with the simpler fixed-owner model.](<redacted>.md) | GDS / goal hierarchy |
|
|
177
177
|
| 0107 | [Module upstreaming — instances contribute modules into the shared core catalog (task [#1766](https://example.com/builders#/task/1766)). The REVERSE of the ADR 0100/0103 distribution arrow: an instance (Example, Mercury) builds a feature module and submits it for approval to be merged INTO the shared Cloud Bongos core, credited to author + origin project; accepted modules land in core `modules/<key>/` **default-off**, added to the `isPublishable()` allowlist, and every instance may opt in. Decisions: (1) platform-native `bongos module submit` → a rank-gated core review queue (NOT instance write-access to core; fork-and-PR is a fallback); (2) attribution is PORTABLE METADATA — `module.json` gains `author`/`origin`/`license`/`maintainer` (a record, not a cross-GDS foreign key, since each repo has its own identities per [ADR 0100](<redacted>.md) §3); (3) reward = CREDIT NOW, money later (drachmae/cross-instance payout deferred to a separate economics call); (4) AGPL-IN with a DCO-style sign-off, NO CLA ([ADR 0065](<redacted>.md)), `isPublishable()` pre-check at submit rejects un-AGPL-able content (game/art/copyright); (5) SECURITY REVIEW is a hard accept-gate — a contributed module runs in-process in every enabling instance, no sandbox yet; (6) default-off + the publish-allowlist add IS the accept action; (7) maintenance/deprecation policy for orphaned modules (coreVersion compat). Sequenced: manifest fields + module-owned test discovery in core CI + submit-time pre-check buildable NOW; the submit→review→accept→catalog pipeline depends on R88 (extract core, [#1727](https://example.com/builders#/task/1727)) + R85 (Mercury, [#1689](https://example.com/builders#/task/1689)). Renames away from the taken "contribution seam" term (that is the in-process UI hook).](<redacted>.md) | Cloud Bongos / architecture |
|
|
178
|
-
| 0108 | [Instance composition topology — CONFIGURABLE-ROOT: the core is consumed as a versioned npm dependency (task [#1870](https://example.com/builders#/task/1870), BV1.R115). Resolves the composition MECHANISM that [ADR 0062](<redacted>.md) §10 / [ADR 0100](<redacted>.md) §1 / [ADR 0103](<redacted>.md) §2 each left open (they are mechanism-neutral — "installs a pinned core + layers on top"; 0103's R88 record deferred "the topology/instance paths the scaffold populates on `init`"). A 2026-07-03 owner review chose configurable-root over the earlier clone-and-layer draft, weighting SCALABILITY over least-resistance: upgrades = O(1)-per-instance version bump (not O(instances×drift) merges); the boundary is enforced by the PACKAGE (can't rot under many agents) not convention+CI; a dependency forces a real core API; instance repos stay small; "root is a parameter" is the precondition for multi-instance/hosted. Decisions: (1) core installed as `@
|
|
178
|
+
| 0108 | [Instance composition topology — CONFIGURABLE-ROOT: the core is consumed as a versioned npm dependency (task [#1870](https://example.com/builders#/task/1870), BV1.R115). Resolves the composition MECHANISM that [ADR 0062](<redacted>.md) §10 / [ADR 0100](<redacted>.md) §1 / [ADR 0103](<redacted>.md) §2 each left open (they are mechanism-neutral — "installs a pinned core + layers on top"; 0103's R88 record deferred "the topology/instance paths the scaffold populates on `init`"). A 2026-07-03 owner review chose configurable-root over the earlier clone-and-layer draft, weighting SCALABILITY over least-resistance: upgrades = O(1)-per-instance version bump (not O(instances×drift) merges); the boundary is enforced by the PACKAGE (can't rot under many agents) not convention+CI; a dependency forces a real core API; instance repos stay small; "root is a parameter" is the precondition for multi-instance/hosted. Decisions: (1) core installed as `@bongos/core`, host content resolved via an injected instance-root — two resolvers `resolveInstanceRoot()`/`resolveCoreRoot()` in the existing `src/instance-config.js`, defaulting to repo-root TODAY (byte-identical) and flipping `resolveCoreRoot()` to `node_modules` when packaged, no further per-site edits; the two directions are OPPOSITE (config→instance-root; the `infra/` installers `serve-internal` serves→core-root); (2) `package-core` emits a real `package/`-rooted npm package (`main`→platform-server, `bin`→bongos, deps pruned to express/pg/undici), `isPublishable()` stays the single core definition; (3) `.claude/` is MATERIALIZED into the instance by `bongos init`/`upgrade` (Claude Code reads it from the project root, never node_modules) + the ~30 skills/hooks rewritten `node scripts/gds/…`→`bongos …` (skills already ship in-core; hooks+settings are already "topology"); (4) `bongos upgrade` (R86) is an npm version bump, deploy flips `git reset --hard`→`npm ci` (Docker/`platform-server`/`selfhost-boot` already model it); (5) cross-repo migration numbering gets a namespacing + lineage discipline (two repos both adding `NNN_*.sql` collide on the shared `schema_migrations` stem key); (6) land Mercury-first — step-1 resolvers now (zero-change), package+`.claude` prove on greenfield R85, Example splits last (R89) with the trust-boundary path sites + the two-root guardrail fix (fitness.js/gen-repo-map/gen-file-map all hardcode REPO_ROOT) behind a security review. Runtime-library half is SMALL/de-risked (~8–11 readers; entrypoint seam `mountInternalSurfaces`/`buildPlatformApp`, two-roots loader, single-sourced partition already exist); the real work is delivery+ops (§3/§4/§5) — the accepted "more pain now". Clone-and-layer/submodule/monorepo rejected. Informs R85 ([#1689](https://example.com/builders#/task/1689)), R86 ([#1690](https://example.com/builders#/task/1690)), R89 ([#1728](https://example.com/builders#/task/1728)). Docs amended (not superseded): file-map, architecture, packaging-the-core + self-host recipes, ADR 0105, modules-contract; cross-refs in 0062/0100/0103/0098/branding-contract/CLAUDE.md. No migration, no new criterion; goal 26.](<redacted>.md) | Cloud Bongos / architecture |
|
|
179
179
|
| 0109 | [The GDS API is self-describing — a standards-compliant **OpenAPI 3.1** spec generated from the live route files + a hosted `/docs` site (task [#1918](https://example.com/builders#/task/1918), BONGOS-V1, Part A of the API-docs + `/api/gds`→`/api/bongos` rename). Replaces the hand-maintained, drifted `docs/routes-permissions.md` with `scripts/gds/gen-api-docs.js`, which reuses the SAME `route-rank-check.js` introspection the ship-time rank gate ([ADR 0016](<redacted>.md)) uses — so the docs enumerate exactly the routes the server mounts and cannot drift (the [ADR 0063](<redacted>.md) generate-and-`--check` pattern). Emits `docs/api/openapi.json` (each op carries its rank in `x-rank` + a `security` ref, path params, and best-effort request-body fields from the `validateOrRespond` schema; undocumented bodies are counted) + a generated `docs/api-reference.md`. A vendored Redoc bundle renders it at `/docs` on every instance's apex (`cloudbongos.com/docs` canonical), served from the SHARED `mountInternalSurfaces` so a self-hosted instance is self-documenting for free; `GET /api/gds` returns a machine-discovery index and the 404 hint names `/docs`. Introduces the single `src/bongos/api-prefix.js` prefix constant that Part B flips to `/api/bongos`. Freshness enforced by `gen-api-docs --check` in `fitness.js` + ship-time regen; full standard lint via `npm run docs:api:lint` (`@redocly/cli`, off the critical path). Rejected: annotation-based/hand-written specs (drift), CDN renderer (self-host convention/air-gap), redocly as a hard CI dep (lean project).](<redacted>.md) | GDS / API docs |
|
|
180
180
|
| 0110 | [Move whole-file generated navigation docs off the git graph (task [#1926](https://example.com/builders#/task/1926), BONGOS-V1, goal 24 build-pipeline-integrity). `docs/repo-map.md` (ADR 0063) + `docs/session-log-index.md` (ADR 0062 §8) were regenerated + **committed on every ship**, so parallel builder branches collided on them constantly (the [ADR 0082](<redacted>.md) reactive heal only cleans up after the fact). Applies the blessed `gen-atlas.js`/`atlas.json` precedent ([ADR 0094](<redacted>.md)): **gitignore** both whole files + regenerate them SERVER-SIDE at deploy (`deploy-prod.sh`, LOUD on failure → goal-24 `gcarry-pipe-loud`) and at `box-source-fetch` — a file not in git can never appear in a branch diff, so the conflict class is gone AT THE SOURCE. Both generators are DUAL-DUTY (each also refreshes a tracked block — the nested `CLAUDE.md` symbol blocks / the `CLAUDE.md` §13 snippet), so the CI freshness gate is SPLIT: `gen-*.js --check-block` (tracked block, still HARD) vs `--check-whole` (the gitignored file, no longer gated) — the adversarial-review fatal flaw was that the old single `--check` bundled both, so a naive relax would silently drop the block gate. `ship.js` drops the gitignored paths from its `git add` (an explicit `git add <ignored>` aborts the commit). Reactive heal (`git-merge-regen`, `.gitattributes`, ADR 0082 resolver) KEPT unchanged as a transition backstop; trust boundary untouched (no bot commits to main; no `.github/workflows/` change). Deferred to follow-ups: the OpenAPI spec (external consumers → bounded regen bot) + diagram renders (headless Chromium). Accepts: whole files fresh only post-deploy, not at merge.](<redacted>.md) | Build pipeline / generated artifacts |
|
|
181
|
-
| 0111 | [Instance hosting + domains as a Cloud Bongos module — spec of the new **`provisioning`** module boundary + the **self-host vs managed** split (task [#1485](https://example.com/builders#/task/1485), BONGOS-V1 goal 26; consolidates owner ideas [#345](https://example.com/builders#/idea/345) + [#303](https://example.com/builders#/idea/303)). Standing up a Cloud Bongos instance still leaves an irreducibly-manual infra checklist (`bongos init` files it as kickoff tasks); this ADR specs the module that automates it. **Distinct from [ADR 0092](<redacted>.md)'s `hosting`** (that brokers compute/storage *workloads*; this provisions an *instance's own* server/DB/domain/DNS/TLS) — takes the free key `provisioning` to avoid the collision. Decisions: (1) a portable `default:false` `provisioning` module owns only the web-tier request/state surface (`provisioning_instances`/`_intents`/`_events`, own+Archon rank-gated routes, a `provisioning.instanceState` port) — it holds **no** cloud tokens; (2) the web tier only **enqueues intents**, a new control-plane CLI `scripts/gds/provision.js` (sibling of `box.js`, sole token-holder, systemd-timer-drained, idempotent/dry-run-default) executes them — the [ADR 0031](<redacted>.md)/[ADR 0016](<redacted>.md) box precedent, with the DO+Cloudflare clients extracted to a shared control-plane location so `box.js`+`provision.js` reuse them without a module→module import; (3) provisioning composes the [`cloudbongos-standup.md`](../recipes/cloudbongos-standup.md) runbook (server co-tenant-or-droplet → DB → migrate → env → service → DNS → TLS → Caddy), with GitHub-OAuth-app + first-sign-in demarcated as guided/manual; (4) **bring-your-own-domain first** (registrar-API purchase deferred) + **Caddy on-demand ACME TLS** for new hosts (removes the manual Cloudflare Origin-Cert leg; existing apexes keep theirs); (5) **self-host and managed run the identical `@
|
|
181
|
+
| 0111 | [Instance hosting + domains as a Cloud Bongos module — spec of the new **`provisioning`** module boundary + the **self-host vs managed** split (task [#1485](https://example.com/builders#/task/1485), BONGOS-V1 goal 26; consolidates owner ideas [#345](https://example.com/builders#/idea/345) + [#303](https://example.com/builders#/idea/303)). Standing up a Cloud Bongos instance still leaves an irreducibly-manual infra checklist (`bongos init` files it as kickoff tasks); this ADR specs the module that automates it. **Distinct from [ADR 0092](<redacted>.md)'s `hosting`** (that brokers compute/storage *workloads*; this provisions an *instance's own* server/DB/domain/DNS/TLS) — takes the free key `provisioning` to avoid the collision. Decisions: (1) a portable `default:false` `provisioning` module owns only the web-tier request/state surface (`provisioning_instances`/`_intents`/`_events`, own+Archon rank-gated routes, a `provisioning.instanceState` port) — it holds **no** cloud tokens; (2) the web tier only **enqueues intents**, a new control-plane CLI `scripts/gds/provision.js` (sibling of `box.js`, sole token-holder, systemd-timer-drained, idempotent/dry-run-default) executes them — the [ADR 0031](<redacted>.md)/[ADR 0016](<redacted>.md) box precedent, with the DO+Cloudflare clients extracted to a shared control-plane location so `box.js`+`provision.js` reuse them without a module→module import; (3) provisioning composes the [`cloudbongos-standup.md`](../recipes/cloudbongos-standup.md) runbook (server co-tenant-or-droplet → DB → migrate → env → service → DNS → TLS → Caddy), with GitHub-OAuth-app + first-sign-in demarcated as guided/manual; (4) **bring-your-own-domain first** (registrar-API purchase deferred) + **Caddy on-demand ACME TLS** for new hosts (removes the manual Cloudflare Origin-Cert leg; existing apexes keep theirs); (5) **self-host and managed run the identical `@bongos/core` artifact** ([ADR 0108](<redacted>.md)) — the only differences are *whose* credentials the control plane holds and *who* operates it; managed = self-host + <redacted> infra + a central fleet control plane driving the same `bongos upgrade` channel, AGPL unchanged ([ADR 0065](<redacted>.md) §2, no open-core); (6) cheapest-first cost DNA (co-tenant $0 → dedicated droplet only when justified; snapshot-park idle; billing deferred); (7) self-host arm buildable now on the shipped configurable-root substrate, managed arm gated on R85 Mercury ([#1689](https://example.com/builders#/task/1689)) + R86 upgrade channel ([#1690](https://example.com/builders#/task/1690)). Seeds 8 follow-up tasks via `scripts/gds/seed-provisioning-tasks.js`.](<redacted>.md) | Cloud Bongos / architecture |
|
|
182
182
|
| 0112 | [Private goals — a `visibility` flag (`public` default \| `private`) + a two-sided `goal_membership_requests` queue (task [#1951](https://example.com/builders#/task/1951), BONGOS-V1). A goal manager can make a goal **private**; on a private goal non-members can't self-join — they are **invited** (a consented, pending row the invitee accepts from their inbox — distinct from the immediate `POST /goals/:id/members` add, kept byte-identical) or they **request** to join, approved by the goal's owner/manager/Archon (the [ADR 0106](<redacted>.md) `authorizeGoalManagement` wall reused wholesale). Both directions are one `goal_membership_requests` table keyed by `kind ∈ {invite, request}`, `status ∈ {pending, accepted, rejected, cancelled}`, with a **partial-unique `(goal_id, builder_id) WHERE status='pending'`** (the `access_requests` precedent — one open ask per person+goal). `GET /goals/inbox` returns each builder's actionable slice — invitations to them + join-requests on goals they own/manage + their own outstanding requests — powering a **home-page inbox**. **Private = join-gated, not hidden** (owner decision): a private goal still shows its title + progress; true concealment would need its own ADR. Purely additive (default `public` = today's open-join, unchanged) + revertible; ships in four phases (schema/db → routes → goals-page UI → home inbox). Rejected: fully-hidden goals (large filter surface for the stated intent), invite-as-force-add (owner asked for accept-from-inbox), two separate tables (shared lifecycle + mutual-exclusion index), requests on public goals (redundant — just join).](<redacted>.md) | GDS / goal hierarchy |
|
|
183
183
|
| 0113 | [Per-builder art sandbox (task [#885](https://example.com/builders#/task/885)): descope to the sprite/animation Studio only. Task 885's 8-part breakdown (walkable offline preview, static sandbox server + systemd unit, `cf-tunnel.js` multi-ingress, box-provisioning wiring, reset-to-main script, retire staging) went stale on two axes: (1) Parts A1/B were already shipped, one day after 885 was filed, under [ADR 0044](<redacted>.md) (real degraded-DB game server at `sandbox-<login>.example.com` over the existing box tunnel) + [ADR 0052](<redacted>.md) (game-only entry, every rank) — a better design than the static-file/offline-mode one 885 proposed; (2) the touches\[\] paths point at pre-carve locations (`public/game/...`, `src/bongos/cf-tunnel.js`, `src/bongos/routes/box.js`) that moved to `modules/game/...`/`scripts/gds/cf-tunnel.js`/`modules/dev-box/...` under the ADR 0083/0093 module carve. Part C (retire staging) also contradicts the standing 2026-05-29 defer-not-delete decision in `docs/recipes/staging-environment.md`. Keeps only the Studio — a sprite/animation reviewer at `/studio`, layered onto the *existing* sandbox as a static page under `modules/game/public/` (the `/art` gallery precedent in `src/preview-server.js`; no new server/tunnel/unit) that auto-discovers sprites from `manifest.json` and extracts the right-facing mirror logic from `Player.js` into a shared `spriteFacing.js` — filed as one new child task instead of re-minting the stale 8.](<redacted>.md) | Builder experience / dev box |
|
|
184
184
|
| 0114 | [Instance docs-discovery contract — pins how an agent locates its doc surfaces from config alone, with no hard-coded host. Three layers: (1) `config/branding.json` `domains.{publicOrigin,buildersOrigin,statusOrigin}` is the source of truth — the hall is the canonical doc-reading surface, `/docs` on `publicOrigin` renders the API reference, the status page is the off-box outage fallback; (2) `GET <publicOrigin>/api/bongos` (the machine-discovery index, + permanent `/api/gds` alias) is the zero-config entry point and now re-advertises `hall`/`status`/`public` from `branding().domains` so a second instance bootstraps its surfaces from a single unauthenticated call; (3) a resolution order for agents. Formalizes existing keys (no new required field); CLAUDE.md's preamble references it instead of hand-waving one config key.](<redacted>.md) | Portability / docs-discovery |
|
|
@@ -191,7 +191,7 @@ This keeps the decision history honest and traceable.
|
|
|
191
191
|
| 0117 | [API versioning + deprecation policy — the policy (R05) for goal 36's `api-versioning` criterion (task [#1992](https://example.com/builders#/task/1992), BONGOS-V1). The API (212 endpoints at `/api/bongos` + permanent `/api/gds` alias) had no version marker, no breaking-change definition, no deprecation window — any change could silently break a self-hoster/shipped Dev Box binary/status dashboard. Decision: (1) an explicit path segment `/api/bongos/v1/…` is the **contract** version, distinct from `info.version` (the package semver); (2) today's unversioned paths become **permanent aliases pinned to v1** — the load-bearing rule is *an unversioned path never silently jumps major versions*, so when v2 mounts at `/v2/` the alias stays on v1 and breaking changes reach only opted-in callers; (3) breaking = remove/rename/retype a field/endpoint/error-code, new required field, tightened validation, narrowed auth, changed default — non-breaking = additive fields/endpoints/optional-params + new error codes *within an existing ADR-0116 category*; (4) a superseded version lives **≥180 days** with RFC 8594 `Deprecation`/`Sunset` headers, the unversioned alias exempt (permanent); (5) the version is discoverable via OpenAPI `servers` + `x-api-version` + the machine-discovery index + `/docs`. **Policy only** — R06 ([#1993](https://example.com/builders#/task/1993)) mounts `/v1` + aliases the unversioned paths (day-one no-op). Rejected: header-based versioning (invisible in URL, un-curl-able), semver-in-path (too fine), reusing the package semver (conflates build with contract), sunsetting the unversioned alias (breaks un-updatable binaries).](<redacted>.md) | API contract / goal 36 |
|
|
192
192
|
| 0116 | [API error envelope + code taxonomy — the contract (R01) for goal 36's `api-error-envelope` criterion (task [#1988](https://example.com/builders#/task/1988), BONGOS-V1). An audit found **352 distinct `error:'…'` string literals** returned across 400/401/403/404/409/413/422/429/500/502/503 in ~4 different response shapes — a caller cannot write one error-handling path. Decision: **one envelope everywhere** — `{error:{code,message,details?}}` — where the specific `code` slug is **preserved** (a caller keying on `task_not_found` keeps working) and the 352 codes **collapse by category, not by discard**. A kernel registry `src/bongos/api-errors.js` defines ~12 canonical categories (each with a default HTTP status + `retriable` flag) and a pure/total `classify(code)` — an `OVERRIDES` table for the ~90 exceptions (auth codes, preconditions-as-conflicts, `*_too_large`) plus a suffix/prefix heuristic for the long tail (`*_failed`→internal, `*_not_found`→404, `bad_*`/`*_required`→400, `already_*`/`*_taken`→409, `*_unconfigured`→503); an unknown/future code falls back to `internal` (500). Ships the `ApiError` carrier + `envelope()` formatter + `statusForCode(code,override)` (a site keeps its current status; the sweep changes shape, not status). **Contract-only — nothing imports it yet, zero behaviour change.** R02 ([#1989](https://example.com/builders#/task/1989)) wires the middleware + `res.fail()`; R03 ([#1990](https://example.com/builders#/task/1990)) fails the build on a new hand-rolled flat error; R04 ([#1991](https://example.com/builders#/task/1991)) sweeps the ~219 handlers behind that gate. Rejected: flat `{error,message}` (today's mess, no room to grow), numeric codes (opaque), collapsing to 12 codes only (loses signal, breaks callers), per-module registries (defeats "one shape").](<redacted>.md) | API contract / goal 36 |
|
|
193
193
|
| 0122 | [Onboarding diagram renders: untracked + rendered at deploy from a host-provisioned Chrome (task [#2027](https://example.com/builders#/task/2027), BONGOS-V1) — completes the diagram half [ADR 0110](<redacted>.md) deferred. The `.svg`/`.png` renders were committed on every render-capable ship and kept colliding on parallel branches; ADR 0110 deferred them because `mermaid-cli`/puppeteer downloads its own Chromium on first run (a multi-minute fetch that stalls the 2 GB droplet). Decision: provision Google Chrome once on the host; `render-diagrams.sh` auto-detects an installed browser (or honors `PUPPETEER_EXECUTABLE_PATH`) and sets `PUPPETEER_SKIP_DOWNLOAD` so it never fetches (falls back to the download only on a dev laptop), passing a committed `puppeteer-config.json` with `--no-sandbox` (Ubuntu 24.04 restricts unprivileged user namespaces) and pinning `mermaid-cli@11`; deploy-prod.sh + box-source-fetch render from the tracked `.mmd` (LOUD `::ERROR::`, best-effort); `git rm --cached` + gitignore the `*.svg`/`*.png` (the `.mmd` + `assertions.json` stay tracked, so the nightly diagram-drift cron is unaffected); ship runs `gen-diagrams --no-render`. Verified rendering all 5 diagrams on the prod droplet before landing. Rejected: on-deploy Chromium download (the droplet stall), snap chromium (flaky headless), keep-tracked + strengthen the ADR 0082 resolver (hides the collision).](<redacted>.md) | internal / ADR 0110 |
|
|
194
|
-
| 0121 | [Greenfield vs. brownfield onboarding — scope `bongos init --adopt` (task [#1986](https://example.com/builders#/task/1986), BONGOS-V1, goal 35's `adopt-existing-repo` criterion). Today the creation flow is greenfield-only: `scripts/gds/init.js` opens *"stand up a NEW instance"*, refuses to run where `config/branding.json` exists (`--force` overwrites), and seeds a synthetic `"V1 — Prove the loop"`; it inspects nothing about an existing tree. Decision: TWO explicit modes over goal 35's one canonical step sequence (task 1981), branching at exactly the SCAFFOLD leg — greenfield (`bongos init`, today) vs brownfield (`bongos init --adopt`); every other leg (provision, OAuth, verify, brand) identical, so the CLI (1982) + hall wizard (1983) forms inherit adopt for free. `--adopt` must: (1) LAYER not clobber — add the pinned `@
|
|
194
|
+
| 0121 | [Greenfield vs. brownfield onboarding — scope `bongos init --adopt` (task [#1986](https://example.com/builders#/task/1986), BONGOS-V1, goal 35's `adopt-existing-repo` criterion). Today the creation flow is greenfield-only: `scripts/gds/init.js` opens *"stand up a NEW instance"*, refuses to run where `config/branding.json` exists (`--force` overwrites), and seeds a synthetic `"V1 — Prove the loop"`; it inspects nothing about an existing tree. Decision: TWO explicit modes over goal 35's one canonical step sequence (task 1981), branching at exactly the SCAFFOLD leg — greenfield (`bongos init`, today) vs brownfield (`bongos init --adopt`); every other leg (provision, OAuth, verify, brand) identical, so the CLI (1982) + hall wizard (1983) forms inherit adopt for free. `--adopt` must: (1) LAYER not clobber — add the pinned `@bongos/core` dep + lean `config/` + `.claude/` additively, merge-or-report on pre-existing files, never reuse the `--force`-overwrites path; (2) reflect REAL state — capture the repo's actual version/goal instead of the synthetic V1, import the existing backlog (GitHub issues / TODO / roadmap) into GDS tasks; (3) DETECT the existing stack + layout so modules / repo-map / fitness-boundary fit; (4) a CONFLICT PRE-FLIGHT report (migration namespace, fitness boundary, protected-path / rank gates, existing CI), `--dry-run` default, gated behind an accept step. Scope-only — no adopt code ships; seeds a 5-task dependency-wired breakdown. Rejected: overloading `--force` to merge (footgun), a separate `bongos adopt` command (leg duplication / drift), auto-import with no pre-flight, building adopt-mode now (1986 is scope-only).](<redacted>.md) | Cloud Bongos / onboarding |
|
|
195
195
|
| 0123 | [Idle-swept dev boxes lose uncommitted work: an on-box autosave-push, not a VM snapshot (task [#837](https://example.com/builders#/task/837) SPIKE). `box.js`'s idle sweep hard-deletes the droplet once a box reads idle — nothing on that path checks git status, and park's snapshot was deliberately removed from the automated path for cost. Decision: a new on-box cron (sibling to `box-heartbeat.sh`) that, when `/workspace` is dirty, commits + pushes to the box's own `claude/<name>` claim branch using the same one-shot-credentialed-then-scrubbed pattern `box-source-fetch.sh` already uses for fetch — bounding loss to the autosave interval (~5 min) instead of unbounded. Open dependency: confirm/mint a push-scoped box credential (today's `BOX_SOURCE_FULL_TOKEN` is fetch-only by convention). Rejected: reintroducing snapshot-before-destroy (the exact cost the team removed), a control-plane git-status check before sweep (needs new box-exec capability, would pin dirty boxes active forever), relying on `/builder-end`'s manual dirty-check alone (doesn't fire on the automatic sweep path at all). Scope-only — no autosave code ships; seeds a follow-up implementation task.](<redacted>.md) | internal / dev-box |
|
|
196
196
|
| 0124 | [Typed API response bodies — follow-up to ADR 0118 for goal 36's `api-client-sdk` (task [#2061](https://example.com/builders#/task/2061), from idea 519, BONGOS-V1). Task 2050 typed every request body but success RESPONSES stayed the `ApiResponse` (`= any`) alias — routes declare no response schema. Decision: infer the success shape STATICALLY from each route's inline `res.json({ … })` object literal (reusing the request extractor's `captureBalanced`/`splitTopLevel` machinery; never executes a route) → a `<OperationId>Response` component on the `200` `content`, and the client returns `Promise<ThatShape>`. Only KEYS are provable (values type `unknown` unless trivially safe — a boolean literal, or the ADR-0119 `pageMeta()` `page` block → a shared `PageInfo`). Degrade-gracefully: a bare value (`res.json(row)`), a top-level spread, an empty `{}`, or a computed key keeps `ApiResponse` — we only ever narrow `any`, never mistype. 162/221 endpoints now typed; runtime JS unchanged (only `openapi.json` + `index.d.ts`). Rejected: a per-route `responseSchema` export (duplicates the `res.json` truth + 200-file churn, anti-code-first), executing routes to observe responses (breaks the zero-execution contract), tracing variables to type values (brittle data-flow analysis), `additionalProperties:false` on responses (a false compile error if an unparsed branch adds a key).](<redacted>.md) | API contract / goal 36 |
|
|
197
197
|
| 0125 | [The control plane scaffolds + pushes a standalone instance's repo (task [#2055](https://example.com/builders#/task/2055), BONGOS-V1, goal 35's `project-creation-flow`). `provision.js` used to THROW if a standalone instance's repo wasn't already hand-cloned at `/srv/cloudbongos/<slug>` — a manual dead-end between `bongos init` and provision. Decision: the provisioning runner SCAFFOLDS the repo itself (`scaffoldStandaloneRepo`) before the existing code-sync/db/service steps: clone the empty `target_ref` (forcing branch `main` on the unborn HEAD) → build the core tarball (`package-core.js`) → materialize `config/` + `.claude/` + a `file:`-pinned `package.json` + lockfile via `bongos init --vendor-core` (task 2053, `--no-seed`) → commit + push (with a `node_modules`-excluding `.gitignore`; vendored tarball + lockfile stay tracked). Idempotent (skips once `package.json` exists) + dry-run-safe (injected `exec`/`writeFile`). Git transport reuses the existing `github-deploy` SSH alias (no secret in any command) — the one operational grant is that the control plane's deploy key gets WRITE on each instance repo (per-repo, like the platform repo). `deploy-instance.sh` already existed at `scripts/deploy/` (ADR 0108 §4 / task 1884; the task description predated it) — it's the ongoing consumer deploy, not the first-time scaffold; no `infra/` duplicate created. Rejected: a control-plane PAT/App-token over HTTPS (token-leak risk in the URL + a new broad secret class — a GitHub App is the scalable follow-up), keeping the "clone it yourself first" throw (the dead-end itself), scaffolding from a template fork (a template repo to maintain vs `--vendor-core`'s single-source tree).](<redacted>.md) | Cloud Bongos / onboarding |
|
|
@@ -203,6 +203,7 @@ This keeps the decision history honest and traceable.
|
|
|
203
203
|
| 0131 | [Rank-scoped skill visibility — feasibility spike (task [#1650](https://example.com/builders#/task/1650)). Wanted: a session lists only the skills the builder's rank can use, via a SessionStart hook filtering the skill menu — pure UX declutter (the server rank gate + each skill's early self-check still enforce). Finding: Claude Code hooks CANNOT filter/hide/rewrite the skill listing — SessionStart offers only `additionalContext` (inject text) + `reloadSkills` (add-only), no hook subtracts a skill, and hooks can't mutate settings at runtime. A HARD menu hide needs a pre-launch step (the launcher that already fetches rank) writing per-builder `skillOverrides:"off"` into `.claude/settings.local.json` before `claude` starts (misses plugin skills; workspace-trust-gated). Recommendation: don't build the heavy launcher change for a cosmetic gain; if some declutter is wanted now, use the existing hook's `additionalContext` as a soft nudge; keep the server gate + self-checks. Owner to decide.](<redacted>.md) | dx / skills |
|
|
204
204
|
| 0132 | [Migrate the co-hosting fleet to the control plane over an SSH DB tunnel (task [#2071](https://example.com/builders#/task/2071), BONGOS-V1, goal 26; extends ADR 0130, completes 2065 / blocker 64). Stripping the co-hosting box's tokens proved NOT a switch-flip: it runs FIVE token-consuming timers (the 4 `box.js` dev-box lifecycle sweeps + `provision-intent-runner`), each reading its queue from the box's LOCAL `example` Postgres (firewalled to localhost) — so a control-plane runner can't reach the queues, and ADR 0130 moved only box-step execution, not queue reading. Also `box.js` was never migrated. Decision: a persistent SSH tunnel (control-plane `127.0.0.1:5433` → co-hosting `127.0.0.1:5432`, `infra/cohost-db-tunnel.service`) + all five runners re-homed as `cohost-*` control-plane variants reading the example queues via `DATABASE_URL` through the tunnel (no public Postgres exposure), calling DO/CF with the control-plane tokens; provisioning remote-execs box steps per ADR 0130. DB auth: a scram password on the EXISTING `lars` role (identical privileges the runners already use; reachable only via `127.0.0.1`), stored only in chmod-600 `/etc/cloudbongos/cohost-fleet.env`. `box.js` unchanged (pure DO/CF API + Discord/DNS-hook execs, all on the control plane). `cohost-provision-intent-runner.*` supersedes 2065's `provision-intent-runner-cloudbongos.*`. Rejected: exposing Postgres over the VPC (opens a TCP surface + `pg_hba`/cred work; droplets not guaranteed same-VPC); a token-signing broker on the co-hosting box (leaves tokens' blast radius there — opposite of the goal); moving the queue tables to the cloudbongos DB (the OTB web tier can't reach it either — firewall symmetry). Units ship additive + not-enabled, self-healed install-only. Activation (SSH trust · DB password · tunnel · enable `cohost-*` + disable co-hosting timers same window · verify via drift-reconcile · bake · owner token rotate+strip · drop rollback DB) is a sequenced operator-op.](<redacted>.md) | infra / Cloud Bongos |
|
|
205
205
|
| 0133 | [One-click GitHub sign-in for onboarding via the GitHub App Manifest flow (task [#2080](https://example.com/builders#/task/2080), BONGOS-V1, goal 35 / criterion 165). The last onboarding friction was wiring GitHub sign-in — the owner had to hand-create an OAuth App + paste a client secret, unscriptable because GitHub has NO create-OAuth-App API. Decision: use GitHub's App **Manifest** flow. The signed-in owner clicks one pre-filled link; GitHub creates the App and returns a temporary `code`; the control-plane runner (`provision.js`, sole token-holder) exchanges it for `client_id`/`client_secret`, writes them to `/etc/<slug>/web.env` (the `oauth-secret.js` path), restarts, and verifies `/healthz` auth_configured — the secret never touches the browser, chat, or web tier. Spans the trust boundary: web tier mints a one-time `state` + stores a `provisioning_oauth_manifests` row + a PUBLIC callback stores GitHub's code (`state` is the auth); the control-plane runner drains `code_received` rows on its existing timer tick (gated on instance=active). Sign-in code unchanged — a GitHub App's user token hits the same `GET /user`; GitHub ignores the extra `scope` for Apps. Back-compat: existing OAuth-app instances untouched; manifest is the new default, manual path kept as fallback. All three onboarding surfaces (hall wizard one-click button, `bongos onboard`, `/new-project`) updated in lockstep.](<redacted>.md) | onboarding / auth |
|
|
206
|
-
| 0134 | [Private-first npm distribution for the core + client (task [#2089](https://example.com/builders#/task/2089), BONGOS-V1, goal 35 / criterion 165; refines [#2025](https://example.com/builders#/task/2025), builds on ADR 0108). Owner wants the npm/CLI onboarding commands ready but private pre-launch, choosing paid private npm over free GitHub Packages. npm rule forces the shape: unscoped names are always public, only SCOPED packages can be private (paid plan) — so the private form stays scoped `@
|
|
206
|
+
| 0134 | [Private-first npm distribution for the core + client (task [#2089](https://example.com/builders#/task/2089), BONGOS-V1, goal 35 / criterion 165; refines [#2025](https://example.com/builders#/task/2025), builds on ADR 0108). Owner wants the npm/CLI onboarding commands ready but private pre-launch, choosing paid private npm over free GitHub Packages. npm rule forces the shape: unscoped names are always public, only SCOPED packages can be private (paid plan) — so the private form stays scoped `@bongos/core` + `@bongos/client` (zero rename; org already owned). Decision: publish both as scoped PRIVATE packages now via `publishConfig:{access:"restricted"}` baked into the synthesized core `package.json` (`package-core.js buildPackageJson`) + the generated client `package.json` (`gen-api-client.js`); `package-core.js --publish` dry-runs by default, real publish needs `--publish --live` + owner `npm login` (tooling holds no token). Consumers auth via env-fed `.npmrc` `_authToken=<redacted> Vendored-tarball install (task 2053) stays the working default; provision/init registry auto-pull is a follow-up. Going public at launch is one flag (`npm access public`) or task 2025's unscoped vanity `cloudbongos` — 2025 becomes the go-public step, not superseded. The daily `bongos` command is name-independent throughout. Runbook: docs/recipes/private-npm-distribution.md.](<redacted>.md) | distribution / npm |
|
|
207
|
+
| 0135 | [Module upstream submission: the review queue lives in the submitting instance's own DB, interim (task [#1770](https://example.com/builders#/task/1770), BONGOS-V1, builds on ADR 0107). ADR 0107 §1 says `bongos module submit` files a packaged module into a rank-gated review queue "on the core" — but the extracted core (`github.com/example-owner/cloud-bongos`) is a private GIT REPO, not a running service (no HTTP API), and per ADR 0100 §3 each project runs its OWN GDS DB, so there is no shared cross-repo queue to file into; R85 (the forcing-function second consumer) was also abandoned. Decision: the queue is a table (`module_submissions`, migration `<redacted>.sql`) in THIS instance's own Bongos DB. `bongos module submit <key>` and the hall's submit button both call the SAME route, `POST /modules/:key/submit` (metic+archon), which re-runs the ADR 0107 §4 pre-check against its OWN on-disk copy (authoritative — a local CLI pass is a courtesy) and refuses without a sign-off; `GET /modules/submissions` lists the queue for task 1771 (core-side review & accept, still backlog) to read from. Rejected: a live call to a not-yet-built core endpoint (guesses at 1771's contract); a GitHub Issue/PR against the private core repo (loses the structured queue, no standing credential); a dry-run-only local-file client mirroring do-api.js (a worse queue than a DB table this instance already operates). Forward-compatible: when a real core endpoint exists, filing becomes a network hop with no change to the CLI/UI contract.](<redacted>.md) | modules / upstreaming |
|
|
207
208
|
|
|
208
209
|
> ⚠️ **Numbering collision (known):** two ADRs share `0008`. The numbers were assigned in parallel sessions before the lock could be enforced. The files are kept under their existing filenames to preserve git history, but the table disambiguates them as `0008-a` / `0008-b`. New ADRs should resume at `0040` to avoid further collisions. (`0024` is also a real on-disk duplicate: the cloneable-repo/local-first-memory ADR and the multi-agent-system-architecture ADR were both numbered `0024` in parallel; both keep their filenames, disambiguated as `0024-a` / `0024-b` like `0008`. Likewise `0025` ended up with two on-disk files — the offsite-backup ADR and the structured-criterion↔task-link ADR — both kept under their existing filenames and disambiguated as `0025-a` / `0025-b`; separately, the BFG ADR had also claimed `0025` on 2026-05-30 and yielded to `0026`. A third happened 2026-06-06: `0036` was claimed in parallel by the builders-subdomain ADR ([#726](https://example.com/builders#/task/726)) and this Discord-channels ADR ([#727](https://example.com/builders#/task/727)); the Discord-channels ADR yielded to `0037`. A fourth landed 2026-06-10: `0044` was claimed in parallel by the palette-replacement ADR and the per-box game-preview ADR ([#897](https://example.com/builders#/task/897)); both keep their filenames, disambiguated as `0044-a` / `0044-b` like `0008`. A fifth was caught pre-land 2026-06-13: `0050` was claimed in parallel by the device-flow-admission ADR ([#1000](https://example.com/builders#/task/1000)) and the sandbox-for-everyone ADR ([#1001](https://example.com/builders#/task/1001)); the latter yielded to `0052` before merging, so there is no `0050` duplicate. A sixth was caught pre-land 2026-06-21: the tool-agnostic-design-layer ADR ([#1375](https://example.com/builders#/task/1375)) first collided at `0079` with the value-gated-autonomous-engagement ADR, then at `0080` with the llm-cache-api-transport ADR (both part of the 0075–0080 LLM-cost-reduction series, landed on `main` in parallel); it yielded to `0081` before merging, so there is no `0079`/`0080` design-layer duplicate. A seventh landed 2026-06-23: `0084` was claimed in parallel by the auto-derived-per-task-rank-floor ADR and the remove-default-spend-cap ADR (task 1499); the rank-floor ADR landed on `main` first, so the spend-cap ADR yielded to `0085`, leaving no `0084` duplicate. An eighth landed 2026-06-27: `0095` was claimed in parallel by the borrowed-memory-and-retrieval-concepts ADR (PR #459, landed first) and the cross-agent-context-management ADR ([#1651](https://example.com/builders#/task/1651), PR #460); both keep their filenames, disambiguated as `0095-a` / `0095-b` like `0008`. These two are companions — `0095-a` adopts the memory/retrieval concepts and explicitly defers the grep-vs-semantic question to `0095-b`.)
|