@bongos/core 1.21.69 → 1.21.71
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 +119 -64
- package/.claude/skills/bongos-feedback/SKILL.md +10 -46
- package/.claude/skills/goal-create/SKILL.md +21 -3
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -2
- package/clients/bongos-client/index.cjs +8 -2
- package/clients/bongos-client/index.d.ts +15 -6
- package/clients/bongos-client/index.mjs +8 -2
- package/docs/adr/0368-red-and-blue-teams-practice-on-a-range-never-on-live.md +99 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +196 -19
- package/docs/api-reference.md +9 -6
- package/docs/copy-inventory.md +25 -21
- package/docs/copy-registry.json +76 -30
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +8 -8
- package/docs/recipes/core-release-pipeline.md +6 -0
- package/docs/recipes/upgrading-the-core.md +2 -0
- package/modules/hall-ui/public/settings-software-update.js +77 -2
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/lifecycle/db-tasks.js +12 -0
- package/modules/lifecycle/goal-spec-task.js +129 -0
- package/modules/lifecycle/routes/goals.js +7 -9
- package/modules/lifecycle/routes/task-write-routes.js +11 -1
- package/modules/lifecycle/upkeep-home.js +55 -0
- package/modules/platform-identity/instance-feedback.js +108 -7
- package/modules/platform-identity/routes/sso.js +71 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +42 -0
- package/scripts/gds/claude-materialize.js +125 -31
- package/scripts/gds/claude-md-template.js +47 -10
- package/scripts/gds/cli-lib.js +4 -4
- package/scripts/gds/feedback-send.js +146 -73
- package/scripts/gds/paste-token.js +1 -1
- package/scripts/gds/provision-net.js +1 -8
- package/scripts/gds/provision-units.js +28 -2
- package/scripts/gds/reauth.js +1 -1
- package/scripts/gds/run-unit-tests.js +3 -0
- package/scripts/gds/setup.js +1 -1
- package/scripts/gds/start.js +4 -4
- package/scripts/gds/surface.js +15 -3
- package/scripts/gds/task.js +32 -5
- package/scripts/gds/upgrade-operator.js +73 -0
- package/scripts/gds/upgrade.js +7 -2
- package/scripts/hall-preview/server.js +3 -0
- package/src/bongos/platform-visibility-gate.js +3 -0
- package/src/bongos/routes/core-update.js +98 -4
- package/src/bongos/routes/feedback.js +100 -76
- package/src/bongos/upgrade-request.js +158 -0
- package/src/module-api.js +1 -1
- package/tests/claude_materialize.mjs +195 -18
- package/tests/cli_allowlist_permissions.mjs +6 -2
- package/tests/cli_surface.mjs +2 -2
- package/tests/fixtures/page-ledger.mjs +5 -0
- package/tests/goal_admission_override.mjs +1 -1
- package/tests/goal_category.mjs +1 -1
- package/tests/goal_create_spec_entry.mjs +61 -0
- package/tests/goal_spec_task.mjs +197 -0
- package/tests/goal_spec_task_db.mjs +95 -0
- package/tests/instance_feedback.mjs +267 -68
- package/tests/provision_settings_apply.mjs +25 -1
- package/tests/software_update.mjs +18 -1
- package/tests/start_env_guard.mjs +2 -2
- package/tests/upgrade_operator.mjs +105 -0
- package/tests/upgrade_request.mjs +348 -0
- package/tests/upkeep_home.mjs +156 -0
|
@@ -7,61 +7,25 @@ plain: >-
|
|
|
7
7
|
reach-for: >-
|
|
8
8
|
When Bongos itself got in your way — a command that failed or confused you, or something you wish it did. You do not have to ask; complaining is enough.
|
|
9
9
|
cost: >-
|
|
10
|
-
Free
|
|
10
|
+
Free, and instant: one line back, and it sends in the background. It changes nothing in your project.
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Sends a note about Bongos — the tool — straight to its maintainers (tasks 1004462, 1004591). One tool call, one line back.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**When.** File when the builder (a) labels it — "Bongos feedback: …", "tell the Bongos team" — that **is** the go; (b) wishes — "Bongos should…" (**Suggestions count**: send with `--idea`, even if you also fix it in their project); or (c) vents that Bongos is broken — **File it without being asked**, don't argue them out of it. Only about **Bongos itself** (a `bongos`/`/builder-*` command, claim, ship, grade, deploy, upgrade, hall, Settings, sign-in, credits, how it organises work). Not their own project's code, Claude, their laptop or GitHub, nor a rule working as designed. **One filing per distinct problem per session.**
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
**Write it yourself — Never paste the rant.** First line = what went wrong (it becomes the title); then what they did, what they expected, what happened, the exact error line. No code, file contents, secrets or personal data.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
- **Suggestions count.** Feedback is not only breakage. "Bongos should…", "I don't want Bongos to…", or a complaint about how Bongos organises their work (versions, goals, tasks, ranks) is feedback too — send it with `--idea`. Fixing it in their own project (moving the goal, saving a preference) is fine, but it does not replace sending it: the next project would hit the same thing.
|
|
21
|
-
- **Proactive** — the builder is venting that Bongos is not working: frustration, swearing, "this is broken again", "why does X keep failing", "Bongos is useless today". **File it without being asked**, then tell them in one line (below). The owner's standing rule (2026-09-30): every rant about Bongos not working becomes something to fix. Do not ask permission first, and do not argue them out of it.
|
|
22
|
-
|
|
23
|
-
## Is it about Bongos? (proactive mode files only when yes)
|
|
24
|
-
|
|
25
|
-
File when the complaint is about **Bongos itself**: how it organises their work (a goal that forced a new version, a version they did not want), a `bongos`/`/builder-*` command, a claim, ship, grade, merge or deploy step, the hall, Settings, sign-in, credits, the upgrade, or a Bongos message that confused them.
|
|
26
|
-
|
|
27
|
-
Do **not** file when it is about their **own project's** code, tests or design, about Claude itself, their laptop, GitHub or another service — or when the "problem" is a rule working as designed (an `[otb-plain]` card saying `By design: Yes`). If it is genuinely unclear, fix the thing in front of you first and file only if it turns out to be Bongos.
|
|
28
|
-
|
|
29
|
-
**One filing per distinct problem per session.** A second rant about the same failure adds nothing; say it is already filed (with the number) instead.
|
|
30
|
-
|
|
31
|
-
## Writing the report
|
|
32
|
-
|
|
33
|
-
Write it yourself from what you saw. **Never paste the rant.** The maintainers need the facts, not the mood:
|
|
34
|
-
|
|
35
|
-
1. A plain first line naming what went wrong — it becomes the title. (`bongos claim hung for two minutes, then failed with fetch failed`)
|
|
36
|
-
2. What the builder was doing, what they expected, what happened instead, and the exact error line if there was one.
|
|
37
|
-
3. Whether it happened more than once, and any workaround you found.
|
|
38
|
-
|
|
39
|
-
**Leave out:** code, file contents, secrets, tokens, customer or personal data, and anything about their project the maintainers do not need. Only the text you send travels (plus the Bongos and CLI versions, added automatically). As a backstop the command runs every report through Bongos's secret scrubber and refuses to send if it can't, but it is a backstop, not permission to be careless.
|
|
40
|
-
|
|
41
|
-
A report that is really a wish ("I'd like Bongos to…") goes with `--idea`.
|
|
42
|
-
|
|
43
|
-
## Sending
|
|
44
|
-
|
|
45
|
-
From the project checkout. For anything longer than one line, write the report to a file with the Write tool first — the shell eats backticks inside a quoted argument:
|
|
19
|
+
**Send** (stdin, so nothing in the text is mangled by the shell):
|
|
46
20
|
|
|
47
21
|
```bash
|
|
48
|
-
bongos feedback --message-file
|
|
22
|
+
bongos feedback --message-file - <<'EOF'
|
|
23
|
+
<report>
|
|
24
|
+
EOF
|
|
49
25
|
```
|
|
50
26
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
**Proactive:** one line, after sending, then carry on with what they were doing. For example: *"I've reported this to the Bongos team as task 1004500 — it's waiting for a maintainer to approve it."* No more than that, unless they ask what was sent — then show them the report.
|
|
54
|
-
|
|
55
|
-
**Asked:** report the CLI's output verbatim.
|
|
56
|
-
|
|
57
|
-
## If it refuses
|
|
58
|
-
|
|
59
|
-
The CLI prints one plain line for each refusal. In proactive mode, a refusal is one line to the builder and nothing more — never retry in a loop.
|
|
60
|
-
|
|
61
|
-
- **"not connected to Cloud Bongos"** — a self-hosted project has no link to the hub. Point the builder at the Bongos core repository's issue tracker.
|
|
62
|
-
- **"does not yet know you as a member"** — the hub only accepts reports from someone it has seen sign in to this project. Signing in to the project once through Cloud Bongos in a browser fixes it.
|
|
63
|
-
- **"Too much feedback"** — the hourly cap. Don't retry; mention it once.
|
|
27
|
+
Add `--idea` for a wish. The command prints one line — relay it, then carry on. Proactive filing: one short sentence, e.g. *"I've sent that to the Bongos team."* If it says the project runs only on this computer, tell the builder in one line; never retry.
|
|
64
28
|
|
|
65
29
|
## Files this skill touches
|
|
66
30
|
|
|
67
|
-
- Runs: `bongos feedback` → `scripts/gds/feedback-send.js` → `POST /api/bongos/feedback
|
|
31
|
+
- Runs: `bongos feedback` → `scripts/gds/feedback-send.js` → a receipt from this project (`POST /api/bongos/feedback/receipt`, `src/bongos/routes/feedback.js`) → the hub's `POST /api/bongos/sso/feedback/direct` (`modules/platform-identity/instance-feedback.js`), which redeems the receipt before filing.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: goal-create
|
|
3
3
|
description: >-
|
|
4
|
-
Plan and create ONE goal (
|
|
4
|
+
Plan and create ONE goal (a module-scoped workspace) with its criteria and seed tasks, or spec one made in the hall. Metic+. Triggers: "/goal-create", "create a goal", "what goal should this live in", "spec goal N", a task created with no goal.
|
|
5
5
|
plain: >-
|
|
6
6
|
Helps you set up a new goal: what it is for, how you will know it is done, and the first tasks to get there.
|
|
7
7
|
reach-for: >-
|
|
@@ -89,8 +89,10 @@ The order is forced: a criterion needs `goal_id`, a task needs both `goal_id` an
|
|
|
89
89
|
> **Always `--body-file`, never inline `--body`, for anything carrying a title, description or criterion prose.** Those strings are authored in this session and routinely contain an apostrophe ("the builder's hall"), which terminates a single-quoted shell argument and corrupts the JSON — or, with a hostile string, runs whatever follows on the builder's machine. Write the body to a temp `.json` file and pass its path. On Windows also heed `scripts/gds/CLAUDE.md`: do **not** create that file with `Out-File`/`>` (the UTF-8 BOM breaks `JSON.parse`), and never pipe `api.js` through `2>&1` (its banner goes to stderr by design).
|
|
90
90
|
|
|
91
91
|
```
|
|
92
|
-
# 5a — the goal. STRICT validation: exactly these
|
|
93
|
-
# (version_id*, title*, subtitle, description, scope_modules[], succeeds_goal_id, category_id
|
|
92
|
+
# 5a — the goal. STRICT validation: exactly these fields are accepted
|
|
93
|
+
# (version_id*, title*, subtitle, description, scope_modules[], succeeds_goal_id, category_id,
|
|
94
|
+
# plus admit_reason — Archon only — and spec_task, which ONLY the hall form sends: it makes
|
|
95
|
+
# the goal arrive with a "Spec goal N" task, the job this skill does inline. Never send it here).
|
|
94
96
|
# ANY other key is a 400 unknown_field — do NOT pass criteria, tasks, lead or visibility.
|
|
95
97
|
# goal.json: {"version_id":"<ver>","title":"...","description":"...","scope_modules":["<key>"],"category_id":<id>}
|
|
96
98
|
# category_id comes from GET /api/bongos/work-categories (task 1003277); omitting it is
|
|
@@ -159,6 +161,22 @@ Run the phases above **with the orphan task as the seed** — its title, descrip
|
|
|
159
161
|
|
|
160
162
|
**A goal-less task is not an error and was never blocked.** `goal_advisory` is advice on a request that already succeeded. Never fail a build, refuse work, or badger the builder over it — if they say "later", file the idea and move on.
|
|
161
163
|
|
|
164
|
+
## Finishing a hall-made goal's spec task (task 1004586, ADR 0366 D3)
|
|
165
|
+
|
|
166
|
+
A goal created from the hall's form has only a name, a description, a version and a category. It arrives with one task inside it, **"Spec goal N: its scope, criteria and first tasks"** (`source: goal-spec`, `source_ref: goal-<N>-spec`), and every other task in that goal waits on it: the server gives each one a dependency on the spec task, so nothing in the goal can be claimed until the spec ships. This skill is how that task gets done. Invoke it with the goal id ("spec goal 1000123") or from the claimed spec task.
|
|
167
|
+
|
|
168
|
+
1. **Claim the spec task** (`/builder-claim <id>`) if it is not already yours. Find it with `GET "/api/bongos/tasks?goal_id=<N>"` — the row whose `source_ref` is `goal-<N>-spec`.
|
|
169
|
+
2. **Phase 1, with the goal fixed.** Read the goal (`GET /api/bongos/goals/<N>`), its version's other open goals and criteria, and the live module roster exactly as above. The goal's name and description are the person's statement of intent: they are your evidence, as an orphan task's text is in the orphan hook.
|
|
170
|
+
3. **Phase 2 becomes a duplicate check.** Do not create a goal. If another open goal already owns this outcome, raise it in the grey-area round (archive one, or move the work) rather than speccing a duplicate.
|
|
171
|
+
4. **Phase 3 drafts what the form could not ask:** `scope_modules` (inferred and verified exactly as above, never ticked from a list), 2–4 criteria, and the seed tasks with their dependencies. Do not re-draft the name or description; suggest an edit in the grey-area round if one is wrong.
|
|
172
|
+
5. **Phase 4 is unchanged:** at most one grey-area round, then one approval round.
|
|
173
|
+
6. **Phase 5 writes onto the EXISTING goal**, in this order:
|
|
174
|
+
- **Scope:** `POST /api/bongos/goals/<N>/scope --body '{"add":["<key>"]}'` (keys only, safe inline). The goal's owner or a manager; an Archon for a protected key.
|
|
175
|
+
- **Criteria:** `POST /api/bongos/versions/<ver>/done-when` with `"goal_id":<N>`, exactly as 5b.
|
|
176
|
+
- **Seed tasks:** `POST /api/bongos/tasks` with `"goal_id":<N>`, exactly as 5c. **Each one is automatically made to wait on the spec task** — do not remove that edge, and **do not promote them**: they move to `ready` by themselves the moment the spec task ships (the ordinary auto-promotion). Add their own dependency edges (5d) as usual.
|
|
177
|
+
- **Then ship the spec task** with `/builder-ship`. That ship is what releases the goal's work. Its value summary says what the goal now owns and which tasks became claimable.
|
|
178
|
+
7. **If you do not own or manage the goal,** the scope and criteria writes are refused (403 / `400 bad_goal_id`, see above). Do not work around it: send the drafted scope, criteria and task table to the goal's owner (`/ask-for-help` naming them, or paste it on the spec task), file the seed tasks you are allowed to file, and leave the spec task claimed until the owner has written the rest. A spec task shipped with no scope and no criteria releases a goal that is still undefined, which is the state this task exists to prevent.
|
|
179
|
+
|
|
162
180
|
## The sibling advisory: `dependency_advisory` (task 1002827)
|
|
163
181
|
|
|
164
182
|
The same `POST /tasks` response can also carry a non-null **`dependency_advisory`** — a *different* check, unrelated to which goal the task landed in: it names OPEN tasks in the same version whose `touches[]` overlap this one's, with no declared dependency between them (`scripts/gds/audit-deps.js`'s detector, run live for the first time).
|
|
@@ -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
|
-
-
|
|
8
|
+
- 523 operations across 75 resource groups
|
|
9
9
|
|
|
10
10
|
## Use it from your project
|
|
11
11
|
|
|
@@ -385,8 +385,10 @@ function createClient(opts = {}) {
|
|
|
385
385
|
getDoneWhenPendingReview: (args) => request("GET", "/done-when/pending-review", { hasBody: false }, args),
|
|
386
386
|
},
|
|
387
387
|
"feedback": {
|
|
388
|
-
// POST /feedback — rank: any-builder — POST /feedback
|
|
389
|
-
|
|
388
|
+
// POST /feedback/receipt — rank: any-builder — POST /feedback/receipt
|
|
389
|
+
postFeedbackReceipt: (args) => request("POST", "/feedback/receipt", { hasBody: true }, args),
|
|
390
|
+
// GET /feedback/receipt/{id} — rank: public — GET /feedback/receipt/:id
|
|
391
|
+
getFeedbackReceiptId: (args) => request("GET", "/feedback/receipt/{id}", { hasBody: false }, args),
|
|
390
392
|
},
|
|
391
393
|
"founding": {
|
|
392
394
|
// POST /founding/alive — rank: any-builder — POST /founding/alive
|
|
@@ -1079,6 +1081,8 @@ function createClient(opts = {}) {
|
|
|
1079
1081
|
"softwareUpdate": {
|
|
1080
1082
|
// GET /software-update — rank: any-builder — GET /software-update
|
|
1081
1083
|
getSoftwareUpdate: (args) => request("GET", "/software-update", { hasBody: false }, args),
|
|
1084
|
+
// POST /software-update/request — rank: archon — POST /software-update/request
|
|
1085
|
+
postSoftwareUpdateRequest: (args) => request("POST", "/software-update/request", { hasBody: true }, args),
|
|
1082
1086
|
},
|
|
1083
1087
|
"specialities": {
|
|
1084
1088
|
// GET /specialities — rank: any-builder — GET /specialities
|
|
@@ -1125,6 +1129,8 @@ function createClient(opts = {}) {
|
|
|
1125
1129
|
postSsoDeviceStart: (args) => request("POST", "/sso/device/start", { hasBody: true }, args),
|
|
1126
1130
|
// POST /sso/feedback — rank: public — POST /sso/feedback
|
|
1127
1131
|
postSsoFeedback: (args) => request("POST", "/sso/feedback", { hasBody: true }, args),
|
|
1132
|
+
// POST /sso/feedback/direct — rank: public — POST /sso/feedback/direct
|
|
1133
|
+
postSsoFeedbackDirect: (args) => request("POST", "/sso/feedback/direct", { hasBody: true }, args),
|
|
1128
1134
|
// POST /sso/invites/clear — rank: public — POST /sso/invites/clear
|
|
1129
1135
|
postSsoInvitesClear: (args) => request("POST", "/sso/invites/clear", { hasBody: true }, args),
|
|
1130
1136
|
// POST /sso/invites/notify — rank: public — POST /sso/invites/notify
|
|
@@ -384,8 +384,10 @@ function createClient(opts = {}) {
|
|
|
384
384
|
getDoneWhenPendingReview: (args) => request("GET", "/done-when/pending-review", { hasBody: false }, args),
|
|
385
385
|
},
|
|
386
386
|
"feedback": {
|
|
387
|
-
// POST /feedback — rank: any-builder — POST /feedback
|
|
388
|
-
|
|
387
|
+
// POST /feedback/receipt — rank: any-builder — POST /feedback/receipt
|
|
388
|
+
postFeedbackReceipt: (args) => request("POST", "/feedback/receipt", { hasBody: true }, args),
|
|
389
|
+
// GET /feedback/receipt/{id} — rank: public — GET /feedback/receipt/:id
|
|
390
|
+
getFeedbackReceiptId: (args) => request("GET", "/feedback/receipt/{id}", { hasBody: false }, args),
|
|
389
391
|
},
|
|
390
392
|
"founding": {
|
|
391
393
|
// POST /founding/alive — rank: any-builder — POST /founding/alive
|
|
@@ -1078,6 +1080,8 @@ function createClient(opts = {}) {
|
|
|
1078
1080
|
"softwareUpdate": {
|
|
1079
1081
|
// GET /software-update — rank: any-builder — GET /software-update
|
|
1080
1082
|
getSoftwareUpdate: (args) => request("GET", "/software-update", { hasBody: false }, args),
|
|
1083
|
+
// POST /software-update/request — rank: archon — POST /software-update/request
|
|
1084
|
+
postSoftwareUpdateRequest: (args) => request("POST", "/software-update/request", { hasBody: true }, args),
|
|
1081
1085
|
},
|
|
1082
1086
|
"specialities": {
|
|
1083
1087
|
// GET /specialities — rank: any-builder — GET /specialities
|
|
@@ -1124,6 +1128,8 @@ function createClient(opts = {}) {
|
|
|
1124
1128
|
postSsoDeviceStart: (args) => request("POST", "/sso/device/start", { hasBody: true }, args),
|
|
1125
1129
|
// POST /sso/feedback — rank: public — POST /sso/feedback
|
|
1126
1130
|
postSsoFeedback: (args) => request("POST", "/sso/feedback", { hasBody: true }, args),
|
|
1131
|
+
// POST /sso/feedback/direct — rank: public — POST /sso/feedback/direct
|
|
1132
|
+
postSsoFeedbackDirect: (args) => request("POST", "/sso/feedback/direct", { hasBody: true }, args),
|
|
1127
1133
|
// POST /sso/invites/clear — rank: public — POST /sso/invites/clear
|
|
1128
1134
|
postSsoInvitesClear: (args) => request("POST", "/sso/invites/clear", { hasBody: true }, args),
|
|
1129
1135
|
// POST /sso/invites/notify — rank: public — POST /sso/invites/notify
|
|
@@ -167,7 +167,7 @@ export interface GetSessionsIdResponse { session: unknown }
|
|
|
167
167
|
export interface GetSessionsIdTranscriptResponse { transcript: unknown }
|
|
168
168
|
export interface GetSessionsMineResponse { sessions: unknown; count: unknown }
|
|
169
169
|
export interface GetSessionsSearchResponse { sessions: unknown; count: unknown; total: unknown; totals: unknown; limit: unknown; offset: unknown; session_types: unknown }
|
|
170
|
-
export interface GetSoftwareUpdateResponse { ok: boolean; update: unknown; door: unknown; can_update: unknown }
|
|
170
|
+
export interface GetSoftwareUpdateResponse { ok: boolean; update: unknown; door: unknown; can_update: unknown; ask: unknown }
|
|
171
171
|
export interface GetSpecialitiesIdDocumentsResponse { documents: unknown }
|
|
172
172
|
export interface GetSpecialitiesIdResponse { speciality: unknown }
|
|
173
173
|
export interface GetSpecialitiesIdSkillsResponse { speciality: unknown; adopted: boolean; enabled: unknown; skills: unknown }
|
|
@@ -316,7 +316,8 @@ export interface PostDoneWhenCriterionIdUatRequest { kind: string; recording?: s
|
|
|
316
316
|
export interface PostDoneWhenCriterionIdUatResponse { ok: boolean; signoff: unknown; criterion_closed: unknown; achieved_goals: unknown; closed_versions: unknown }
|
|
317
317
|
export interface PostDoneWhenCriterionIdUnsatisfyRequest { reason?: string }
|
|
318
318
|
export interface PostDoneWhenCriterionIdUnsatisfyResponse { criterion: unknown }
|
|
319
|
-
export interface
|
|
319
|
+
export interface PostFeedbackReceiptRequest { sha256: string; kind?: "bug" | "idea" }
|
|
320
|
+
export interface PostFeedbackReceiptResponse { ok: boolean; receipt: unknown; hub: unknown }
|
|
320
321
|
export interface PostFoundingPlanRequest { days?: Record<string, unknown>; passes?: number; advisors?: string; government?: string; expected_builders?: number; existing?: boolean }
|
|
321
322
|
export interface PostGithubRepoVisibilityRequest { repo: string; visibility: "public" | "private" }
|
|
322
323
|
export interface PostGithubRepoVisibilityResponse { ok: boolean; repo: unknown; private_deployable: boolean }
|
|
@@ -353,8 +354,8 @@ export interface PostGoalsIdTransferRequest { builder_id?: StringifiableId }
|
|
|
353
354
|
export interface PostGoalsIdTransferResponse { ok: boolean; goal: unknown; changed: boolean }
|
|
354
355
|
export interface PostGoalsReorderRequest { version_id: string; ordered_ids: number[] }
|
|
355
356
|
export interface PostGoalsReorderResponse { ok: boolean; goals: unknown }
|
|
356
|
-
export interface PostGoalsRequest { version_id: string; title: string; subtitle?: string; description?: string; scope_modules?: string[]; succeeds_goal_id?: number; category_id?: number; admit_reason?: string }
|
|
357
|
-
export interface PostGoalsResponse { ok: boolean; goal: unknown; succeeds_goal_id: unknown; category_advisory: unknown; admitted: unknown }
|
|
357
|
+
export interface PostGoalsRequest { version_id: string; title: string; subtitle?: string; description?: string; scope_modules?: string[]; succeeds_goal_id?: number; category_id?: number; admit_reason?: string; spec_task?: boolean }
|
|
358
|
+
export interface PostGoalsResponse { ok: boolean; goal: unknown; succeeds_goal_id: unknown; category_advisory: unknown; admitted: unknown; spec_task: unknown }
|
|
358
359
|
export interface PostGovernmentAssignmentsRequest { builder_id: number; rank_key: string }
|
|
359
360
|
export interface PostGovernmentBoardCharterRequest { form?: string; franchise?: string; council_rank?: string; pass_rule?: string; rationale_md?: string; acknowledge_self_removal?: boolean }
|
|
360
361
|
export interface PostGovernmentBoardCharterResponse { ok: boolean; amendment: unknown; item: unknown; reads_as: unknown }
|
|
@@ -496,6 +497,8 @@ export interface PostSsoApplicantProfileRequest { client_id: string; client_secr
|
|
|
496
497
|
export interface PostSsoApplicantProfileResponse { profile: unknown; guild: unknown }
|
|
497
498
|
export interface PostSsoDevicePollRequest { device_code: string }
|
|
498
499
|
export interface PostSsoDeviceStartRequest { origin: string }
|
|
500
|
+
export interface PostSsoFeedbackDirectRequest { origin: string; receipt: string; message: string; kind?: string; context?: unknown }
|
|
501
|
+
export interface PostSsoFeedbackDirectResponse { ok: boolean; accepted: boolean }
|
|
499
502
|
export interface PostSsoFeedbackRequest { client_id: string; client_secret: string; github_id: StringifiableId; message: string; kind?: string; context?: unknown }
|
|
500
503
|
export interface PostSsoInvitesClearRequest { client_id: string; client_secret: string; github_login: string }
|
|
501
504
|
export interface PostSsoInvitesNotifyRequest { client_id: string; client_secret: string; github_login: string }
|
|
@@ -879,8 +882,10 @@ export interface BongosClient {
|
|
|
879
882
|
getDoneWhenPendingReview(args?: RequestArgs): Promise<GetDoneWhenPendingReviewResponse>;
|
|
880
883
|
};
|
|
881
884
|
"feedback": {
|
|
882
|
-
/** POST /feedback — rank: any-builder */
|
|
883
|
-
|
|
885
|
+
/** POST /feedback/receipt — rank: any-builder */
|
|
886
|
+
postFeedbackReceipt(args: RequestArgs & { body: PostFeedbackReceiptRequest }): Promise<PostFeedbackReceiptResponse>;
|
|
887
|
+
/** GET /feedback/receipt/{id} — rank: public */
|
|
888
|
+
getFeedbackReceiptId(args?: RequestArgs): Promise<ApiResponse>;
|
|
884
889
|
};
|
|
885
890
|
"founding": {
|
|
886
891
|
/** POST /founding/alive — rank: any-builder */
|
|
@@ -1573,6 +1578,8 @@ export interface BongosClient {
|
|
|
1573
1578
|
"softwareUpdate": {
|
|
1574
1579
|
/** GET /software-update — rank: any-builder */
|
|
1575
1580
|
getSoftwareUpdate(args?: RequestArgs): Promise<GetSoftwareUpdateResponse>;
|
|
1581
|
+
/** POST /software-update/request — rank: archon */
|
|
1582
|
+
postSoftwareUpdateRequest(args?: RequestArgs): Promise<ApiResponse>;
|
|
1576
1583
|
};
|
|
1577
1584
|
"specialities": {
|
|
1578
1585
|
/** GET /specialities — rank: any-builder */
|
|
@@ -1619,6 +1626,8 @@ export interface BongosClient {
|
|
|
1619
1626
|
postSsoDeviceStart(args: RequestArgs & { body: PostSsoDeviceStartRequest }): Promise<ApiResponse>;
|
|
1620
1627
|
/** POST /sso/feedback — rank: public */
|
|
1621
1628
|
postSsoFeedback(args: RequestArgs & { body: PostSsoFeedbackRequest }): Promise<ApiResponse>;
|
|
1629
|
+
/** POST /sso/feedback/direct — rank: public */
|
|
1630
|
+
postSsoFeedbackDirect(args: RequestArgs & { body: PostSsoFeedbackDirectRequest }): Promise<PostSsoFeedbackDirectResponse>;
|
|
1622
1631
|
/** POST /sso/invites/clear — rank: public */
|
|
1623
1632
|
postSsoInvitesClear(args: RequestArgs & { body: PostSsoInvitesClearRequest }): Promise<ApiResponse>;
|
|
1624
1633
|
/** POST /sso/invites/notify — rank: public */
|
|
@@ -381,8 +381,10 @@ export function createClient(opts = {}) {
|
|
|
381
381
|
getDoneWhenPendingReview: (args) => request("GET", "/done-when/pending-review", { hasBody: false }, args),
|
|
382
382
|
},
|
|
383
383
|
"feedback": {
|
|
384
|
-
// POST /feedback — rank: any-builder — POST /feedback
|
|
385
|
-
|
|
384
|
+
// POST /feedback/receipt — rank: any-builder — POST /feedback/receipt
|
|
385
|
+
postFeedbackReceipt: (args) => request("POST", "/feedback/receipt", { hasBody: true }, args),
|
|
386
|
+
// GET /feedback/receipt/{id} — rank: public — GET /feedback/receipt/:id
|
|
387
|
+
getFeedbackReceiptId: (args) => request("GET", "/feedback/receipt/{id}", { hasBody: false }, args),
|
|
386
388
|
},
|
|
387
389
|
"founding": {
|
|
388
390
|
// POST /founding/alive — rank: any-builder — POST /founding/alive
|
|
@@ -1075,6 +1077,8 @@ export function createClient(opts = {}) {
|
|
|
1075
1077
|
"softwareUpdate": {
|
|
1076
1078
|
// GET /software-update — rank: any-builder — GET /software-update
|
|
1077
1079
|
getSoftwareUpdate: (args) => request("GET", "/software-update", { hasBody: false }, args),
|
|
1080
|
+
// POST /software-update/request — rank: archon — POST /software-update/request
|
|
1081
|
+
postSoftwareUpdateRequest: (args) => request("POST", "/software-update/request", { hasBody: true }, args),
|
|
1078
1082
|
},
|
|
1079
1083
|
"specialities": {
|
|
1080
1084
|
// GET /specialities — rank: any-builder — GET /specialities
|
|
@@ -1121,6 +1125,8 @@ export function createClient(opts = {}) {
|
|
|
1121
1125
|
postSsoDeviceStart: (args) => request("POST", "/sso/device/start", { hasBody: true }, args),
|
|
1122
1126
|
// POST /sso/feedback — rank: public — POST /sso/feedback
|
|
1123
1127
|
postSsoFeedback: (args) => request("POST", "/sso/feedback", { hasBody: true }, args),
|
|
1128
|
+
// POST /sso/feedback/direct — rank: public — POST /sso/feedback/direct
|
|
1129
|
+
postSsoFeedbackDirect: (args) => request("POST", "/sso/feedback/direct", { hasBody: true }, args),
|
|
1124
1130
|
// POST /sso/invites/clear — rank: public — POST /sso/invites/clear
|
|
1125
1131
|
postSsoInvitesClear: (args) => request("POST", "/sso/invites/clear", { hasBody: true }, args),
|
|
1126
1132
|
// POST /sso/invites/notify — rank: public — POST /sso/invites/notify
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# ADR 0368 — Red and blue teams practise on a range, never on live
|
|
2
|
+
|
|
3
|
+
- **Status:** proposed
|
|
4
|
+
- **Date:** 2026-10-06
|
|
5
|
+
- **Goal:** goal 1000094 (Working area 10 — Security, BONGOS-V2)
|
|
6
|
+
- **Builds on:** [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) (the server enforces rank), [ADR 0019](0019-red-team-bounty-payout-table.md) (the bounty table), [ADR 0188](0188-patrol-runs-on-ci-not-the-server.md) (the weekly deterministic patrol), [ADR 0292](0292-both-install-paths-stay-and-vendoring-is-the-production-one.md) (a project is built from a release package), [ADR 0346](0346-dev-boxes-are-retired.md) (no staging since the dev boxes went), [ADR 0361](0361-merges-publish-as-candidates-release-decides-what-others-are-offered.md) (releases publish as candidates before anyone is offered them)
|
|
7
|
+
- **Decided with:** the owner, in a 2026-10-06 scoping interview. Every decision below is theirs; the D-numbers record the answer, not a recommendation.
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Goal 1000094 states its target as *"Red-teaming is consistent"*, and says the server should not be hackable by Claude's latest model. What exists today is all prevention, and some of it is only partly real:
|
|
12
|
+
|
|
13
|
+
- **Report inbox.** `/builder-redteam` files to `vulnerability_reports` and the Archon adjudicates. Its SKILL.md promises an auto-repro subagent that does not exist (task 1003431).
|
|
14
|
+
- **Bounty table.** The table is in `modules/security/security.js` (10/50/200/1000, overridable with `GDS_BOUNTY_*`). **No bounty has ever been paid:** confirming a report sets `drachmae_awarded` on the report, but nothing writes to `credit_log`.
|
|
15
|
+
- **Patrol.** The weekly CI run (`scripts/gds/redteam-patrol.js`) checks secrets in git history, dependency drift and action pins. It cannot attack a running server.
|
|
16
|
+
- **Detection.** `audit_log` records every authenticated write, refused ones included. It does not record reads or anonymous requests, and nothing alerts anyone.
|
|
17
|
+
- **Rules.** The only written rules are one line in the `#red-team` Discord channel topic: responsible disclosure, no live exploitation.
|
|
18
|
+
- **Staging.** None. There is also no fixture of fake builders at each rank.
|
|
19
|
+
- **Candidates are frequent.** Every merge publishes a candidate (161 in the week to 2026-10-06, about 23 a day). Only the one the owner presses Release on reaches other projects.
|
|
20
|
+
|
|
21
|
+
The established professional practice has four parts. Bongos has fragments of the second and none of the rest:
|
|
22
|
+
|
|
23
|
+
1. a practice copy (a *range*) running the exact release about to go live, on fake data;
|
|
24
|
+
2. rules of engagement;
|
|
25
|
+
3. a blue team that detects as well as defends;
|
|
26
|
+
4. a loop that turns every break into a fix plus a permanent check.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
**D1 — Outcome.** The first version delivers all four of these: an advisory release check (D7), a scoreboard, sides with points (D8), and live detection (D9).
|
|
31
|
+
|
|
32
|
+
**D2 — The range is its own small server.**
|
|
33
|
+
- It runs on its own server (~$12/month, about 2 GB), separate from the shared box, so attackers may go after the machine itself.
|
|
34
|
+
- **It tests the candidate about to be released, not every candidate.** On `/deploy` the owner marks a candidate "stage for attack". The range is then rebuilt from scratch from that exact version, using the same vendored package a real project gets (ADR 0292). It rebuilds only when the owner chooses to.
|
|
35
|
+
- There is one range at a time. Staging a new candidate replaces the old one.
|
|
36
|
+
- It holds only fake data: fake builders at every rank, at least two fake tenants, and planted *canary secrets* (fake keys whose appearance anywhere proves a breach).
|
|
37
|
+
- It uses stand-ins for GitHub and Discord, so attacking the ship/deploy pipeline or the bot never touches the real services.
|
|
38
|
+
|
|
39
|
+
**D3 — The range shares nothing with live.**
|
|
40
|
+
- **Logins are already separate.** A session is a random token looked up in each server's own database (`db-kernel.js` `lookupSession`). A range login is therefore unknown to cloudbongos.com, and the reverse holds too.
|
|
41
|
+
- **The real coupling is the identity hub.** An ordinary project signs builders in through cloudbongos.com and reports activity back to it (ADR 0141, `modules/platform-identity`). The range must do neither. It runs its own stand-in hub, and fake builders sign in there.
|
|
42
|
+
- **No real secret is ever placed on the range.** That covers GitHub app keys, the Discord token, npm tokens and DigitalOcean tokens. The range is also never seeded from a copy of the live database.
|
|
43
|
+
- **A test proves the separation.** The range's configuration must name no live hub address and hold no real secret. Only the planted canaries look like secrets.
|
|
44
|
+
|
|
45
|
+
**D4 — Rules of engagement.**
|
|
46
|
+
- **In bounds:** everything on the range: rank escalation, cross-tenant reach, credit minting, the ship/deploy pipeline (against the stand-ins), the bot stand-in, and the range server itself.
|
|
47
|
+
- **Always out of bounds:**
|
|
48
|
+
- cloudbongos.com and the real box;
|
|
49
|
+
- denial of service, meaning flooding the range until it goes down;
|
|
50
|
+
- social engineering of real people.
|
|
51
|
+
|
|
52
|
+
Breaking these rules is a ban, not a bounty. Real GitHub and Discord are out of reach by construction, because the range only knows the stand-ins.
|
|
53
|
+
|
|
54
|
+
**D5 — Who plays.**
|
|
55
|
+
- **Red (attack):** AI sessions, plus human builders at Metic or above.
|
|
56
|
+
- **Blue (defend):** any builder may take a fix task, as today. The blue area of the hall shows confirmed-but-unfixed breaks, so seeing it requires Metic or above, the same bar as red.
|
|
57
|
+
- **One side at a time.** A builder may switch sides once every 30 days. When a red builder switches, their unconfirmed attempts stay theirs: they are still adjudicated and still credited to that builder as finder. That builder may never claim the fix for their own finding.
|
|
58
|
+
|
|
59
|
+
**D6 — AI attacks run on subscriptions, never the metered API.**
|
|
60
|
+
- Attack sessions run in a builder's own Claude Code session or headless through the Claude CLI, including under Autobongos.
|
|
61
|
+
- No Bongos-paid API key is used, so the practice adds no model spend.
|
|
62
|
+
- A finding from a session is credited to the builder who ran it, Autobongos runs included.
|
|
63
|
+
|
|
64
|
+
**D7 — Release check: advisory now, a gate later.**
|
|
65
|
+
- Each candidate's bar is **at least 3 attack runs, on Claude's newest model, each starting as xenos from a fixed attack brief, with no confirmed high or critical break.**
|
|
66
|
+
- While only our own projects run on Bongos, the bar is **advisory**: the deploy page shows a candidate's result beside its Release button, and releasing is unchanged.
|
|
67
|
+
- It becomes a blocking gate when the first outside tenant goes live; that switch is its own decision. Once it blocks, only the Archon may override, and the override records a typed reason in Bongos.
|
|
68
|
+
|
|
69
|
+
**D8 — Visibility and rewards.**
|
|
70
|
+
- Red attempts are private to the attacker and the Archon. Blue sees a break only once it is confirmed and turned into a fix task.
|
|
71
|
+
- Red and blue each get **their own area of the hall**, with a shared scoreboard (runs per candidate, breaks by severity, fixes, detections).
|
|
72
|
+
- **Points only, no credits yet.** ADR 0019's table stays as the severity scale. Paying it out, the missing `credit_log` write, is deferred until the range has proven itself. When it pays, the finder is the session owner (D6).
|
|
73
|
+
|
|
74
|
+
**D9 — Live detection.** On cloudbongos.com, alert on three signals:
|
|
75
|
+
- **repeated refusals:** many `rank_forbidden`/`permission_forbidden` responses from one builder or token in a short window;
|
|
76
|
+
- **odd CLI-token use:** a new origin, or unusual volume. Tokens never expire ([ADR 0294](0294-cli-sessions-do-not-expire.md)), so a leaked token is the largest live risk;
|
|
77
|
+
- **credits without a ship:** a credit appearing with no matching shipped task.
|
|
78
|
+
|
|
79
|
+
Alerts go to a new private `#security-alerts` Discord channel, visible to Archons only and added through `docs/discord/channels.json`. They do not go to the existing channels:
|
|
80
|
+
- `#red-team` is open to every Metic and is where reports are filed;
|
|
81
|
+
- `#security-ship-feed` announces shipped fixes.
|
|
82
|
+
|
|
83
|
+
The first two signals can be built from `audit_log`, which already records refused authenticated writes. Mass-read detection is deferred, because reads are not logged today.
|
|
84
|
+
|
|
85
|
+
**D10 — Blue's starting backlog.**
|
|
86
|
+
- The goal's still-valid open tasks become blue's backlog. On 2026-10-06 it had 48 open tasks (44 ready, 1 blocked, 3 backlog), and an audit that day judged about 40 of them still real.
|
|
87
|
+
- The four most urgent go first: 1004380 (a non-ASCII pre-launch password guess crashes the server), 1003412 (the re-auth skill wrongly says re-issuing a token kills the leaked one), and 1004382 + 1004383 (rank, credit and fitness files missing from `protected-surfaces.json`).
|
|
88
|
+
|
|
89
|
+
## Consequences
|
|
90
|
+
|
|
91
|
+
- **Cost** is ~$12/month for the range server, and there is no model spend.
|
|
92
|
+
- **Release speed is unchanged while the check is advisory.** Once it is a gate, releasing becomes two steps: stage the candidate, then wait for three attack runs to finish against it.
|
|
93
|
+
- **"Not hackable by Claude's latest model"** becomes a measured, per-release claim instead of an aspiration.
|
|
94
|
+
- **Deferred, each its own later decision:**
|
|
95
|
+
- credit payouts;
|
|
96
|
+
- the blocking gate;
|
|
97
|
+
- mass-read detection;
|
|
98
|
+
- outside researchers.
|
|
99
|
+
- **A known gap:** with no API spend, attack runs happen only when a builder or Autobongos picks one up. A candidate can sit with zero runs, and the check will show that plainly rather than pass it.
|
package/docs/adr/README.md
CHANGED
|
@@ -486,3 +486,4 @@ These 20 numbers are each shared by exactly two files. They are **accepted histo
|
|
|
486
486
|
| 0365 | [**A guild engages a project through each member's own door** ([task 1002303](https://cloudbongos.com/builders#/task/1002303)). Amends ADR 0336 D7's invite half after ADR 0353. **D1:** the engagement row as D7 specified, plus `guild_id` on the echo (the latest application decides the label). **D2:** a guild applies = each member applies as themselves through the relay, naming the engagement. **D3:** a project invites a guild = the hub records it and returns one hall hand-off link per public-roster member; no hub-written invites, the one-step relay stays deferred. **D4:** a project is named only when on the map and public, for both kinds. **D5:** the label rides the applicant-profile read; the queue is R28's. **D6:** the D4.4 guild cards are their own task.](0365-a-guild-engages-a-project-through-each-members-own-door.md) | guilds / invites / applications |
|
|
487
487
|
| 0366 | [**Hall planning is forms; a goal made there carries its own spec task, and the interviews stay in the CLI** ([task 1003461](https://cloudbongos.com/builders#/task/1003461), goal 1000089). Spike answer. **D1:** form-shaped, build in the hall: create a task under a goal, create a goal (name + subtitle, version, category, no scope field), attach a criterion to a goal you hold; every server refusal rendered as copy. **D2:** interview-shaped, deliberately absent from the hall: version planning, scope inference, drafting criteria and seed tasks, reuse-vs-create judgement, carry-forward. **D3 (owner):** a hall-made goal is created with a "Spec goal N" task (`POST /goals` `spec_task: true`, `source_ref goal-<id>-spec`) and every task in the goal depends on it until it ships; no new permission, no migration. **D4:** an optional "Plan with Claude" hand-off button.](0366-hall-planning-is-forms-and-a-goal-made-there-carries-its-own-spec-task.md) | lifecycle / planning |
|
|
488
488
|
| 0367 | [**The Discord support bot answers from public docs only, under a monthly cap that fails closed** ([task 1002534](https://cloudbongos.com/builders#/task/1002534), goal 1000110). **D1:** one pinned channel, only when @-mentioned; the first gateway reply. **D2:** the corpus is an allowlist (`README.md`, `docs/onboarding/`, `docs/api-reference.md`) served by a new `memory.searchPublicDocs` port whose scope (doc, xenos, no owner, allowlisted ref) is fixed in SQL; the asker is never looked up. **D3:** no writes but the reply and one `audit_log` row per answer, which is also the spend ledger, so no migration. **D4:** a nonce-fenced prompt, no tools, mentions disabled. **D5:** `claude-haiku-4-5`, `DISCORD_SUPPORT_MONTHLY_CAP_USD` default $5, fail closed, with in-flight answers reserved at worst case. **D6:** off by default; its own switch and an env-only, namespaced key. **D7:** the channel's humans review wrong answers; the fix is the doc.](0367-the-support-bot-answers-from-public-docs-only.md) | discord / support / spend |
|
|
489
|
+
| 0368 | [**Red and blue teams practise on a range, never on live** (goal 1000094 — *Working area 10, Security*; proposed). **D2:** the range is its own small server, rebuilt from the candidate the owner marks "stage for attack". **D3:** it shares no identity hub, secret or data with live. **D5:** red is AI plus Metic+, with one side at a time and a 30-day switch. **D6:** attacks run on subscriptions, never the metered API. **D7:** 3 runs on the newest model with 0 high+ breaks, advisory until an outside tenant exists. **D8:** points only, no credits yet. **D9:** three live signals to `#security-alerts`.](0368-red-and-blue-teams-practice-on-a-range-never-on-live.md) | security / red-team / range |
|