@bongos/core 1.19.1033 → 1.19.1035
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 +204 -99
- package/.claude/skills/builder-start/SKILL.md +1 -1
- package/.claude/skills/grade-recover/SKILL.md +2 -1
- package/.claude/skills/grader-health/SKILL.md +4 -0
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +8 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/adr/0089-modular-multi-model-adversarial-grader.md +15 -0
- package/docs/adr/README.md +1 -1
- package/docs/api/openapi.json +120 -4
- package/docs/api-reference.md +9 -2
- package/docs/architecture.md +3 -2
- package/docs/canonical-permissions.md +2 -2
- package/docs/copy-inventory.md +34 -30
- package/docs/copy-registry.json +69 -33
- package/docs/module-api-changelog.md +5 -1
- package/docs/recipes/local-dev.md +45 -3
- package/docs/recipes/packaging-the-core.md +1 -1
- package/modules/autonomy/cadence.js +52 -0
- package/modules/autonomy/db.js +19 -0
- package/modules/autonomy/routes/autonomy.js +28 -0
- package/modules/government/catalog.js +2 -2
- package/modules/government/protected-surfaces.json +1 -0
- package/modules/grading/grade-engine.js +267 -0
- package/modules/grading/grader-subagent.js +4 -3
- package/modules/grading/grader-workers/index.js +67 -11
- package/modules/grading/grader-workers/worker-verdict.js +32 -2
- package/modules/grading/grader.js +109 -88
- package/modules/hall-ui/public/deploy.js +35 -0
- package/modules/hall-ui/public/project-settings.css +7 -0
- package/modules/lifecycle/routes/tasks.js +19 -4
- package/modules/lifecycle/ship-assurance.js +17 -1
- package/modules/provisioning/control-plane-guard.js +14 -13
- package/modules/provisioning/core-upgrade.js +30 -17
- package/modules/provisioning/public-refusal.js +44 -0
- package/modules/provisioning/routes/disconnect.js +2 -1
- package/modules/provisioning/routes/env-manifest.js +5 -4
- package/modules/provisioning/routes/provisioning.js +16 -15
- package/modules/provisioning/routes/repo-private.js +2 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +80 -0
- package/scripts/gds/autobongos-run.js +10 -2
- package/scripts/gds/backfill-cost.js +20 -9
- package/scripts/gds/claim.js +2 -2
- package/scripts/gds/credential-presence.js +281 -0
- package/scripts/gds/deploy-main-audit-gate.sh +11 -4
- package/scripts/gds/dev-lib.js +212 -5
- package/scripts/gds/dev.js +102 -50
- package/scripts/gds/doctor.js +3 -2
- package/scripts/gds/grade-correlation-audit.js +280 -0
- package/scripts/gds/grade-replay.js +237 -0
- package/scripts/gds/hooks/pre-push.js +7 -3
- package/scripts/gds/main-audit.js +19 -10
- package/scripts/gds/memory-map.js +3 -1
- package/scripts/gds/onboarding-config.js +3 -2
- package/scripts/gds/plain-cards.js +4 -0
- package/scripts/gds/preflight.js +12 -4
- package/scripts/gds/provision-core-upgrade.js +155 -4
- package/scripts/gds/provision-repo-auth.js +15 -0
- package/scripts/gds/provision.js +1 -1
- package/scripts/gds/publish-credential-check.js +3 -0
- package/scripts/gds/push-main.js +14 -3
- package/scripts/gds/push-path-brief.js +3 -2
- package/scripts/gds/run-routine.js +18 -5
- package/scripts/gds/run-unit-tests.js +14 -3
- package/scripts/gds/session-cost.js +12 -3
- package/scripts/gds/ship-deploy-target.js +11 -6
- package/scripts/gds/ship-grade-steps.js +33 -0
- package/scripts/gds/ship-land.js +157 -11
- package/scripts/gds/ship-merge.js +3 -2
- package/scripts/gds/ship-preflight-steps.js +30 -8
- package/scripts/gds/ship.js +1 -1
- package/scripts/gds/upgrade.js +3 -3
- package/scripts/hall-preview/README.md +20 -3
- package/scripts/hall-preview/live-identity.js +1 -1
- package/scripts/hall-preview/refresh-fixtures.js +71 -11
- package/scripts/hall-preview/scrub-identity.js +296 -0
- package/src/bongos/core-update.js +23 -1
- package/src/bongos/routes/core-update.js +27 -2
- package/src/bongos/routes/security.js +3 -3
- package/src/build-info.js +4 -2
- package/src/module-api.js +1 -1
- package/tests/adr_0089_scope.mjs +70 -0
- package/tests/autobongos_cadence.mjs +74 -0
- package/tests/consumer_layout_boot.mjs +363 -31
- package/tests/core_upgrade_door.mjs +40 -6
- package/tests/core_upgrade_runner.mjs +240 -4
- package/tests/credential_presence.mjs +153 -0
- package/tests/deploy_audit_gate.mjs +5 -2
- package/tests/deploy_page_projects.mjs +63 -2
- package/tests/dev_lib.mjs +2 -1
- package/tests/dev_windows.mjs +324 -0
- package/tests/env_knobs_neutral_prefix.mjs +203 -0
- package/tests/fixtures/grade-replay/recorded-panel-grades.json +27 -0
- package/tests/government_protected_surfaces.mjs +10 -0
- package/tests/grade_correlation_audit.mjs +159 -0
- package/tests/grade_engine.mjs +249 -0
- package/tests/grade_engine_replay.mjs +99 -0
- package/tests/grader_prompt_window_bound.mjs +132 -0
- package/tests/grader_unbacked_score.mjs +194 -0
- package/tests/hall_preview_scrub.mjs +214 -0
- package/tests/integration_parked_ratchet.mjs +24 -1
- package/tests/provisioning_public_refusal.mjs +195 -0
- package/tests/publish_status_branch.mjs +128 -0
- package/tests/routine_observability_api.mjs +195 -0
- package/tests/ship_assurance.mjs +86 -3
- package/tests/ship_land_red_pr.mjs +236 -0
- package/tests/ship_preflight.mjs +67 -1
- package/tests/start_env_guard.mjs +16 -0
- package/tests/upgrade.mjs +20 -1
|
@@ -34,7 +34,7 @@ Delivery is now deterministic (task 1288). The **card-delivery hook** (`.claude/
|
|
|
34
34
|
|
|
35
35
|
> **FIRST, read WHERE THIS INSTANCE'S BOARD LIVES — `config/branding.json` → `domains.buildersOrigin`.** Every route below branches on it, and getting it wrong is worse than an error. A **remote `https://` origin** means the board is **HOSTED**: it is a running website with the builder's real tasks in it. A **`localhost`** origin means the instance is genuinely local and has to be brought up. Prescribing the local cure on a hosted instance walks the builder through three obedient steps to a board that is not theirs.
|
|
36
36
|
|
|
37
|
-
- **`bongos: command not found`, a "cannot find module" from `node scripts/gds/start.js`, or you can otherwise tell this is a fresh checkout** (a scaffolded instance whose `node_modules` is absent). The `bongos` CLI ships *inside* the not-yet-installed `@bongos/core` package, so nothing runs until deps are installed — this is the #1 fresh-clone wall. Note before prescribing anything: **`@bongos/core` is a
|
|
37
|
+
- **`bongos: command not found`, a "cannot find module" from `node scripts/gds/start.js`, or you can otherwise tell this is a fresh checkout** (a scaffolded instance whose `node_modules` is absent). The `bongos` CLI ships *inside* the not-yet-installed `@bongos/core` package, so nothing runs until deps are installed — this is the #1 fresh-clone wall. Note before prescribing anything: **`@bongos/core` is a PUBLIC npm package** ([ADR 0283](../../../docs/adr/0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md), since 2026-09-13), so a bare `npm install` needs **no `NPM_TOKEN`** and no npm login. A 404 on `@bongos/core` is therefore **not** a normal outcome: it means the dependency in `package.json` names something the registry does not have (a version that was never published, a mistyped name). A local `file:` tarball that is missing fails differently (ENOENT, not a 404). Read the exact spec, tell the builder what it points at, and do not quietly repoint it at a different version. Route by `buildersOrigin`:
|
|
38
38
|
- **Hosted (remote origin)** — lead with the fact that **nothing needs installing**: the board is a website, so open `<buildersOrigin>/builders` to see and claim work right now. The CLI is an optional second client and needs a token. **Never route to `bongos dev` here** — it stands up a *local* instance with its *own empty database*, so it cannot show the hosted board and quietly answers a different question than the one asked.
|
|
39
39
|
- **Local (`localhost` origin)** — the instance really is local: `npm install`, then `bongos dev` (one-command local bring-up), then re-run `/builder-start`.
|
|
40
40
|
Either way, don't hand-run internal scripts; the package isn't unpacked yet.
|
|
@@ -38,9 +38,10 @@ The panel could not run (worker infra outage or a pre-spawn bypass). This is **n
|
|
|
38
38
|
|
|
39
39
|
### Branch B — fabricated / server-shape fail: the guard tripped or an axis was forged by an error
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Three known sub-shapes, all recognizable because the "fail" carries **no substantive findings**. First make sure it really carries none: read the top-level `issues[]` — the API has lifted the stored findings there since task 1003488 (2026-09-02). Before that, a grade READ BACK showed no `issues` at all even when the panel had filed some, which is how task 1002721's real (and wrong) Quality blocker was taken for an empty fail.
|
|
42
42
|
|
|
43
43
|
- **Server-guard trip** (`signals.server_guard.tripped` non-empty, e.g. `implausible_pass`): the server downgraded a client-asserted pass it found implausible — historically this guard has fired on genuinely clean, unanimous zero-issue passes. Explain the downgrade honestly, then re-grade: `node scripts/gds/ship.js <id> --regrade`.
|
|
44
|
+
- **An unbacked low score** (`signals.unbacked_scores` lists it; the card reads `UNBACKED LOW SCORE`): a reviewer scored below threshold without a major/blocker finding, so the severity gate downgraded it to manual-confirm instead of a hard fail (task 1002903). There is nothing to fix. Re-grade once; if it repeats on a diff you can defend, take the override path below with your evidence.
|
|
44
45
|
- **A FAIL scored around 4.5/10 with zero issues and a null/zero axis** (the errored-worker class): a worker error was forwarded as a real axis score. Same response — re-grade; the panel rerun replaces the forged shape with a real verdict. Since task 1003318 a Quality worker that never ran (a `Prompt is too long` window overflow, `error: PROMPT_TOO_LONG`) no longer forges a 0: the server records `signals.cq_lens_unavailable` with a json-null axis and the card reads `QUALITY LENS UNAVAILABLE` — re-grade or manual-confirm; a 4.5 with a 0 axis now only comes from a server predating it.
|
|
45
46
|
|
|
46
47
|
If an honest re-grade re-trips the guard on a diff you can defend, use the **auditable override path** — file an override-request with a real rationale (≥20 chars; only the task's most-recent claim holder or an Archon may file; an Archon then decides it):
|
|
@@ -54,6 +54,10 @@ The evaluation found every grade ran on a generation-stale pin. Read the live pi
|
|
|
54
54
|
|
|
55
55
|
With `--probe`, run the live panel calibration: `node scripts/gds/grade-smoke.js`. It grades known-good and known-bad fixtures against the real panel and reports miscalibration. **Costs ~$0.30–0.90 per run; Metic+ only.** Never run it implicitly — only when the user passed `--probe` or asked for a live calibration.
|
|
56
56
|
|
|
57
|
+
## Related: do the workers fail differently?
|
|
58
|
+
|
|
59
|
+
This skill checks whether the grader is RUNNING well. Whether its four workers carry independent signal — or are four copies of one opinion — is a different question with its own read-only report: `node scripts/gds/grade-correlation-audit.js` (pairwise agreement + Cohen's kappa between workers, each worker's kappa against the owner's override and manual-confirm decisions, refused below a minimum sample; no model calls, Metic+ for the override read). Point the owner at it when the question is panel composition rather than health.
|
|
60
|
+
|
|
57
61
|
## Report shape
|
|
58
62
|
|
|
59
63
|
End with a compact findings list, most severe first: axis, evidence (the numbers you read), verdict (outage / drift / anomaly / stale-pin / healthy), and the exact remediation command or the skill to hand off to (`/grade-recover` for a single stuck task; a Bongos task via `capture.js` for systemic fixes). A clean bill of health is one line per axis.
|
|
@@ -129,7 +129,7 @@ node scripts/gds/push-main.js # SEC #863: the sanctioned gated main-push (NOT
|
|
|
129
129
|
ssh -o ConnectTimeout=10 lars@REDACTED_IP "~/deploy.sh"
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
> **SEC #863 (ADR 0043):** push `main` only via `node scripts/gds/push-main.js`. A raw `git push origin main` is now blocked by the pre-push permission gate — that block is the friction that stops an ad-hoc bypass like the Builder 25 incident. `push-main.js` carries the `
|
|
132
|
+
> **SEC #863 (ADR 0043):** push `main` only via `node scripts/gds/push-main.js`. A raw `git push origin main` is now blocked by the pre-push permission gate — that block is the friction that stops an ad-hoc bypass like the Builder 25 incident. `push-main.js` carries the `<PREFIX>_ALLOW_MAIN_PUSH` override (`BONGOS_` on a vanilla instance) because /merge-mode only ever lands tasks that already passed the grader + the Archon confirm gate. The post-push main audit (`node scripts/gds/main-audit.js`) re-checks regardless.
|
|
133
133
|
|
|
134
134
|
The deploy script applies any new migrations, restarts the service, and curls /healthz.
|
|
135
135
|
|
|
@@ -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
|
+
- 439 operations across 66 resource groups
|
|
9
9
|
|
|
10
10
|
## Use it from your project
|
|
11
11
|
|
|
@@ -189,6 +189,8 @@ function createClient(opts = {}) {
|
|
|
189
189
|
getAutonomyPrecheck: (args) => request("GET", "/autonomy/precheck", { hasBody: false }, args),
|
|
190
190
|
// GET /autonomy/runners — rank: any-builder — GET /autonomy/runners
|
|
191
191
|
getAutonomyRunners: (args) => request("GET", "/autonomy/runners", { hasBody: false }, args),
|
|
192
|
+
// GET /autonomy/runs — rank: metic+archon — GET /autonomy/runs
|
|
193
|
+
getAutonomyRuns: (args) => request("GET", "/autonomy/runs", { hasBody: false }, args),
|
|
192
194
|
},
|
|
193
195
|
"backup": {
|
|
194
196
|
// GET /backup/status — rank: metic+archon — GET /backup/status
|
|
@@ -350,6 +352,10 @@ function createClient(opts = {}) {
|
|
|
350
352
|
// GET /core-update — rank: archon — GET /core-update
|
|
351
353
|
getCoreUpdate: (args) => request("GET", "/core-update", { hasBody: false }, args),
|
|
352
354
|
},
|
|
355
|
+
"coreUpgrades": {
|
|
356
|
+
// GET /core-upgrades — rank: archon — GET /core-upgrades
|
|
357
|
+
getCoreUpgrades: (args) => request("GET", "/core-upgrades", { hasBody: false }, args),
|
|
358
|
+
},
|
|
353
359
|
"cost": {
|
|
354
360
|
// POST /cost — rank: any-builder — POST /cost
|
|
355
361
|
postCost: (args) => request("POST", "/cost", { hasBody: true }, args),
|
|
@@ -188,6 +188,8 @@ function createClient(opts = {}) {
|
|
|
188
188
|
getAutonomyPrecheck: (args) => request("GET", "/autonomy/precheck", { hasBody: false }, args),
|
|
189
189
|
// GET /autonomy/runners — rank: any-builder — GET /autonomy/runners
|
|
190
190
|
getAutonomyRunners: (args) => request("GET", "/autonomy/runners", { hasBody: false }, args),
|
|
191
|
+
// GET /autonomy/runs — rank: metic+archon — GET /autonomy/runs
|
|
192
|
+
getAutonomyRuns: (args) => request("GET", "/autonomy/runs", { hasBody: false }, args),
|
|
191
193
|
},
|
|
192
194
|
"backup": {
|
|
193
195
|
// GET /backup/status — rank: metic+archon — GET /backup/status
|
|
@@ -349,6 +351,10 @@ function createClient(opts = {}) {
|
|
|
349
351
|
// GET /core-update — rank: archon — GET /core-update
|
|
350
352
|
getCoreUpdate: (args) => request("GET", "/core-update", { hasBody: false }, args),
|
|
351
353
|
},
|
|
354
|
+
"coreUpgrades": {
|
|
355
|
+
// GET /core-upgrades — rank: archon — GET /core-upgrades
|
|
356
|
+
getCoreUpgrades: (args) => request("GET", "/core-upgrades", { hasBody: false }, args),
|
|
357
|
+
},
|
|
352
358
|
"cost": {
|
|
353
359
|
// POST /cost — rank: any-builder — POST /cost
|
|
354
360
|
postCost: (args) => request("POST", "/cost", { hasBody: true }, args),
|
|
@@ -35,6 +35,7 @@ export interface GetAuditLogResponse { rows: unknown; page: unknown }
|
|
|
35
35
|
export interface GetAuthAppPairInfoResponse { pair_code: unknown; device_name: unknown; platform: unknown; status: unknown }
|
|
36
36
|
export interface GetAuthWebAdmissionStatusResponse { admitted: boolean }
|
|
37
37
|
export interface GetAutonomyRunnersResponse { runners: unknown; none: unknown }
|
|
38
|
+
export interface GetAutonomyRunsResponse { routine: unknown; limit: unknown; runs: unknown }
|
|
38
39
|
export interface GetBackupStatusResponse { backup_dir: unknown; db_name: unknown; latest_dump: unknown; stale: unknown; age_hours: unknown; trigger_running: unknown }
|
|
39
40
|
export interface GetBlockersIdResponse { blocker: unknown }
|
|
40
41
|
export interface GetBlockersResponse { blockers: unknown; page: unknown }
|
|
@@ -61,6 +62,7 @@ export interface GetConnectionsRequestsResponse { incoming: unknown; outgoing: u
|
|
|
61
62
|
export interface GetConnectionsResponse { connections: unknown; count: unknown }
|
|
62
63
|
export interface GetConnectionsSuggestionsResponse { suggestions: unknown; count: unknown }
|
|
63
64
|
export interface GetCoreUpdateResponse { ok: boolean; update: unknown }
|
|
65
|
+
export interface GetCoreUpgradesResponse { ok: boolean; running: unknown; limit: unknown; upgrades: unknown }
|
|
64
66
|
export interface GetDiscordChannelsPlanResponse { ok: boolean; guild_id: unknown; op_count: unknown; ops: unknown; warnings: unknown }
|
|
65
67
|
export interface GetDiscordChannelsSnapshotResponse { ok: boolean; guild_id: unknown; config: unknown; warnings: unknown }
|
|
66
68
|
export interface GetDoneWhenPendingReviewResponse { criteria: unknown; count: unknown }
|
|
@@ -601,6 +603,8 @@ export interface BongosClient {
|
|
|
601
603
|
getAutonomyPrecheck(args?: RequestArgs): Promise<ApiResponse>;
|
|
602
604
|
/** GET /autonomy/runners — rank: any-builder */
|
|
603
605
|
getAutonomyRunners(args?: RequestArgs): Promise<GetAutonomyRunnersResponse>;
|
|
606
|
+
/** GET /autonomy/runs — rank: metic+archon */
|
|
607
|
+
getAutonomyRuns(args?: RequestArgs): Promise<GetAutonomyRunsResponse>;
|
|
604
608
|
};
|
|
605
609
|
"backup": {
|
|
606
610
|
/** GET /backup/status — rank: metic+archon */
|
|
@@ -762,6 +766,10 @@ export interface BongosClient {
|
|
|
762
766
|
/** GET /core-update — rank: archon */
|
|
763
767
|
getCoreUpdate(args?: RequestArgs): Promise<GetCoreUpdateResponse>;
|
|
764
768
|
};
|
|
769
|
+
"coreUpgrades": {
|
|
770
|
+
/** GET /core-upgrades — rank: archon */
|
|
771
|
+
getCoreUpgrades(args?: RequestArgs): Promise<GetCoreUpgradesResponse>;
|
|
772
|
+
};
|
|
765
773
|
"cost": {
|
|
766
774
|
/** POST /cost — rank: any-builder */
|
|
767
775
|
postCost(args: RequestArgs & { body: PostCostRequest }): Promise<PostCostResponse>;
|
|
@@ -185,6 +185,8 @@ export function createClient(opts = {}) {
|
|
|
185
185
|
getAutonomyPrecheck: (args) => request("GET", "/autonomy/precheck", { hasBody: false }, args),
|
|
186
186
|
// GET /autonomy/runners — rank: any-builder — GET /autonomy/runners
|
|
187
187
|
getAutonomyRunners: (args) => request("GET", "/autonomy/runners", { hasBody: false }, args),
|
|
188
|
+
// GET /autonomy/runs — rank: metic+archon — GET /autonomy/runs
|
|
189
|
+
getAutonomyRuns: (args) => request("GET", "/autonomy/runs", { hasBody: false }, args),
|
|
188
190
|
},
|
|
189
191
|
"backup": {
|
|
190
192
|
// GET /backup/status — rank: metic+archon — GET /backup/status
|
|
@@ -346,6 +348,10 @@ export function createClient(opts = {}) {
|
|
|
346
348
|
// GET /core-update — rank: archon — GET /core-update
|
|
347
349
|
getCoreUpdate: (args) => request("GET", "/core-update", { hasBody: false }, args),
|
|
348
350
|
},
|
|
351
|
+
"coreUpgrades": {
|
|
352
|
+
// GET /core-upgrades — rank: archon — GET /core-upgrades
|
|
353
|
+
getCoreUpgrades: (args) => request("GET", "/core-upgrades", { hasBody: false }, args),
|
|
354
|
+
},
|
|
349
355
|
"cost": {
|
|
350
356
|
// POST /cost — rank: any-builder — POST /cost
|
|
351
357
|
postCost: (args) => request("POST", "/cost", { hasBody: true }, args),
|
|
@@ -114,6 +114,19 @@ The two deterministic server-side guards — route-rank ([ADR 0016](0016-trust-b
|
|
|
114
114
|
|
|
115
115
|
The worker-contract (`buildPrompt` / `extractJson` / `computeVerdict`), the `runWorker` parallel runner, the `task_grades` + `grade_attempts` schema, the trivial-diff fast-path, and the server-side guard envelope are **kept**. This ADR generalizes their *inputs* (which model, which rubric, which evidence) — it does not throw them away. The content-addressed LLM cache ([ADR 0077](0077-content-addressed-llm-cache.md)) applies unchanged and now memoizes open-model calls too.
|
|
116
116
|
|
|
117
|
+
### 8. Scope: what the engine does NOT own
|
|
118
|
+
|
|
119
|
+
Four other surfaces spend model tokens reading the repository, or stand guard over the build pipeline, so anyone planning the unification can mistake them for graders. They are deliberately **outside** the engine. None of them registers as a `grader.workType.<key>` provider, and none is migrated onto the engine. Each shares at most plumbing, and none shares the grade path, the verdict or the rubric. Verified against `main` on 2026-09-26 (task 1002844):
|
|
120
|
+
|
|
121
|
+
| Surface | What it is | What it shares with the grader | The one contract it preserves |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| **BFG session evaluator**: `scripts/gds/session-eval.js` ([ADR 0027](0027-bfg-session-inefficiency-evaluator.md)) | Scores how a builder *session* worked (ceremony vs work, footguns) from its uploaded digest | Nothing. It imports no grading code. | It judges the **session**, never the ship. It cannot change a grade or move a task's status. |
|
|
124
|
+
| **gate-review**: `scripts/gds/gate-review.js` | The gatekeeper ([ADR 0058](0058-automatic-gate-surface-gatekeeper.md)) that escalates a change **to the gate itself** before it lands | No grader call. It lists `modules/grading/grader.js`, `grader-workers/` and `grader-rubric.json` among its hard-floor paths, and reads the protected set from `src/bongos/permission-path-check.js`. | A change to the grader is reviewed by something **outside** the grader. It must never depend on the code it reviews. |
|
|
125
|
+
| **main-audit**: `scripts/gds/main-audit.js` ([ADR 0043](0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md)) | The post-push audit of `main` (layer 3): real git history against real builder ranks | The rank/permission classifier `src/bongos/permission-path-check.js`. This is the same kernel checker the grader's permission-path pre-pass reaches through the module doorway. It is not grading code. | It runs **after the fact, whether or not a grade ran**, so it catches what bypassed the grader (a raw push, `--no-verify`). It must never assume the grade happened. |
|
|
126
|
+
| **Architect audit**: `scripts/gds/architect-audit.js` | First-principles "what to delete" audits that feed ideas into scope | Three subprocess/parse helpers from `modules/grading/grader.js`: `runSubagentCached`, `extractAssistantResult` and `extractJson`. This is plumbing: spawn a model, read its JSON. | It produces **ideas for a human to triage**, never a verdict on a ship. If the engine ever moves those helpers, the Architect needs the same three functions, not a work-type. |
|
|
127
|
+
|
|
128
|
+
**How to apply it.** An LLM call that decides whether a ship lands belongs in the engine. Everything else stays out, and a new exception is claimed by adding a row here. The one coupling to watch when the engine grows is the Architect's three plumbing helpers: keep them exported, or move the Architect's import in the same change.
|
|
129
|
+
|
|
117
130
|
---
|
|
118
131
|
|
|
119
132
|
## Consequences
|
|
@@ -172,6 +185,8 @@ A build cluster under GDS-V4 (or its successor), staged so nothing destabilizes
|
|
|
172
185
|
|
|
173
186
|
Roll out behind a flag (the [ADR 0079](0079-value-gated-autonomous-engagement.md) "build disabled, arm later" pattern); the deterministic security gate is never flagged off.
|
|
174
187
|
|
|
188
|
+
**Progress (2026-09-25).** Step 3's engine and registry landed first, ahead of steps 1–2, because both build on it: `modules/grading/grade-engine.js` names the pipeline (`select → pre_pass → evidence → fan_out → aggregate → gate → persist`), owns both deterministic pre-passes and a deterministic veto no work-type's aggregate can override, and resolves work-types from its own registry or a module's `grader.workType.<key>` seam provider. The code panel is registered as work-type `code`, the default. Proven a no-op: a before/after run of 27 grading scenarios came out byte-identical, and a replay of 376 recorded production grades reproduced every stored decision except 22 from June 2026, which an older grader produced before two deliberate gate changes (the task 943 "grader unavailable" outcome and the task 574 missing-security-lens block). Task 1002839; the replay tool is `scripts/gds/grade-replay.js`.
|
|
189
|
+
|
|
175
190
|
---
|
|
176
191
|
|
|
177
192
|
## Sources
|
package/docs/adr/README.md
CHANGED
|
@@ -160,7 +160,7 @@ This keeps the decision history honest and traceable.
|
|
|
160
160
|
| 0087-a | [Bongos app architecture + the model-agnostic feedback handoff contract (BONGOS-V1 / C4). Records the umbrella desktop app that absorbs the Dev Box app as a module, the capture→synthesize→handoff pipeline (R04–R14), and the Rung-3 *visual* capture floor (pixels + voice + freeze-frame markup over any app) with element-level reads deferred to V2 (Accessibility requested lazily, never up front). The contract: a bundle is a self-contained `~/CloudBongos/feedback/<id>/` dir where every screenshot is referenced by ABSOLUTE FILE PATH, never base64 — so the text prompt stays ~5–11k tokens and any agent opens the frames with its own file read. One shared `latest` pointer (atomic `latest.json` + best-effort symlink, self-healing) feeds three readers that can't drift: Claude Desktop (clipboard + open folder, R12), Claude Code (`/feedback` slash command cats prompt.md + Reads the frame paths, R13), and an optional zero-dependency stdio MCP server (`get_latest_feedback` returns text + file paths, never base64, R14). Hand-rolled MCP (newline-delimited JSON-RPC, no SDK dep) keeps the install surface flat. Bongos succeeds the GDS as the primary builder surface after GDS-V4; a fourth agent is an adapter, not a redesign. $0 new recurring cost.](0087-bongos-app-architecture-and-handoff-contract.md) | Bongos app / feedback loop |
|
|
161
161
|
| 0087-b | [Compete with Cursor on governance, not tooling](0087-compete-on-governance-not-tooling-cursor.md) | strategy / positioning |
|
|
162
162
|
| 0088 | [Consistent-character generative animation — a generate→grade→audit→regenerate loop built by EXTENDING the existing family pipeline (ADR 0008: `run_family` + cross-frame coherence + the `character_identity` Gemini review + `t5_anchor_refine`), not a new grader/loop (the owner's "don't reinvent the loop"). Decision (with the owner, 2026-06-26): option A — Gemini + grade/audit with a PLUGGABLE generator backend (the ADR 0062/0081 adapter pattern) — over a per-character LoRA, which is deferred as "level-2" (needs GPU training + an open model). The family pipeline is already a consistent-character multi-frame loop (walk/peck cycles ship through it) but tuned for 32×32 pixel-art; this adds a painterly `character_anim` family kind (drops palette-snap/32×32), the two genuinely-new criteria `pose_match` + order-aware transition-coherence (existing checks are order-agnostic — they'd pass a shuffled animation), a recursive Gemini inbetween layer for high frame counts (≈12–24 "on twos"/"on ones"), a painterly cutout/register post-process, and packaging as a Cloud Bongos module (ADR 0083). First consumer = the Bongo Buddha's 8 actions → widget (task 1523). Implementation epic: tasks 1549–1559.](0088-consistent-character-generative-animation.md) | GDS / art pipeline |
|
|
163
|
-
| 0089 | [Modular, multi-model adversarial grader (open-weight ensemble) — refactor the ship-gate into a central, work-type-parameterized engine (gather evidence → deterministic pre-passes → judge ensemble → normalize+aggregate → gate → persist) where each MODULE contributes only its rubric + evidence-gatherer + specialist critics through the ADR 0083 seam (`grader.workType.<key>`), answering the owner's "per-module or general?" as central-engine + module-plugged. Adds open-weight LLM judges from a DIFFERENT model family than the author (Claude stays as the quality floor, never the sole judge) to kill same-family self-preference inflation (10–25%); judges run via a `ModelProvider` adapter (`openai-compatible` → DeepInfra/OpenRouter/vLLM/Ollama, the ADR 0062 neutral-contract pattern) on a hosted pay-per-call API (~$1–5/mo, owner spend-gated). Small (2–3) audited panel — z-score/rank-normalize then median + hard-veto aggregation; an error-correlation audit routine (Cohen's κ vs owner decisions) keeps the jury failing DIFFERENTLY (the "Nine Judges, Two Effective Votes" finding) rather than assuming vendor-diversity. Deterministic route-rank + permission-path pre-passes stay ZERO-LLM and authoritative (no LLM verdict, Claude or open, can upgrade a fail over a protected path; ADR 0016/0043/0080); VLM judges advisory on art, deterministic palette/grid checks authoritative. Owner decisions 2026-06-26: central-engine + module-plugged; ensemble vote; hosted API. Reuses the worker-contract + task_grades schema + LLM cache (ADR 0077); rolls out advisory-first behind a flag. Build cluster
|
|
163
|
+
| 0089 | [Modular, multi-model adversarial grader (open-weight ensemble) — refactor the ship-gate into a central, work-type-parameterized engine (gather evidence → deterministic pre-passes → judge ensemble → normalize+aggregate → gate → persist) where each MODULE contributes only its rubric + evidence-gatherer + specialist critics through the ADR 0083 seam (`grader.workType.<key>`), answering the owner's "per-module or general?" as central-engine + module-plugged. Adds open-weight LLM judges from a DIFFERENT model family than the author (Claude stays as the quality floor, never the sole judge) to kill same-family self-preference inflation (10–25%); judges run via a `ModelProvider` adapter (`openai-compatible` → DeepInfra/OpenRouter/vLLM/Ollama, the ADR 0062 neutral-contract pattern) on a hosted pay-per-call API (~$1–5/mo, owner spend-gated). Small (2–3) audited panel — z-score/rank-normalize then median + hard-veto aggregation; an error-correlation audit routine (Cohen's κ vs owner decisions) keeps the jury failing DIFFERENTLY (the "Nine Judges, Two Effective Votes" finding) rather than assuming vendor-diversity. Deterministic route-rank + permission-path pre-passes stay ZERO-LLM and authoritative (no LLM verdict, Claude or open, can upgrade a fail over a protected path; ADR 0016/0043/0080); VLM judges advisory on art, deterministic palette/grid checks authoritative. Owner decisions 2026-06-26: central-engine + module-plugged; ensemble vote; hosted API. Reuses the worker-contract + task_grades schema + LLM cache (ADR 0077); rolls out advisory-first behind a flag. Build cluster in progress: the central engine + work-type registry landed first (task 1002839, `modules/grading/grade-engine.js`, the code panel registered as work-type `code`). Deliberately OUTSIDE the engine (§8): the BFG session evaluator, gate-review, main-audit and the Architect audit — none grades a ship; the Architect shares only three subprocess/parse helpers, and main-audit and gate-review only the rank-permission checker.](0089-modular-multi-model-adversarial-grader.md) | GDS / grader + quality gate |
|
|
164
164
|
| 0090 | [Metic gains task authoring — re-ranks `POST /tasks`, `PATCH /tasks/:id`, `POST /tasks/:id/promote`, and the `tasks/:id/{dependencies,criteria}` CRUD from Archon-only to `requireRank('metic','archon')` (owner decision 2026-06-26: a rank trusted to triage ideas is trusted to seed + shape the tasks they become). Moves the two `EXPECTED_RANKS` pins in lockstep and updates the `// rank:` annotations so the route-rank audit stays green. Keeps Archon-only: rank-setting, version-close, abandon, grader-bypass confirm, and the ADR 0086 cross-tier `POST /dependencies` + goal-scope edges. Amends ADR 0018's table row 7; sits beside ADR 0086, which remains the module-fenced authoring path for sub-Metic ranks. "Tighten later" debt explicitly accepted — no per-version/module/goal fence on a Metic's authoring.](0090-metic-task-authoring.md) | GDS / permissions |
|
|
165
165
|
| 0091 | [Bounding the kernel and carving `db.js` per-domain (Config-B core decomposition) — amends ADR 0083 to extend the strangler from the four FEATURE modules into the CORE itself (BONGOS-V1 C7). Answers the three questions the audit opened: (1) WHAT IS THE KERNEL — replaces the 6-file machinery-only `fitness.js` `KERNEL_FILES` with an enumerated ~3.5k-LOC trust roster (auth · route-rank-check · permission-path-check · secret-box · pool · the new `db-kernel.js` identity/audit/rank/`withTx` slice · instance-config/branding/build-info · routes/_helpers · the module-system machinery + module-scope-map); everything else in `src/bongos/` is a domain destined to become a module or to stay inside the un-split lifecycle state machine. (2) HOW A DOMAIN OWNS ITS SQL — ratifies the proven `modules/discord/db.js` + `modules/game/db.js` pattern (own queries lifted verbatim + deleted from core, reached through the doorway `pool`/`withTx`; kernel helpers re-exported behind the same `db.*` namespace; boundary-forced twins allowed + documented) as THE per-domain carve mechanism; `db.js` shrinks toward a kernel slice + the lifecycle residue. (3) WHAT THE DOORWAY EXPOSES — classifies `module-api.js` re-exports into kernel (stays: identity/audit/rank/auth/seams/config + `withTx`), domain-leak-with-a-home (relocate now: `setBuilderBoxBlocked`→dev-box, gemini-key trio→art-pipeline), and domain-leak-pending-carve (`createTask`/`captureIdea`/`VOTABLE_TASK_STATUSES` stay as MARKED temporary re-exports until lifecycle/ideas are carved in tranche 2 — named, owned, tracked debt, not pretend-kernel). A lifecycle-invoked capability resolves through a kernel PORT, never a direct import (R73 replaces `routes/tasks.js`→`grader.js` with `resolve('grade')`). Deliverable = this decision + the seeded, dependency-wired R68–R74 sub-task shape; OTB byte-identical at every step. Tranche 2 (the other ~8 core domains) deferred per owner.](0091-bounding-the-kernel-and-db-carve.md) | GDS / architecture |
|
|
166
166
|
| 0092 | [Hosting module + on-prem (warehouse) hosting target — scopes a portable Cloud Bongos `hosting` module (default-OFF, fail-to-vanilla) that brokers compute + storage workloads (inference / imagegen / embeddings / training / storage) to configured hosting *targets* through kernel seams, resolving each via `resolveOptional(port, cloudDefaultFn)` so a box-less / module-off instance degrades to cloud automatically. The warehouse GPU box is OTB's first ON-PREM target — one adapter backend declared in HOST-instance config (the ADR 0062 / 0081 core↔host + swappable-adapter pattern), shared across all Example instances, reached over an outbound tunnel the box initiates (no open ports / no static IP), sitting OUTSIDE the trust boundary like Discord (ADR 0033): it computes, it never grants rank or lands code (ADR 0016 / 0043). Inference REUSES the ADR 0089 `ModelProvider` openai-compatible adapter (the on-prem box = one more vLLM / Ollama endpoint) and adopts 0089's decisive finding that low-volume work is cheaper on pay-per-call APIs — so the GPU's honest justification narrows to GPU-bound batch generation, open-model fine-tuning / LoRA (the ADR 0088 deferred level-2), and always-busy bulk, plus the non-GPU wins of backups / storage + an unmetered sandbox; code that ships stays on Claude. Owner decisions 2026-06-27: serve all instances, conservative posture, proceed. Phased: scope use ✅ → build (task 1619) → hardware (task 1620) → software; the cost read-out (task 1621) is an independent input.](0092-hosting-module-and-on-prem-target.md) | Cloud Bongos / hosting |
|
package/docs/api/openapi.json
CHANGED
|
@@ -105,6 +105,10 @@
|
|
|
105
105
|
"name": "core-update",
|
|
106
106
|
"description": "Endpoints under `/api/bongos/core-update`."
|
|
107
107
|
},
|
|
108
|
+
{
|
|
109
|
+
"name": "core-upgrades",
|
|
110
|
+
"description": "Endpoints under `/api/bongos/core-upgrades`."
|
|
111
|
+
},
|
|
108
112
|
{
|
|
109
113
|
"name": "cost",
|
|
110
114
|
"description": "Endpoints under `/api/bongos/cost`."
|
|
@@ -2128,6 +2132,47 @@
|
|
|
2128
2132
|
]
|
|
2129
2133
|
}
|
|
2130
2134
|
},
|
|
2135
|
+
"/autonomy/runs": {
|
|
2136
|
+
"get": {
|
|
2137
|
+
"operationId": "get_autonomy_runs",
|
|
2138
|
+
"tags": [
|
|
2139
|
+
"autonomy"
|
|
2140
|
+
],
|
|
2141
|
+
"summary": "GET /autonomy/runs",
|
|
2142
|
+
"description": "The gate's past decisions — did a routine fire, and what did it decide, and why (task 1002669). READ-ONLY and behind the SAME atom as the precheck above (`autonomy.precheck`, Metic+): whoever may ask the gate the question may read its past answers, and a new atom would need a migration for no change in audience. Adds no gate anywhere — pure observability over rows the precheck already writes. ?routine=<name> narrows to one routine; ?limit=1..100 (default 20), newest first.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `autonomy.precheck` (all required).",
|
|
2143
|
+
"x-rank": "metic+archon",
|
|
2144
|
+
"x-source": "modules/autonomy/routes/autonomy.js",
|
|
2145
|
+
"x-permissions": [
|
|
2146
|
+
"autonomy.precheck"
|
|
2147
|
+
],
|
|
2148
|
+
"responses": {
|
|
2149
|
+
"200": {
|
|
2150
|
+
"description": "Success.",
|
|
2151
|
+
"content": {
|
|
2152
|
+
"application/json": {
|
|
2153
|
+
"schema": {
|
|
2154
|
+
"$ref": "#/components/schemas/GetAutonomyRunsResponse"
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
},
|
|
2159
|
+
"400": {
|
|
2160
|
+
"$ref": "#/components/responses/BadRequest"
|
|
2161
|
+
},
|
|
2162
|
+
"401": {
|
|
2163
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
2164
|
+
},
|
|
2165
|
+
"403": {
|
|
2166
|
+
"$ref": "#/components/responses/Forbidden"
|
|
2167
|
+
}
|
|
2168
|
+
},
|
|
2169
|
+
"security": [
|
|
2170
|
+
{
|
|
2171
|
+
"builderSession": []
|
|
2172
|
+
}
|
|
2173
|
+
]
|
|
2174
|
+
}
|
|
2175
|
+
},
|
|
2131
2176
|
"/backup/status": {
|
|
2132
2177
|
"get": {
|
|
2133
2178
|
"operationId": "get_backup_status",
|
|
@@ -5126,6 +5171,47 @@
|
|
|
5126
5171
|
]
|
|
5127
5172
|
}
|
|
5128
5173
|
},
|
|
5174
|
+
"/core-upgrades": {
|
|
5175
|
+
"get": {
|
|
5176
|
+
"operationId": "get_core_upgrades",
|
|
5177
|
+
"tags": [
|
|
5178
|
+
"core-upgrades"
|
|
5179
|
+
],
|
|
5180
|
+
"summary": "GET /core-upgrades",
|
|
5181
|
+
"description": "GET /core-upgrades — the instance's own core-upgrade ledger, newest first: when the pin last moved, from and to which version, and whether a person or the unattended sweep moved it (task 1002669). READ-ONLY. Gated on the SAME atom as /core-update above and the deploy door, for the same first reason: it names the exact core this box has run and when, which is a vulnerability-window disclosure (ADR 0043). No new atom, so no migration. ?limit=1..100 (default 20).\n\n**Rank:** `archon` — Archon only (rank and identity management + the escalation keys — the trust boundary).\n\n**Permissions:** `core.pin.move` (all required).",
|
|
5182
|
+
"x-rank": "archon",
|
|
5183
|
+
"x-source": "src/bongos/routes/core-update.js",
|
|
5184
|
+
"x-permissions": [
|
|
5185
|
+
"core.pin.move"
|
|
5186
|
+
],
|
|
5187
|
+
"responses": {
|
|
5188
|
+
"200": {
|
|
5189
|
+
"description": "Success.",
|
|
5190
|
+
"content": {
|
|
5191
|
+
"application/json": {
|
|
5192
|
+
"schema": {
|
|
5193
|
+
"$ref": "#/components/schemas/GetCoreUpgradesResponse"
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
},
|
|
5198
|
+
"400": {
|
|
5199
|
+
"$ref": "#/components/responses/BadRequest"
|
|
5200
|
+
},
|
|
5201
|
+
"401": {
|
|
5202
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
5203
|
+
},
|
|
5204
|
+
"403": {
|
|
5205
|
+
"$ref": "#/components/responses/Forbidden"
|
|
5206
|
+
}
|
|
5207
|
+
},
|
|
5208
|
+
"security": [
|
|
5209
|
+
{
|
|
5210
|
+
"builderSession": []
|
|
5211
|
+
}
|
|
5212
|
+
]
|
|
5213
|
+
}
|
|
5214
|
+
},
|
|
5129
5215
|
"/cost": {
|
|
5130
5216
|
"post": {
|
|
5131
5217
|
"operationId": "post_cost",
|
|
@@ -19391,7 +19477,7 @@
|
|
|
19391
19477
|
"tasks"
|
|
19392
19478
|
],
|
|
19393
19479
|
"summary": "GET /tasks/:id/publish-status",
|
|
19394
|
-
"description": "GET /tasks/:id/publish-status?branch=<branch> — poll the PR + prod-deploy state for a server-mediated publish so ship.js can confirm the land WITHOUT gh on the box. Ownership-gated like the POST. Derives state live from GitHub.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
19480
|
+
"description": "GET /tasks/:id/publish-status?branch=<branch> — poll the PR + prod-deploy state for a server-mediated publish so ship.js can confirm the land WITHOUT gh on the box. Ownership-gated like the POST. Derives state live from GitHub. `branch` is optional (task 1003764): without it the server finds the task's own PR (both publish paths title it \"task <id>: …\") and the answer names the `branch` it used and its `branch_source`, so the check agents are sent to instead of git probes never refuses a live publish for want of a branch name.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
|
|
19395
19481
|
"x-rank": "any-builder",
|
|
19396
19482
|
"x-source": "modules/lifecycle/routes/tasks.js",
|
|
19397
19483
|
"parameters": [
|
|
@@ -20636,6 +20722,19 @@
|
|
|
20636
20722
|
"none"
|
|
20637
20723
|
]
|
|
20638
20724
|
},
|
|
20725
|
+
"GetAutonomyRunsResponse": {
|
|
20726
|
+
"type": "object",
|
|
20727
|
+
"properties": {
|
|
20728
|
+
"routine": {},
|
|
20729
|
+
"limit": {},
|
|
20730
|
+
"runs": {}
|
|
20731
|
+
},
|
|
20732
|
+
"required": [
|
|
20733
|
+
"routine",
|
|
20734
|
+
"limit",
|
|
20735
|
+
"runs"
|
|
20736
|
+
]
|
|
20737
|
+
},
|
|
20639
20738
|
"GetBackupStatusResponse": {
|
|
20640
20739
|
"type": "object",
|
|
20641
20740
|
"properties": {
|
|
@@ -20999,6 +21098,23 @@
|
|
|
20999
21098
|
"update"
|
|
21000
21099
|
]
|
|
21001
21100
|
},
|
|
21101
|
+
"GetCoreUpgradesResponse": {
|
|
21102
|
+
"type": "object",
|
|
21103
|
+
"properties": {
|
|
21104
|
+
"ok": {
|
|
21105
|
+
"type": "boolean"
|
|
21106
|
+
},
|
|
21107
|
+
"running": {},
|
|
21108
|
+
"limit": {},
|
|
21109
|
+
"upgrades": {}
|
|
21110
|
+
},
|
|
21111
|
+
"required": [
|
|
21112
|
+
"ok",
|
|
21113
|
+
"running",
|
|
21114
|
+
"limit",
|
|
21115
|
+
"upgrades"
|
|
21116
|
+
]
|
|
21117
|
+
},
|
|
21002
21118
|
"GetDiscordChannelsPlanResponse": {
|
|
21003
21119
|
"type": "object",
|
|
21004
21120
|
"properties": {
|
|
@@ -27782,9 +27898,9 @@
|
|
|
27782
27898
|
"description": "A required dependency/feature is not configured or is temporarily down."
|
|
27783
27899
|
}
|
|
27784
27900
|
},
|
|
27785
|
-
"x-endpoint-count":
|
|
27786
|
-
"x-schema-count":
|
|
27901
|
+
"x-endpoint-count": 439,
|
|
27902
|
+
"x-schema-count": 481,
|
|
27787
27903
|
"x-undocumented-bodies": 9,
|
|
27788
|
-
"x-response-schemas":
|
|
27904
|
+
"x-response-schemas": 329,
|
|
27789
27905
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
|
27790
27906
|
}
|
package/docs/api-reference.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Bongos API reference
|
|
4
4
|
|
|
5
|
-
> **Generated from the live route files** — the route file is authoritative.
|
|
5
|
+
> **Generated from the live route files** — the route file is authoritative. 439 endpoints across 74 route files.
|
|
6
6
|
> Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
|
|
7
7
|
|
|
8
8
|
Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust-boundary-server-enforced-permissions.md)): `public` < `any-builder` < `metic+archon` < `archon`.
|
|
@@ -84,7 +84,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
|
|
|
84
84
|
| GET | `/api/bongos/auth/web/callback` | `public` | — | OAuth callback; the state cookie + code are the bearer. |
|
|
85
85
|
| GET | `/api/bongos/auth/web/start` | `public` | — | /auth/web — browser flow (used by the /builders page). |
|
|
86
86
|
|
|
87
|
-
## `autonomy` (
|
|
87
|
+
## `autonomy` (8)
|
|
88
88
|
|
|
89
89
|
| Method | Path | Rank | Body | Description |
|
|
90
90
|
|---|---|---|---|---|
|
|
@@ -95,6 +95,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
|
|
|
95
95
|
| POST | `/api/bongos/autonomy/heartbeat` | `any-builder` | `host`, `pid`, `started_at`, `mode`, `consecutive_failures`, `last_event`, `working_task_id` | The runner's check-in, once per loop. |
|
|
96
96
|
| GET | `/api/bongos/autonomy/precheck` | `metic+archon` | — | |
|
|
97
97
|
| GET | `/api/bongos/autonomy/runners` | `any-builder` | — | What the hall renders: the CALLER'S OWN runners, with the pure verdict attached so the page and any future alert cannot disagree about wh… |
|
|
98
|
+
| GET | `/api/bongos/autonomy/runs` | `metic+archon` | — | The gate's past decisions — did a routine fire, and what did it decide, and why (task 1002669). |
|
|
98
99
|
|
|
99
100
|
## `backup` (2)
|
|
100
101
|
|
|
@@ -224,6 +225,12 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/0016-trust
|
|
|
224
225
|
|---|---|---|---|---|
|
|
225
226
|
| GET | `/api/bongos/core-update` | `archon` | — | |
|
|
226
227
|
|
|
228
|
+
## `core-upgrades` (1)
|
|
229
|
+
|
|
230
|
+
| Method | Path | Rank | Body | Description |
|
|
231
|
+
|---|---|---|---|---|
|
|
232
|
+
| GET | `/api/bongos/core-upgrades` | `archon` | — | GET /core-upgrades — the instance's own core-upgrade ledger, newest first: when the pin last moved, from and to which version, and whethe… |
|
|
233
|
+
|
|
227
234
|
## `cost` (1)
|
|
228
235
|
|
|
229
236
|
| Method | Path | Rank | Body | Description |
|
package/docs/architecture.md
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
- **Cloud Bongos control-plane droplet (NEW 2026-07-05, [ADR 0126](adr/0126-dedicated-cloudbongos-control-plane-droplet.md), task [#2063](https://example.com/builders#/task/2063)):** DigitalOcean, hostname `cloudbongos`, IPv4 `REDACTED_IP`, NYC3, `s-1vcpu-2gb` (2 GB / 1 vCPU), Ubuntu 24.04, ~$12/mo, tag `cloudbongos-control`. Runs **cloudbongos.com + `builders.` + `status.`** (`src/platform-server.js`, Cloud Bongos brand, port 3002, own `cloudbongos` DB, checkout `/home/lars/cloudbongos-instance`, the root subscribed to the nightly core-patch sweep) and is the **sole token-holding control plane** — a SECOND root, `/home/lars/cloudbongos`, survives only to vendor `infra/` (the box cloud-init the core package does not ship, ADR 0150); it is not subscribed, so nothing may resolve core code from it (task 1002738, [ADR 0144](adr/0144-devbox-rehome-onto-cloudbongos-plane.md) follow-up 5) — `/etc/cloudbongos/box.env` holds the account-wide Cloudflare token (IP-locked to this box) + the DigitalOcean token; this is where `provision.js` provisions instance subdomains. **Structurally separate SSH** (own `cloudbongos_ed25519` key). IPv6 egress disabled so the IPv4-locked CF token can't be spuriously rejected. cloudbongos.com was previously a co-tenant on the co-hosting box; that unit was decommissioned at cutover. **Follow-ups:** deploy automation (task [#2064](https://example.com/builders#/task/2064)) + fully stripping cloud tokens off the co-hosting box (task [#2065](https://example.com/builders#/task/2065)).
|
|
15
15
|
- **Server stack:** Node.js 22 LTS · PostgreSQL 16 · Caddy 2.11.2. `example.service` (systemd) runs `node server.js` as user `lars` on `127.0.0.1:3000`; restart-on-failure.
|
|
16
16
|
- **Hardening:** `lars` user with passwordless sudo; SSH key-only (root login disabled, password auth disabled); ufw allows 22/80/443; fail2ban active; unattended-upgrades enabled; timezone `America/New_York`.
|
|
17
|
+
- **Credentials:** every credential the instance needs — what it is for, which env file holds it (`web.env` · `box.env` · `spaces.env`), GitHub Actions secrets and builder-machine logins — is listed in [`docs/credentials.md`](credentials.md), names only. Whether each is SET on a host is one command run there: `node scripts/gds/credential-presence.js` (task 1002880). Check both before asking the owner for a credential.
|
|
17
18
|
- **TLS:** Origin cert at `/etc/caddy/origin.crt` + `/etc/caddy/origin.key` for the example apexes on the co-hosting box. **cloudbongos.com's apexes moved to the dedicated control-plane droplet** ([ADR 0126](adr/0126-dedicated-cloudbongos-control-plane-droplet.md)) with their own copy of the Cloudflare Origin Cert (SAN `*.cloudbongos.com, cloudbongos.com`) at `/etc/caddy/cloudbongos-origin.{crt,key}` on `REDACTED_IP`. Cloudflare SSL/TLS mode = Full (strict). Renewal is a non-issue for ~15 years. **Provisioned instance hosts use a different, coexisting posture** ([ADR 0111](adr/0111-instance-hosting-provisioning-module.md) §4 / task P4): Caddy **on-demand ACME** (Let's Encrypt) via the global `on_demand_tls { ask … }` block, gated to hostnames the `provisioning` module actually stood up (`/api/bongos/provisioning/tls-check`). Their DNS records are **proxied=false (grey cloud)** so the ACME challenge reaches Caddy — the opposite of the origin-cert apexes; do not enable the proxy on them or renewal breaks silently ([ADR 0003](adr/0003-cloudflare-origin-cert-over-letsencrypt.md)). Per-instance site snippets live in `/etc/caddy/sites/*.caddy` (`import`ed by `infra/Caddyfile`); the provisioning runner verifies the box's resolved Caddy config carries that import and the `on_demand_tls` ask before it reports a domain wired, and refuses with the line to add rather than editing the base file ([ADR 0303](adr/0303-provisioning-verifies-the-base-caddyfile-and-never-edits-it.md)). **Tradeoff (accepted, SEC review of task 1945):** `proxied=false` publishes the box's real origin IP in public DNS for every co-tenant provisioned hostname — defeating, for those names, the Cloudflare-proxy IP-hiding the apexes enjoy (a co-tenant host resolves to the same `REDACTED_IP` that serves the main game). This is inherent to on-demand ACME (the challenge must hit the origin) and acceptable under the budget/trust model; prefer a **dedicated droplet** (its own IP) for any instance expected to draw hostile attention.
|
|
18
19
|
- **Platform observability — REMOVED 2026-09-15 ([ADR 0284](adr/0284-the-control-plane-carries-no-metrics-stack.md), task 1003876):** the control plane runs **no Grafana and no Prometheus**, and `metrics.cloudbongos.com` has no Caddy site. They cost ~300 MB RSS + 2.5 GB of data on the box every provisioned project shares, and scraped none of those projects. Every instance still exports `/metrics` ([`src/bongos/metrics.js`](../src/bongos/metrics.js), loopback or `METRICS_TOKEN`), so a scraper can return from a box that is *not* the control plane. The rest of this bullet is the **historical** ADR 0127 layout, kept so its leftovers (co-hosting-box forwarders, instance `infra/` copies) can be found: this watched the **Bongos API** (`gds_http`/`tasks`/`claims`/`builders`/`auth`), not any one game, so it lived **on the control-plane box** (REDACTED_IP) at **`https://metrics.cloudbongos.com`** and observed the **whole fleet**. **Prometheus** (`127.0.0.1:9090`, loopback-only) scrapes every instance's `/metrics` ([`src/bongos/metrics.js`](../src/bongos/metrics.js)), each tagged with a `project` label (`infra/prometheus/prometheus.yml` (instance-side)): **cloudbongos** locally (`:3002`); **example + emersonian-circles** on the co-hosting box, reached over the **private VPC** via `socat` forwarders on `10.108.0.2:9101/9102` (→ their loopback `:3000`/`:3004`), ufw-locked to the control-plane box's private IP — raw TCP relays, so the exporter still sees a loopback request and needs no token; nothing is public. **Grafana** (`127.0.0.1:3003`, loopback-only) renders the 4 provisioned-as-code dashboards (folder **Cloud Bongos**: system-overview, error-watch, builder-activity, auth-security), each with a **`project`** template variable for per-instance / fleet views. Reverse-proxied on the `*.cloudbongos.com` origin cert — Archon-only, gated by Caddy `basic_auth` (user `archon`) **and** Grafana's own admin login (both secrets owner-held, never in the repo). Grafana config is a systemd `GF_*` env drop-in (`grafana-server.service.d/override.conf`, `chmod 600`). ~256 MB RAM. A future dedicated-droplet instance is added as a `METRICS_TOKEN` Bearer target instead of a VPC forwarder.
|
|
19
20
|
|
|
@@ -65,7 +66,7 @@ The product is organized as a small kernel (`src/`) + **19 modules** under `modu
|
|
|
65
66
|
**Code & deploy:**
|
|
66
67
|
|
|
67
68
|
- **Code repo:** [github.com/example-owner/example](https://github.com/example-owner/example) — **private**. Clone URL: `git@github.com:example-owner/example.git`. Default branch: `main`. GitHub user: `example-owner`.
|
|
68
|
-
- **Deploy:** two modes, selected by `config/deploy.json` `mode` (env `OTB_DEPLOY_MODE`
|
|
69
|
+
- **Deploy:** two modes, selected by `config/deploy.json` `mode` (env `<PREFIX>_DEPLOY_MODE` overrides — `BONGOS_DEPLOY_MODE` on a vanilla instance; the legacy `OTB_DEPLOY_MODE` is still read), currently **`ci`** (flipped from `laptop` 2026-06-13, [#859](https://example.com/builders#/task/859)) — see [ADR 0042](adr/0042-builder-self-deploy-ci-auto-merge.md). **`laptop`** (legacy): `git push origin main` from the Mac → `ssh lars@REDACTED_IP ~/deploy.sh`. The droplet has a read-only deploy key at `~/.ssh/github_deploy` registered on the repo. `~/deploy.sh` does: `git fetch + reset --hard origin/main` → `npm ci --omit=dev` if `package-lock.json` changed → `./scripts/migrate.sh` → `sudo systemctl restart example` → `curl /healthz` smoke (**retried up to 15× @ 1s** — `systemctl` reports "active" before the Node process binds the port, so a single curl raced the bind and false-failed healthy deploys: [ADR 0056](adr/0056-prod-deploy-script-mirror-and-healthcheck-retry.md) / [#1026](https://example.com/builders#/task/1026)). Droplet checkout: `/home/lars/example/`. `~/deploy.sh` is hand-maintained on the droplet but now has a byte-faithful, reviewable repo mirror at `scripts/deploy/deploy-prod.sh` (instance-side) — **not** auto-installed (the ci deploy key is forced-command-locked, ADR 0042/0043; reinstall by hand on change: `scp scripts/deploy/deploy-prod.sh lars@…:deploy.sh`). **`ci`** (current, armed via [#679](https://example.com/builders#/task/679)): no builder box holds the droplet key — `ship.js` opens a PR + enables GitHub auto-merge, and `.github/workflows/deploy-prod.yml` (instance-side) runs the same `~/deploy.sh` from Actions on merge to `main`. Cutover preconditions (incl. a server-recorded **CI grade-gate**) are in ADR 0042.
|
|
69
70
|
- **Post-merge branch cleanup (standard practice):** Every feature branch that lands on main via `--no-ff` merge gets deleted on origin once the deploy succeeds. The merge commit itself preserves the branch's history in main, so the named ref isn't carrying any signal — keeping it just clutters GitHub's branch list and tricks the UI into offering empty PRs against zero-diff branches. The auto-merge in [`scripts/gds/ship.js`](../scripts/gds/ship.js) does this automatically after `~/deploy.sh` returns 0; the manual [`/merge-mode`](../.claude/skills/merge-mode/SKILL.md) flow follows the same step 5b. Local refs and worktree directories stay — only the origin branch and the local tracking ref (cleaned on next `git fetch --prune`) go.
|
|
70
71
|
- **Local dev machine:** Lars's MacBook Air (Apple Silicon, macOS Sequoia). Tooling installed via Homebrew at `/opt/homebrew/`. Node v26.0.0 locally; `package.json` declares `"engines": { "node": ">=22" }`. Single ed25519 SSH key (`~/.ssh/id_ed25519`) authenticates to GitHub and droplet.
|
|
71
72
|
- **Smoke tests:** lightweight scripts in `/tmp/colyseus-*-smoketest.js` verify joinOrCreate, move protocol, interact, identity, queue. Run any after deploy with `node /tmp/colyseus-<name>-smoketest.js`.
|
|
@@ -229,7 +230,7 @@ Bongos views: `claimable_tasks` (rebuilt in 006 to use SELECT t.*; rebuilt again
|
|
|
229
230
|
- Own-scoped session self-service ([ADR 0206](adr/0206-own-scoped-session-revocation-no-absolute-ceiling.md), task 1003341), in [`src/bongos/routes/my-sessions.js`](../src/bongos/routes/my-sessions.js): `GET /me/sessions` (every live session on the caller's own account — source, user agent, created/last-used/expires, a `current` flag; the token value is never selected), `POST /me/sessions/revoke` (`{session_id}` — one of your own; a miss answers `revoked: 0` so a stranger's id cannot be probed) and `POST /me/sessions/revoke-all` (sign out everywhere, the requesting session included). All three gate on `requireBuilder` alone and key every statement on `req.builder.id` — an account is never named in the query, body or path. They exist because a bearer refreshes to `now()+24h` on every call, so a leaked one never expires, and the cross-builder pair (`POST /auth/revoke`, `/auth/revoke-all`, both still gated on the admin `auth.session.revoke` atom) needed an Archon the victim might not have. Surfaced in the hall at Settings → Active sessions.
|
|
230
231
|
- Authenticated CLI/web: `GET /me` (returns `rank`), `GET /versions[/progress]`, `GET /tasks[?version=&status=&kind=&discipline=]`, `GET /tasks/claimable[?version=&discipline=]`, `GET /tasks/:id`, `POST /tasks` (accepts `discipline` + `criterion_ids`; **metic+** — [ADR 0090](adr/0090-metic-task-authoring.md)), `PATCH /tasks/:id` (supports `parent_task_id`, `kind`, `discipline`, `goal_id` — (re)assign the task's goal; must be a goal in the task's own version, task 1763; **metic+**), `POST /tasks/:id/promote` (**metic+**), `POST /claims`, `POST /claims/:id/resolve`, `POST /cost`.
|
|
231
232
|
- Criterion rollup + task↔criterion links ([ADR 0025](adr/0025-structured-criterion-task-link.md), [#435](https://example.com/builders#/task/435)/[#438](https://example.com/builders#/task/438)): `GET /versions/:id/progress` rolls up each done-when criterion → its gating tasks (via `task_criteria`) → live status counts + the not-yet-shipped `remaining[]` + `unattributed_tasks` (the read behind the `/status` skill, in `src/bongos/done-when.js criterionProgress`). The link CRUD mirrors the dependency endpoints: `GET /tasks/:id/criteria`, `POST /tasks/:id/criteria` (**metic+** — [ADR 0090](adr/0090-metic-task-authoring.md)) and `DELETE /tasks/:id/criteria/:criterionId` (**metic+**). A criterion ref is a numeric `done_when_criteria.id`, a positional `"Cn"` token, or a `criterion_id` slug, resolved against the task's version (`POST /tasks` links at create time so `/status` counts the task with no backfill).
|
|
232
|
-
- Server-mediated branch publish (ADR 0055 / [#1025](https://example.com/builders#/task/1025) — lets a dev box with no GitHub push credential ship). Owner-gated like ship: `POST /tasks/:id/publish-branch` (`requireBuilder` + `gateTaskOwnership`, **NOT** `allowBoxScope` → a box-scoped session is `403 box_scope`, so the builder must `/builder-reauth` first; own 32 MB json parser for the base64 thin-bundle body, 20 MB decoded cap; the server pushes the branch + opens the PR + auto-merges with a server-side push credential — a **GitHub App** installation token (short-lived, repo-scoped; preferred, [ADR 0055](adr/0055-server-mediated-branch-publish.md) update / task 1028) or the `GITHUB_PUSH_TOKEN` PAT fallback; `503 push_unconfigured` when neither is set; `400 tip_mismatch` if the bundle tip ≠ the claimed `head_sha`) and `GET /tasks/:id/publish-status?branch=…` (polls PR + deploy-prod state derived live from GitHub). When the PR is still open it also carries `merge_driver`, whose `reason` says why it has not merged; on a red PR `failing_tests` names the failing unit tests (read from the `unit-report` commit status the `unit` workflow posts) and `checks_unreadable: true` says the check list is blind because the App lacks `Checks: read` — so an empty `checks[]` is never read as "nothing failed" (task 1003988, ADR 0301). Because the server's credential is the PR's AUTHOR, publish also **assigns the PR to the task's claim holder** and names them in the body (`modules/lifecycle/pr-assign.js`, task 1003991) — best-effort: the 201 carries `pr_assignee` (the login, or `null` when GitHub would not assign it) and a failed assignment never fails the publish. `ship.js` uses these in `ci` mode only when `pushVia()` resolves to server (a box, or
|
|
233
|
+
- Server-mediated branch publish (ADR 0055 / [#1025](https://example.com/builders#/task/1025) — lets a dev box with no GitHub push credential ship). Owner-gated like ship: `POST /tasks/:id/publish-branch` (`requireBuilder` + `gateTaskOwnership`, **NOT** `allowBoxScope` → a box-scoped session is `403 box_scope`, so the builder must `/builder-reauth` first; own 32 MB json parser for the base64 thin-bundle body, 20 MB decoded cap; the server pushes the branch + opens the PR + auto-merges with a server-side push credential — a **GitHub App** installation token (short-lived, repo-scoped; preferred, [ADR 0055](adr/0055-server-mediated-branch-publish.md) update / task 1028) or the `GITHUB_PUSH_TOKEN` PAT fallback; `503 push_unconfigured` when neither is set; `400 tip_mismatch` if the bundle tip ≠ the claimed `head_sha`) and `GET /tasks/:id/publish-status?branch=…` (polls PR + deploy-prod state derived live from GitHub). `branch` is optional: without it the server finds the task's own PR by its `task <id>: …` title, and the answer names the `branch` it used and its `branch_source` (`query` / `task_pr`, or `null` with a `branch_hint` when there is no such PR) — never a `400` (task 1003764). When the PR is still open it also carries `merge_driver`, whose `reason` says why it has not merged; on a red PR `failing_tests` names the failing unit tests (read from the `unit-report` commit status the `unit` workflow posts) and `checks_unreadable: true` says the check list is blind because the App lacks `Checks: read` — so an empty `checks[]` is never read as "nothing failed" (task 1003988, ADR 0301). Because the server's credential is the PR's AUTHOR, publish also **assigns the PR to the task's claim holder** and names them in the body (`modules/lifecycle/pr-assign.js`, task 1003991) — best-effort: the 201 carries `pr_assignee` (the login, or `null` when GitHub would not assign it) and a failed assignment never fails the publish. `ship.js` uses these in `ci` mode only when `pushVia()` resolves to server (a box, or `<PREFIX>_PUSH_VIA_SERVER=1`); a laptop with `gh` keeps the local push path unchanged. Implementation in `src/bongos/github-push.js`.
|
|
233
234
|
- Archon monitoring reads backing the `/watch` page (ADR 0036): `GET /builders/roster`, `GET /grades/by-builder[?days=N]` (**archon-only**, [#726](https://example.com/builders#/task/726) — per-builder grade breakdown for the MARKS section; the project-wide aggregate stays public at `/public/grades`), `GET /audit-log`, `GET /override-requests`, `GET /access-requests`, `GET /security/reports`. Personal-prefs writes backing `/settings`: `GET/PATCH /me/skill-prefs`, `PATCH /me/disciplines`.
|
|
234
235
|
- Per-builder "needs" + own Gemini key ([ADR 0073](adr/0073-builder-needs-signal-and-byok-gemini-key.md), [#1013](https://example.com/builders#/task/1013)): `GET /me` now also carries `needs` ({items, action_needed_count} — the consistent "the system needs an input from you" signal; `modules/builder-settings/builder-needs.js` is the SSOT — carved out in BV1.R80, resolved by `GET /me` via the `builder-settings` kernel port — rendered by the hall Standing card + the CLI `printNeedsNudge`). The own-key (pragmatic BYOK) endpoints, all own-scoped: `GET /me/art-key/own` (masked meta — last4 only), `PUT /me/art-key/own` (validate-on-save via a Google list-models call → encrypt with `src/bongos/secret-box.js` → store; `503 storage_not_configured` until `BUILDER_SECRET_KEY` is set, `422 invalid_key` on a bad key), `DELETE /me/art-key/own`. The existing `GET /me/art-key` (shared-key box-sync delivery) is extended to also deliver the decrypted own key, which `scripts/gds/fetch-art-key.js` syncs into the local `gemini_api_key` slot (own key wins in the `gen_api.py` cascade).
|
|
235
236
|
- Builder memory (ADR 0024 cloneable memory + ADR 0026 BFG). Self-scoped (owner is always `req.builder.id`, never input): `POST /memory/sync` (push own memory; own 64 MB parser, vs the global 64 KB), `GET /memory/files`, `GET /memory/file`, `DELETE /memory/file` (the BFG delete affordance, 6C.2). Cross-builder (input-owner) endpoints — all privileged + audit-logged fail-closed: `GET /memory/builders/:id/files`+`/file` (**archon forensic** read), `GET /memory/bfg/builders/:id/files`+`/file` (**BFG-principal only**, 6B.1 read), and the one cross-builder WRITE `POST /memory/builders/:id/bfg-write` (**BFG-principal only**, kill-switched `BFG_WRITE_ENABLED` default-OFF, namespaced `bfg/`, attributed `author: BFG`, write+audit in one tx — dreams + transparent corrections, 6C.1/6C.2).
|
|
@@ -315,8 +315,8 @@ pre-merge gate misses, independent of the builder's cooperation:
|
|
|
315
315
|
activated it; ADR 0043 §"G-2/G-17 resolution") blocks it. This hook is a CORE-repo control:
|
|
316
316
|
scaffolded standalone instances are hookless by default — deliberate, not a gap
|
|
317
317
|
([ADR 0165](adr/0165-instances-hookless-by-default.md)); the graded
|
|
318
|
-
pipeline pushes only via `scripts/gds/push-main.js` (which carries the `
|
|
319
|
-
override). A raw push is caught and reverted by the **post-push main audit**
|
|
318
|
+
pipeline pushes only via `scripts/gds/push-main.js` (which carries the `<PREFIX>_ALLOW_MAIN_PUSH`
|
|
319
|
+
override — `BONGOS_ALLOW_MAIN_PUSH` on a vanilla instance). A raw push is caught and reverted by the **post-push main audit**
|
|
320
320
|
(`scripts/gds/main-audit.js`) — nightly and as the `~/deploy.sh` deploy gate — which is the
|
|
321
321
|
authoritative layer (it reads the real git history + real ranks, independent of the builder).
|
|
322
322
|
- **The pre-push hook fails OPEN by contract, and the wrapper is what enforces that** (task 1002725).
|